Продуктивність статичних методів проти методів екземпляра


108

Моє запитання стосується характеристик продуктивності статичних методів проти методів екземплярів та їх масштабованості. Припустимо, для цього сценарію, що всі визначення класів є в одній збірці і потрібні кілька дискретних типів покажчиків.

Поміркуйте:

public sealed class InstanceClass
{
      public int DoOperation1(string input)
      {
          // Some operation.
      }

      public int DoOperation2(string input)
      {
          // Some operation.
      }

      // … more instance methods.
}

public static class StaticClass
{
      public static int DoOperation1(string input)
      {
          // Some operation.
      }

      public static int DoOperation2(string input)
      {
          // Some operation.
      }

      // … more static methods.
}

Наведені вище класи представляють модель стилю помічника.

У класі екземпляра розв'язання методу екземпляра потребує певного часу, як протиставлення StaticClass.

Мої запитання:

  1. Якщо збереження стану не викликає занепокоєнь (не потрібні поля чи властивості), чи завжди краще використовувати статичний клас?

  2. Якщо існує значна кількість цих статичних визначень класів (скажімо, 100, з кількома статичними методами), це вплине негативно на продуктивність виконання або споживання пам'яті порівняно з тією ж кількістю визначень класів екземплярів?

  3. Якщо викликається інший метод у тому ж класі екземпляра, чи все-таки виникає дозвіл екземпляра? Наприклад, використовуючи ключове слово [this], як this.DoOperation2("abc")з DoOperation1одного і того ж екземпляра.



що ви маєте на увазі під "роздільною здатністю екземпляра"? На рівні IL "цей" покажчик доступний так само, як і будь-яка інша локальна змінна. Насправді, у деяких старих версіях CLR / JIT ви можете викликати метод екземпляра на NULL за умови, що він не торкався "цього" - код просто пролітав і провалювався ні на чому .. тепер CLR / JIT містить явні null- перевірити кожен виклик учасника ..
quetzalcoatl

> vijaymukhi.com/documents/books/ilbook/chap8.htm та "екземпляр виклику" проти просто "дзвінка". Перший очікує параметра "цей", а другий - не.
quetzalcoatl

@Quetzalcoatl вибачте за плутанину, питання було скоріше методом методу від того ж екземпляра, і якщо це вимагає вирішення примірника для себе.
Берні Вайт

1
@quetzalcoatl Я припускав, що він мав на увазі, "чи компілятор позбавляється перевірки того, що thisвказує на щось, коли клас викликає метод екземпляра на собі?"
Джон Ханна

Відповіді:


152

Теоретично статичний метод повинен працювати трохи краще, ніж метод екземпляра, при цьому всі інші речі рівні, через додатковий прихований thisпараметр.

На практиці це має незначну різницю, що вона буде прихована в шумі різних рішень компілятора. (Отже, двоє людей могли «довести» одного кращого за іншого, не погоджуючись з результатами). Не в останню чергу, оскільки thisнорма зазвичай передається в реєстр і часто є в цьому реєстрі для початку.

Останній пункт означає, що теоретично слід очікувати, що статичний метод, який приймає об'єкт як параметр і робить щось з ним, буде дещо менш хорошим, ніж еквівалент як екземпляр цього самого об'єкта. Знову ж таки, різниця настільки незначна, що якби ви спробували її виміряти, ви, ймовірно, в кінцевому підсумку виміряєте інше рішення компілятора. (Тим більше, що ймовірність того, що ця посилання весь час перебуває в реєстрі, теж досить висока).

Реальні відмінності в роботі знизяться до того, чи штучно ви отримали в пам'яті об'єкти, щоб зробити щось, що природно має стати статичним, або ви заплутуєте ланцюги передачі об'єктів складними способами робити те, що природно повинно бути екземпляром.

Отже, для числа 1. Якщо збереження стану не викликає занепокоєнь, завжди краще бути статичним, тому що саме для цього є статична . Це не стосується ефективності, хоча існує загальне правило, як грати добре з оптимізаціями компілятора - скоріше, що хтось піде на намагання оптимізувати випадки, які приходять у звичайне використання, ніж ті, які приходять із дивним використанням.

Число 2. Не має різниці. Існує певна сума вартості на кожен клас для кожного члена, що стосується як метаданих, скільки коду у фактичному файлі DLL чи EXE, так і скільки тривожного коду. Це те саме, будь то екземпляр чи статичний.

З пунктом 3, thisяк thisі у випадку. Однак зверніть увагу:

  1. thisПараметр передається в конкретному регістрі. При виклику методу екземпляра в межах одного класу він, ймовірно, вже буде в цьому реєстрі (якщо він не був прихованим і якийсь регістр використовується з якихось причин), і, отже, не потрібно вживати жодних дій, щоб встановити те, thisна що його потрібно встановити . Це певною мірою стосується, наприклад, перших двох параметрів методу, який є першими двома параметрами дзвінка, який він робить.

  2. Оскільки буде зрозуміло, що thisце не нуль, це може використовуватися для оптимізації викликів у деяких випадках.

  3. Оскільки буде зрозуміло, що thisце не нуль, це може зробити знову вбудовані виклики методу більш ефективними, оскільки код, створений для підробленого виклику методу, може пропустити деякі нульові перевірки, які можуть знадобитися в будь-якому випадку.

  4. Однак, нульові чеки дешеві!

Варто зазначити, що загальні статичні методи, що діють на об’єкт, а не методи екземпляра, можуть зменшити деякі витрати, обговорені на веб-сторінці http://joeduffyblog.com/2011/10/23/on-generics-and-some-of- пов'язані накладні витрати / у випадку, коли дана статика не викликається для заданого типу. За його словами, «Убік, виявляється, що методи розширення є прекрасним способом зробити загальні абстракції більш оплатними.

Однак зауважте, що це стосується лише інстанції інших типів, використовуваних методом, які інакше не існують. Як такий, він дійсно не застосовується до багатьох випадків (якийсь інший метод екземпляра використовував цей тип, якийсь інший код десь інший використовував цей тип).

Підсумок:

  1. Здебільшого витрати на примірник порівняно зі статикою нижче незначні.
  2. Які витрати там, як правило, трапляються там, де ви зловживаєте статикою, наприклад, або навпаки. Якщо ви не зробите це частиною свого рішення між статикою та екземпляром, ви, швидше за все, отримаєте правильний результат.
  3. Є рідкісні випадки, коли статичні загальні методи в іншому типі призводять до створення меншої кількості типів, ніж примірних загальних методів, які можуть змусити іноді мати невелику користь для рідкісного використання (і "рідко" посилається на те, які типи використовуються в термін служби програми, а не те, як часто це називається). Як тільки ви отримаєте те, про що він говорить у цій статті, ви побачите, що це на 100% не має значення для більшості рішень щодо статичного проти екземпляра. Редагувати: І в основному це лише ціна з ngen, а не з jitted кодом.

Редагувати: примітка про те, наскільки дешеві нульові перевірки (на що я заявляла вище). Більшість недійсних перевірок у .NET взагалі не перевіряють на null, а вони продовжують робити те, що збиралися зробити, припускаючи, що воно буде працювати, і якщо трапляється виняток з доступу, він перетворюється на NullReferenceException. Таким чином, здебільшого, коли концептуально код C # включає нульову перевірку, оскільки він отримує доступ до члена екземпляра, вартість, якщо він вдається, насправді дорівнює нулю. Виняток становлять деякі вбудовані дзвінки (тому що вони хочуть вести себе так, як ніби вони викликали члена екземпляра), і вони просто потрапляють у поле, щоб викликати таку саму поведінку, тому вони також дуже дешеві, і вони все ще часто можуть залишатися поза будь-яким чином (наприклад, якщо перший крок методу включав доступ до поля таким, яким він був).


Чи можете ви прокоментувати, чи має статичне запитання щодо екземпляра відповідність на узгодженість кешу? Чи частіше опора на те чи інше може спричинити пропуски кешу? Чи є хороший контур, що пояснює, чому?
скриптокаліпсис

@scriptocalypse Не дуже. Кеш інструкцій не побачить різниці, і на цьому рівні не існує великої різниці між доступом до даних через thisабо через явний параметр. Тут більший вплив матиме наближення даних до пов’язаних даних (поля типу типу або значення масиву ближчі, ніж дані у полях еталонного типу) та структури доступу.
Джон Ханна

"теоретично, нам слід очікувати, що статичний метод, який приймає об'єкт як параметр і робить щось з ним, буде дещо менш хорошим, ніж еквівалент як екземпляр цього самого об'єкта". - Ви маєте на увазі, що якщо вище зразковий метод приймає параметр як об'єкт замість рядка, нестатичний краще? наприклад: у мене статичний метод приймає об'єкт як параметр і серіалізує його в рядок і повертає рядок. Ви пропонуєте використовувати нестатичні в цьому випадку?
batmaci

1
@batmaci Я маю на увазі, що хороший шанс obj.DoSomehting(2)буде дещо дешевшим, DoSomething(obj, 2)але, як я також сказав, різниця настільки незначна і настільки залежна від крихітних речей, що в кінцевій компіляції можуть бути різними, що насправді не варто турбуватися про це взагалі. Якщо ви робите щось настільки дороге (щодо різновидів відмінностей тут), як серіалізація чогось струнного, то це особливо безглуздо.
Джон Ханна

У цій інакші чудовій відповіді відсутня одна, можливо очевидна, але важлива річ: метод екземпляра вимагає екземпляра, а створення екземпляра - не з дешевих. Навіть за замовчуванням ctorвсе-таки потрібна ініціалізація всіх полів. Після того, як у вас вже є екземпляр, ця відповідь застосовується ("всі інші речі рівні"). Звичайно, дорогі cctorможуть стати і статичні методи повільними, але це лише на перший виклик стосується методів екземплярів однаково. Дивіться також docs.microsoft.com/en-us/previous-versions/dotnet/articles/…
Abel

8

Якщо збереження стану не викликає занепокоєнь (не потрібні поля чи властивості), чи завжди краще використовувати статичний клас?

Я б сказав, так. Заявляючи про щось, staticви заявляєте про намір страти без громадянства (це не є обов'язковим, але наміром чогось, що можна було б очікувати)

Якщо існує велика кількість цих статичних класів (скажімо, 100 із кількістю статичних методів у кожному), це вплине негативно на продуктивність виконання або споживання пам’яті порівняно з однаковою кількістю класів екземплярів?

Не думайте так, якщо ви не впевнені, що статичні класи справді нестабільні, викликайте, якщо не так, просто зіпсувати розподіл пам’яті та отримати витоки пам’яті.

Якщо ключове слово [це] використовується для виклику іншого методу в межах того ж класу екземплярів, чи все-таки виникає роздільна здатність примірника?

Не впевнений, щодо цього питання (це суто детальна інформація про впровадження CLR), але подумайте так.


Статистичні методи не можна глузувати. Якщо ви зробите TDD або навіть просто тестування, це дуже зашкодить вашим тестам.
бродяга

@trampster Чому? Це просто логіка. Ви можете легко знущатися над тим, що даєте? Щоб отримати правильну поведінку. І багато статичних методів так чи інакше будуть приватними фрагментами логіки у функції.
М. Мімпен

@ M.Mimpen, поки ви залишите його на невеликі приватні шматки штрафу, якщо його публічний метод, і ви використовуєте його з інших закривається, і вам потрібно змінити те, що він робить у вашому тесті, то ваш застряглий, такі речі, як IO файлів або доступ до бази даних або мережеві дзвінки тощо, якщо їх укласти в статичний метод, стане неможливим, якщо ви не скажете, що ви вводите залежну залежність як параметр статичному методу
трампстер

-2

статичні методи швидші, але менше OOP, якщо ви будете використовувати шаблони дизайну статичний метод, ймовірно, поганий код, краще писати бізнес-логіку без статичних, загальних функцій, таких як читання файлів, WebRequest тощо, краще усвідомлювати як статичні ... у вас питань немає універсального відповідь


16
Ви не давали жодних аргументів до своїх претензій.
ymajoros

2
@ fjch1997 2 видавці, здається, думають інакше (для чого це варто). Коментувати низовину рекомендується практика на stackexchange: meta.stackexchange.com/questions/135/…
ymajoros
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.