Встречались ли где — нибудь рекомендации, как лучше писать код с Generics, чтобы меньше нагружать компилятор?
Например, насколько будет разница в используетмой памяти, скорости компиляции, итоовом коде при компиляции при таком использованнии Generics:
Код1
TStringMap = TDictionary<string,string>;
class TMyClass
FMap : TStringMap;
constructor Create;
end;
constructor TMyClass.Create;
begin
inherited;
FMap := TStringMap.Create;
end;
или
Код2
class TMyClass;
FMap : TDictionary<string,string>;
constructor Create;
end;
constructor TMyClass.Create;
begin
inherited;
FMap := TDictionary<string,string>.Create;
end;
Проблема в том , что заканчивается память при компиляции из среды (bds.exe доходит до предела 3Гб).
Программа порядка 1,8 млн строк (только своих) + сторонние компоненты.
Около 2500 только своих модулей
Delphi 10.4.2
Подозреваю что больше всего нагружает компилятор циклические зависимости между модулями (но они почти все разрулены) и сложные конструкции с Generics.
В программе много кода в стиле типа такого
Код3
procedure TfrmTopGraphDM.actSaveNearestGIds4ShRPExecute(Sender: TObject);
type
RPair = record
nb1, nb2: TNB;
end;
var
sch: TSchemeSet;
lPairs: TRStream<String>;
begin
sch := App.SDEDocs.Active;
if not Assigned(sch) then exit;
lPairs :=
TRStream<TNB>.stream<Integer>(sch.Techs)
.map<TNB>(
procedure (aNB: TNB; var aMap: TProc<TNB>)
begin
if (aNB.IsChildOrEqual(ttcPowerTransformer) and TCEAPI.isShRP(aNB)) then
aMap(aNB);
end)
.map<RPair>(
procedure (aNB: TNB; var aMap: TProc<RPair>)
var
lSwitch: TCE;
lPair: RPair;
begin
lSwitch := TRStream<TNB>
.just(aNB)
.map<TCE>(TCeAPI.ce)
.map(TCeAPI.find(TCERules.windingExtraTerminalsRules,
function (aCe: TCE): Boolean
begin
Result := aCe.IsChildOrEqual(ttcSwitch) and not TCEAPI.isCandle(aCE) and (TCEAPI.globalId(aCE) <> '');
end, True))
.first
.toArray()
.firstItem;
if Assigned(lSwitch) then
begin
lPair.nb1 := aNB;
lPair.nb2 := lSwitch;
aMap(lPair);
end;
end)
.map<String>(
procedure (aPair: RPair; var aMap: TProc<String>)
begin
aMap(TTopGraphUtils.SaveNB(aPair.nb1) + CSV_DELIMETER + TTopGraphUtils.SaveNB(aPair.nb2));
end);
TRIO.saveToFile(lPairs);
end;
Где составляющие выражений часто повторяются в разных методах.
Вопрос, как его лучше оптимизировать для компилятора.
Здравствуйте, rudzuk, Вы писали:
R>Здравствуйте, swame, Вы писали:
R>Можно попробовать включить сборку внешним компилятором. Ну и от циклических зависимостей лучше избавиться.
Из командной строки оно пока собирается.
Надо, чтоб работала отладка.
Развесистые циклические зависимости могут сильно гадить, но они вычищены,
остались только сложно устранимые зависимости межлу 2-3 модулями, думаю сейчас они не делают погоды.
Здравствуйте, swame, Вы писали:
s> R>Можно попробовать включить сборку внешним компилятором. Ну и от циклических зависимостей лучше избавиться.
s> Из командной строки оно пока собирается.
Не, я о настройке компиляции Use MSBuild externally to compile
s> Надо, чтоб работала отладка.
Для этого нужно включить Include remote debug symbols в настройке линкера.
Ну и о дженериках. Дженерик это только заготовка, поэтому, сколько бы ни было объявлений, например, TDictionary<string, string> это всегда будет один и тот же конкретизированный тип.
Здравствуйте, rudzuk, Вы писали:
R>Здравствуйте, swame, Вы писали:
R>Ну и о дженериках. Дженерик это только заготовка, поэтому, сколько бы ни было объявлений, например, TDictionary<string, string> это всегда будет один и тот же конкретизированный тип.
ТО есть создается один тип в exe при использовании количества конструкций TDictionary<string, string> такого дженерика в exe?
Возможно так и есть в простых случаях.
Есть еще варианты дженериков от дженериков, методов от дженериков. Все это у нис используется.
В любом случае, для того чтобы компилятору понять, что уже есть такая сущность, нужна память и время.
Вот и хотелось разобраться, что для этого существенно что нет, прежде чем переделывать лесятки тысях строк в коде.
Потому как померять эффект при небольщом количестве изменений непонятно.
Здравствуйте, rudzuk, Вы писали:
R>Здравствуйте, swame, Вы писали:
s>> R>Можно попробовать включить сборку внешним компилятором. Ну и от циклических зависимостей лучше избавиться.
s>> Из командной строки оно пока собирается.
R>Не, я о настройке компиляции Use MSBuild externally to compile
Не очень вариант, так компилится намного медленней.
Здравствуйте, swame, Вы писали:
s> R>Ну и о дженериках. Дженерик это только заготовка, поэтому, сколько бы ни было объявлений, например, TDictionary<string, string> это всегда будет один и тот же конкретизированный тип.
s> ТО есть создается один тип в exe при использовании количества конструкций TDictionary<string, string> такого дженерика в exe?
Да.
s> Есть еще варианты дженериков от дженериков, методов от дженериков. Все это у нис используется.
По идее, это роли играть не должно.
s> В любом случае, для того чтобы компилятору понять, что уже есть такая сущность, нужна память и время. s> Вот и хотелось разобраться, что для этого существенно что нет, прежде чем переделывать лесятки тысях строк в коде. s> Потому как померять эффект при небольщом количестве изменений непонятно.
Вообще, дженерики, конечно затрудняют компиляцию и увеличивают количество генерируемого кода, априори. Поэтому, если есть возможность их не использовать, то лучше ею воспользоваться. По количеству кода в конкретных юнитах может помочь https://github.com/VilleKrumlinde/delphiunitsizes
Здравствуйте, rudzuk, Вы писали:
R>Вообще, дженерики, конечно затрудняют компиляцию и увеличивают количество генерируемого кода, априори. Поэтому, если есть возможность их не использовать, то лучше ею воспользоваться. По количеству кода в конкретных юнитах может помочь https://github.com/VilleKrumlinde/delphiunitsizes
За 13 лет прошедших с выхода Delphi 2010 ничего не изменилось...
Здравствуйте, BlackEric, Вы писали:
BE> R>Вообще, дженерики, конечно затрудняют компиляцию и увеличивают количество генерируемого кода, априори. Поэтому, если есть возможность их не использовать, то лучше ею воспользоваться. По количеству кода в конкретных юнитах может помочь https://github.com/VilleKrumlinde/delphiunitsizes
BE> За 13 лет прошедших с выхода Delphi 2010 ничего не изменилось...
Это не столько к компилятору претензия, сколько к самим дженерикам.
Здравствуйте, BlackEric, Вы писали:
BE>Здравствуйте, rudzuk, Вы писали:
R>>Вообще, дженерики, конечно затрудняют компиляцию и увеличивают количество генерируемого кода, априори. Поэтому, если есть возможность их не использовать, то лучше ею воспользоваться. По количеству кода в конкретных юнитах может помочь https://github.com/VilleKrumlinde/delphiunitsizes
BE>За 13 лет прошедших с выхода Delphi 2010 ничего не изменилось...
С самого начала работаю с ними, все — таки улучшилось.
R>Вообще, дженерики, конечно затрудняют компиляцию и увеличивают количество генерируемого кода, априори. Поэтому, если есть возможность их не использовать, то лучше ею воспользоваться. По количеству кода в конкретных юнитах может помочь https://github.com/VilleKrumlinde/delphiunitsizes
Дженерики это хорошо, радикально упрощают код. Но надо пользоваться с умом. ЛУчше больше олного-лвух уровней вложенности не использовать.
Здравствуйте, BlackEric, Вы писали:
BE>Здравствуйте, swame, Вы писали:
BE>Разбивайте приложение на проекты: dll, bpl и т.д. Что бы можно было собирать независимо по частям. Это резко упростит сбоку и отладку.
Это очевидно, но у нас специфика программы такая, что все попытки рефакторинга на "удаленные фасады" и прочее ведет к дикому усложнению и торможению влаимодействия модулей.
А те части, которые легко логически отделяются, и так небольшие.
Что можно, выделяем.
Здравствуйте, rudzuk, Вы писали:
R>Ну и о дженериках. Дженерик это только заготовка, поэтому, сколько бы ни было объявлений, например, TDictionary<string, string> это всегда будет один и тот же конкретизированный тип.
Прошу прощения, а можете одним абзацем описать, что такое tdictionary и для чего он используется? Я пока не понял. Это не то же самое, что массив из рекордов, содержащих два поля?
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Здравствуйте, Khimik, Вы писали:
K> Прошу прощения, а можете одним абзацем описать, что такое tdictionary и для чего он используется? Я пока не понял. Это не то же самое, что массив из рекордов, содержащих два поля?
Здравствуйте, swame, Вы писали:
S>bds.exe доходит до предела 3Гб
Очевидный вариант — это просто доставить ОЗУ в машины. 3 Гб по нынешним меркам это не очень много. Сейчас разработчику нужно >= 16 Гб ОЗУ чтобы более или менее себя комфортно чувствовать с современным ПО.
Здравствуйте, Aquilaware, Вы писали:
A> S>bds.exe доходит до предела 3Гб
A> Очевидный вариант — это просто доставить ОЗУ в машины. 3 Гб по нынешним меркам это не очень много.
bds.exe 32-битный процесс, он больше 4GB (адресного пространства!) не съест.
A> Сейчас разработчику нужно >= 16 Гб ОЗУ чтобы более или менее себя комфортно чувствовать с современным ПО.
Здравствуйте, BlackEric, Вы писали:
BE>За 13 лет прошедших с выхода Delphi 2010 ничего не изменилось...
Воспоминания, воспоминания...
Delphi 5, проект 300к строк компилировался нормально, но в конце иногда сдыхал линкер. Поменяли на линкер от Delphi 2005 — проблема ушла.
Вывод — надо просто подождать
Здравствуйте, rudzuk, Вы писали:
R>Здравствуйте, Aquilaware, Вы писали:
A>> Сейчас разработчику нужно >= 16 Гб ОЗУ чтобы более или менее себя комфортно чувствовать с современным ПО.
R>Менеджед ПО — зло.
При чём тут managed или нет? Всё от задач засисит. Вот у меня сейчас ноут 16 гиг, всё более-менее норм. Но если запустить докер с виртуалкой какой-нибудь, а-ля Ubuntu с кубернетисами, то ой, RAM прямо по верхней границе колеблется. Потому как вроде бы не слишком большие аппетиты каждого из инструментов суммируются в заметную цифру.
Здравствуйте, Mr.Delphist, Вы писали:
MD>Здравствуйте, BlackEric, Вы писали:
BE>>За 13 лет прошедших с выхода Delphi 2010 ничего не изменилось...
MD>Воспоминания, воспоминания... MD>Delphi 5, проект 300к строк компилировался нормально, но в конце иногда сдыхал линкер. Поменяли на линкер от Delphi 2005 — проблема ушла. MD>Вывод — надо просто подождать
Сказать заказчикам — давайте подождем дат 10, пока появится компилятор который cможет собрать мое приложение с дженериками?
НО картина может быть и обратная — Delhii до 7 спокойно, хотя может и долго собирало приложение с большим количеством циклических зависимостей,
а более поздние компиляторы, например 2010 и более поздние на таком приложении просто дохли, приходилось изничтожать эти зависимости.
Здравствуйте, Mr.Delphist, Вы писали:
MD>Здравствуйте, rudzuk, Вы писали:
R>>Здравствуйте, Aquilaware, Вы писали:
A>>> Сейчас разработчику нужно >= 16 Гб ОЗУ чтобы более или менее себя комфортно чувствовать с современным ПО.
R>>Менеджед ПО — зло.
MD>При чём тут managed или нет? Всё от задач засисит. Вот у меня сейчас ноут 16 гиг, всё более-менее норм. Но если запустить докер с виртуалкой какой-нибудь, а-ля Ubuntu с кубернетисами, то ой, RAM прямо по верхней границе колеблется. Потому как вроде бы не слишком большие аппетиты каждого из инструментов суммируются в заметную цифру.
Здравствуйте, Mr.Delphist, Вы писали:
MD> R>Менеджед ПО — зло.
MD> При чём тут managed или нет? Всё от задач засисит. Вот у меня сейчас ноут 16 гиг, всё более-менее норм. Но если запустить докер с виртуалкой какой-нибудь, а-ля Ubuntu с кубернетисами, то ой, RAM прямо по верхней границе колеблется. Потому как вроде бы не слишком большие аппетиты каждого из инструментов суммируются в заметную цифру.
Помню-помню: любую проблему можно решить введением слоя абстракции, кроме проблемы слишком большого количества слоев абстракций.
Здравствуйте, swame, Вы писали:
s> MD>Вывод — надо просто подождать
s> Сказать заказчикам — давайте подождем дат 10, пока появится компилятор который cможет собрать мое приложение с дженериками?
Кстати, 64-битные компиляторы могут появиться уже в 12 версии, т.е. в сентябре (не факт, но вероятность есть).