Встречались ли где — нибудь рекомендации, как лучше писать код с 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 прямо по верхней границе колеблется. Потому как вроде бы не слишком большие аппетиты каждого из инструментов суммируются в заметную цифру.