Каков реальный пример взрыва вредного кода, вызванного инициализацией поля?

В CLR через С#, Рихтер отмечает, что инициализация полей в объявлении класса, например,

class C {
  int x = 3;
  int y = 4;

  public C() { ... }
  public C(int z) { ... }

  ...
}

приводит к вставке операторов в начале каждого конструктора, которые задают поля заданным значениям. Таким образом, строка int x = 3; выше будет отвечать за две отдельные инициализации: одну в конструкторе без параметров и одну в конструкторе, которая принимает аргумент int.

Рихтер продолжает:

Это означает, что вы должны знать о взрыве кода [...] Если у вас несколько инициализированных полей экземпляра и много перегруженных методов конструктора, вы должны рассмотреть возможность определения полей без инициализации, создав один конструктор, который выполняет общая инициализация, и каждый конструктор явно вызывает общий конструктор инициализации. Этот подход уменьшит размер сгенерированного кода.

У меня возникли проблемы с представлением сценария, в котором это станет заметной проблемой, и это заставляет меня задаться вопросом, не хватает ли я чего-то здесь. Например, если мы предположим, что наш класс имеет десять конструкторов и сто полей, и для выполнения инициализации требуется, скажем, шестнадцать байтов исходного машинного кода, тогда мы говорим о суммарном 16 Кбайт генерируемого кода. Несомненно, что ничтожный объем памяти на любом компьютере в этом веке, верно?

Я предполагаю, что использование дженериков может умножить это на небольшой коэффициент, но все же влияние на рабочий набор кажется довольно небольшим.

Вопрос: Я что-то пропустил здесь, и если да, то что?

В то время как мой вопрос в основном теоретический - я хочу проверить свое собственное понимание - это также немного практично, поскольку инициализация полей, где они объявлены, кажется, создает значительно более читаемый код, чем использование централизованного конструктора, такого как Рихтер,.

Ответ 1

Каков реальный пример взрыва вредного кода, вызванного инициализацией поля?

Я не знаю какого-либо реального примера этой проблемы.

У меня возникли проблемы с представлением сценария, в котором это станет заметной проблемой.

Я тоже.

что заставляет меня задаться вопросом, не хватает ли я чего-то здесь.

Насколько мне известно.

Несомненно, что ничтожный объем памяти на любом компьютере в этом веке, верно?

Right. А также не забывайте, что вы платите за игру в мире джит. Вы получаете только столько котов, которые вы вызываете.

Я предполагаю, что использование дженериков может умножить это на небольшой коэффициент

Это верно для общих типов, которые создаются со значениями типов.

но все же влияние на рабочий набор кажется довольно небольшим.

Опять же, рабочий набор - это плата за игру. Код, который вызывается только несколько раз, выгружается в конце концов, и многие конструкторы вызываются только несколько раз в программе.

Мне что-то не хватает, и если да, то что?

Насколько мне известно.

инициализация полей, где они были объявлены, по-видимому, создает значительно более читаемый код, чем предполагает использование централизованного конструктора, например, Рихтера.

Сначала я бы оптимизировал для правильности и элегантности, и только обойти эту проблему, если эмпирический тест производительности ясно показал, что это реальная проблема, влияющая на пользователя.

Ответ 2

Во-первых, я хотел бы отметить, что часть этого ответа взята из колледжа еще в 1999-1990 годах (что касается процедурного кода, а не ООП), и бит, который я получил со временем, не обязательно все, что нужно сделать с С#, поэтому я не уверен в точных числах по отношению к С#, поэтому любое число, начинающееся с ~, может быть неточным (в случае 16b для конструкторов я бы предположил, что он на самом деле 12b + 1b за параметр, хотя это может быть не так для С#), однако для демонстрационных целей они не должны быть важными.

Обычно при создании объекта вы будете знать, какая информация нужна, и иметь возможность создать пустой шаблон или дать некоторые значения, чтобы создать то, что вы хотите, чтобы создать его путем создания цепочек конструкторов, но что, если при создании экземпляров вы не знайте все значения или не можете связать свои конструкторы? В приведенном ниже примере показан класс, который создаст экземпляр объекта заданного дизайна, на основе которого вы его дадите.

class ObjOfSetDesign
{
 int A, B, C, D, E, F;
 int X = 90;
 int Y = 45;
 int Z = 75;

public ObjOfSetDesign(int a)
{
 A = a;
 D = a / 5;
 B = (A + D) * 2;
 C = B * 6;
 E = A * 4;
 F = C / 2;
}

public ObjOfSetDesign(int b, int c)
{
 B = b;
 C = c;
 D = b / 12;
 A = D * 5;
 E = A * 4;
 F = c / 2;
}

public ObjOfSetDesign (int d, int e, int f)
{
 D = d;
 E = e;
 F = f;
 A = d * 5;
 C = f * 2;
 B = c / 6;
}

Очевидно, что это не лучший пример, вы можете вычислить A и использовать этот конструктор каждый раз, но если ваш класс имеет 100 полей или некоторые из переменных используют XYZ для вычисления их стоимости, тогда у вас может не быть такой роскоши. Тем не менее, ни один из конструкторов не имеет ничего общего, кроме того, что значения могут использоваться для вычисления других, в случае второго конструктора вы не можете даже использовать b и c для вызова: this (a) либо.

Смешайте это с тем фактом, что не все эти поля могут быть просто ints, у вас могут быть списки или передача экземпляра пользовательского класса в конструктор для создания вашего объекта, и я надеюсь, что это поможет вам понять, как класс может имеют 100 полей с 10 независимыми конструкторами, и каждый из них будет иметь XYZ, введенный в начале конструктора.

Что касается того, как это может быть значительным, вы приходите к 16kb в вашем примере, это будет охватывать код для создания шаблона, но AFAIK также должен будет сам хранить данные. Если половина ваших полей инициализируется как ints, что дополнительно 100b, хотя, если бы у вас был список, вы бы создали ~ 22b для создания списка, 1b для каждого индекса плюс данные, содержащиеся внутри. Если ваш список содержит списки, то то же самое применимо и к каждому списку, который содержит меня, который приводит меня к рассмотрению следующего класса.

class BiggerObj
{
 ObjOfSetDesign Obj1 = new ObjOfSetDesign(5);
 ObjOfSetDesign Obj2 = new ObjOfSetDesign(10);
 ObjOfSetDesign Obj3 = new ObjOfSetDesign(15);

 ObjOfSetDesign Obj4;
 ObjOfSetDesign Obj5;

 public BiggerObj(int a4, int a5)
 {
  Obj4 = new ObjOfSetDesign(a4);
  Obj5 = new ObjOfSetDesign(a5);
 }

 public BiggerObj(int b4, int c4, int b5, int c5)
 {
  Obj4 = new ObjOfSetDesign(b4, c4);
  Obj5 = new ObjOfSetDesign(b5, c5);
 }
 publ BiggerObj(int d4, int e4, int f4, int d5, int e5, int f5)
 {
  Obj4 = new ObjOfSetDesign(d4, e4, f4);
  Obj5 = new ObjOfSetDesign(d5, e5, f5);
 }
}

В этом примере наш ObjOfSetDesign теперь вводится в начало нашего конструктора BiggerObj вместе с кодом для его создания, включая наши 3 конструктора и XYZ, которые теперь вводятся 9 раз. Вот как я рассчитываю, что добавить.

В ObjOfSetDesign у вас есть:

  • 9 полей = 9b
  • 3 инициализированных ints = 6b
  • 3 конструктора (~ 16b x 3) = 48b + 18b, введенные = 66b

Совокупность на этапе 81b для создания объекта, содержащего 18b данных

В BiggerObj у вас есть:

  • 5 полей = 5b
  • 3 инициализированный ObjOfSetDesign = (75b x 3) 225b
  • 3 конструктора (~ 16b x 3) = 48b + 675b, введенные = 723b

Всего на 953b для создания объекта, содержащего 90b

И это даже не включает стоимость вызова функций внутри каждого конструктора. Если в обоих случаях мы бы назвали общий конструктор для инициализации заданных полей, ObjOfSetDesign будет 73b, а BiggerObj достигнет 69b, плюс размер кода наших конструкторов (я не сказал этого, так как он не изменился бы отдельно от существует дополнительный конструктор, который бы уравновешивался исходным методом инициализации при объявлении) и до сих пор хранит одинаковый объем данных.

В более крупном масштабе, таком как ваш класс полей 100, особенно если он вложен, например, выше, это может привести к тому, что объект будет вставлять > 5 МБ кода, который будет создан, когда он на самом деле должен быть не более 50 КБ.

Надеюсь, что это поможет, и если какое-либо из этого не относится к С#, пожалуйста, дайте мне знать, чтобы я мог его исправить.