Есть ли возможность безболезненно скормить GCC больщой исходник, около 17Мб? Сейчас проблема в том, что съедается вся оперативка — 16Гб, а компиляция так до конца и не доходит. Сейчас проблена рещается тем, что файл полностью не генерится, генерится только около 7000 строк (против полных 350000) и все нормально отрабатывает.
Файл исходника — автогенеренные юнит тесты, код примерно такой:
void RunTests()
{
Test test;
test.fn(..)
test.fn2(..)
// еще порядка 8-10 вызовов функций
// следующий кейс, примерно то же самое, 8-10 вызовов методов класса Test
test.fn(..)
test.fn2(..)
// и так далее на 365000 строк
}
Здравствуйте, koenjihyakkei, Вы писали:
K>Есть ли возможность безболезненно скормить GCC больщой исходник, около 17Мб? Сейчас проблема в том, что съедается вся оперативка — 16Гб, а компиляция так до конца и не доходит.
а в чём заключается боль? можете памяти докупить, можете автогенерированный код отрефакторить, можете свап побольше сделать.
Здравствуйте, koenjihyakkei, Вы писали:
K>Есть ли возможность безболезненно скормить GCC больщой исходник, около 17Мб? Сейчас проблема в том, что съедается вся оперативка — 16Гб, а компиляция так до конца и не доходит.
Здравствуйте, koenjihyakkei, Вы писали:
K>Есть ли возможность безболезненно скормить GCC больщой исходник, около 17Мб? Сейчас проблема в том, что съедается вся оперативка — 16Гб, а компиляция так до конца и не доходит.
Чё-то слишком много он у тебя памяти жрёт. На первый взгляд там у тебя какая-то засада. Например файл сам себя инклюдит.
Здравствуйте, Kernighan, Вы писали:
K>Чё-то слишком много он у тебя памяти жрёт. На первый взгляд там у тебя какая-то засада. Например файл сам себя инклюдит.
Сам себя не инклюдит, в первом сообщении дописал. Единственное на что сейчас грешу, это то что для каждого кейса создается по одному-два локального экземпляра другого класса, возможно локальный стек все и сжирает..
Вот код поподробнее
};
class Item {
// ...
Item& add1() { ... return *this; }
Item& add2() { ... return *this; }
}
class Test {
Item create() {
// ...return *m_item;
}
}
// используется примерно так
test.create().add1().add1().add2(); // произвольное количество вызовов функций
// и так далее более 47842 раз
test.create().add1().add1().add2();
По идее компилятор должен понять, что не надо под каждый такой Item выделять место в стеке, но такое ощущение, что именно это он и делает.
Здравствуйте, koenjihyakkei, Вы писали:
K>По идее компилятор должен понять, что не надо под каждый такой Item выделять место в стеке, но такое ощущение, что именно это он и делает.
Я не понял какая связь между
а компиляция так до конца и не доходит
и
По идее компилятор должен понять, что не надо под каждый такой Item выделять место в стеке, но такое ощущение, что именно это он и делает.
Здравствуйте, ArtDenis, Вы писали:
AD>Здравствуйте, koenjihyakkei, Вы писали:
K>>По идее компилятор должен понять, что не надо под каждый такой Item выделять место в стеке, но такое ощущение, что именно это он и делает.
AD>Я не понял какая связь между
AD>
AD>а компиляция так до конца и не доходит
Не доходит — в смысле я прерываю компиляцию, ибо резко начинает жрать память =загружает всю оперативку и начинает своп юзать, а когда она завершится непонятно.
AD>и AD>
AD>По идее компилятор должен понять, что не надо под каждый такой Item выделять место в стеке, но такое ощущение, что именно это он и делает.
Ну написал же, что в одной функции более 47842 раз вызывается функция, которая создает временный объект. Но сейчас я уже проверил эту теорию, переписав функцию, чтобы она возвращала ссылку, а не копию... не помогло.
AD>
Здравствуйте, smeeld, Вы писали:
S>А где у ТС сказано, что исходник в виде одного файла?
Ну, например, в самом слове "исходник", который в единственном числе.
S>Зачем такие непотребства творить?
Ну что мне всю проблематику рассказывать сложившейся ситуации? Ну нет еще функциональных тестов, нету. Только в процессе и непонятно, когда будут готовы, а юнит-тестами можно уже сейчас все покрыть и отловить все основные баги. Проблема лишь в том, что я описал в топике.
Разбивать файл на несколько исходников и модифицировать билд файлы не вариант.
Здравствуйте, koenjihyakkei, Вы писали:
K>Есть ли возможность безболезненно скормить GCC больщой исходник, около 17Мб? Сейчас проблема в том, что съедается вся оперативка — 16Гб, а компиляция так до конца и не доходит. Сейчас проблена рещается тем, что файл полностью не генерится, генерится только около 7000 строк (против полных 350000) и все нормально отрабатывает.
K>Файл исходника — автогенеренные юнит тесты, код примерно такой:
Поставьте этому файлу -O0. Оптимизация такому файлу нафиг не сдалась, а именно она жрёт ресурсы.
K>Есть ли возможность безболезненно скормить GCC больщой исходник, около 17Мб?
Полагаю что нет. Компилятор должен загрузить твой исходник полностью, распарсить его (AST компилятора явно займет больше места в памяти чем сам исходник). Наверняка линковка тоже не быстро происходит. Вряд ли это тот случай, под который оптимизировали GCC.
Я предлагаю:
— разбить исходник на несколько единиц трансляции, два десятка cpp-шек по ~1Мб будут компилироваться быстрее
— сделать так, чтобы большинство символов в исходников имели внутреннее связывание (internal linkage)
Здравствуйте, koenjihyakkei, Вы писали:
K>Есть ли возможность безболезненно скормить GCC больщой исходник, около 17Мб? Сейчас проблема в том, что съедается вся оперативка — 16Гб, а компиляция так до конца и не доходит. Сейчас проблена рещается тем, что файл полностью не генерится, генерится только около 7000 строк (против полных 350000) и все нормально отрабатывает.
Сталкивался с подобной проблемой на разных компиляторах, как раз при генерируемом коде. Обычно дело не просто в большом translation unit (что, учитывая includes, и вовсе не редкость), а в большом объёме кода в одной из функций.
Решается это либо распилом этой функции на несколько, либо заменой кода на данные, что предпочтительней.
Например в коде функции foo десять тысяч вызовов функции bar с разными аргументами. Делаем массив наборов аргументов, а десять тысяч вызовов заменяем пробежкой по массиву однострочным циклом. Такой код компилируется на порядки быстрее, и дешевле по ресурсам.
Здравствуйте, ELazin, Вы писали:
EL>Я предлагаю: EL>- разбить исходник на несколько единиц трансляции, два десятка cpp-шек по ~1Мб будут компилироваться быстрее
Большие TU обычно сами по себе проблемой не являются. Более того, тысяча файлов собранные в десяток TU может компилироваться даже быстрее (есть реальный опыт ускорения в разы) — смотри Unity Builds.
Проблемой обычно являются функции с большими телами, а конкретно с большим количеством statements. В таких случаях вылезает нелинейная зависимость (что-то типа квадратичной) затрат времени/ресурсов компиляции от количества statements. Вероятно это как-то связанно с оптимизацией — типа компилятор пытается целиком прожевать весь заинлайненный код.
Подобные большие функции обычно появляются при кодогенерации и рассуждениях типа "ну и что что убогая копипаста, она же генерируется".
Генерировать же, по моим представлениям, нужно где-то на грани необходимого минимума — данные, структуры, обвязки необходимые для reflection, плюс те места, которые без генерации получаются чрезмерно сложными.
Сам по себе код генерирующий другой код — он сложнее обычного, его труднее поддерживать, неудобно с ним работать и т.д. и т.п. Нужно стараться его избегать, но естественно НЕ ценой копипасты.
Здравствуйте, koenjihyakkei, Вы писали:
K>Файл исходника — автогенеренные юнит тесты
Если можно автогенерённые тесты оформить в виде параметрических тестов, — то, наверное, следует это сделать.
В зависимости от фреймворка, конкретная техника может отличаться.
Но, в конце концов, хотя бы так
В любом случае, нужно разбить одну большую функцию на подфункции, потому что иначе там со стеком бог знает что творится. Конечно, гусь пытается рассовать временные переменные по регистрам и слотам стекового кадра, и тратит на это кучу сил.
В этой ветке уже несколько раз упоминали стек, а я до сих пор не понимаю причем тут он. У ТС не проходит компиляция. До выполнения exe'шника дело не доходит. Причем тут работа со стеком во время компиляции?
Здравствуйте, alex19, Вы писали:
A>В этой ветке уже несколько раз упоминали стек, а я до сих пор не понимаю причем тут он. У ТС не проходит компиляция. До выполнения exe'шника дело не доходит. Причем тут работа со стеком во время компиляции?
При том, что компилятор должен рассовать стопятьсот локальных переменных, дав им уникальные смещения в кадре стека, а заодно определив стратегию подгрузки в регистры.
Если включить оптимизацию, то задача внезапно из линейной может стать квадратичной как по времени, так и по памяти.
Здравствуйте, koenjihyakkei, Вы писали:
K>Есть ли возможность безболезненно скормить GCC больщой исходник, около 17Мб? Сейчас проблема в том, что съедается вся оперативка — 16Гб, а компиляция так до конца и не доходит. Сейчас проблена рещается тем, что файл полностью не генерится, генерится только около 7000 строк (против полных 350000) и все нормально отрабатывает.
1. Компилится ли с -O0?
2. Если да — варианты: всегда компилить без оптимизации, или же индивидуальными ключами отключать разные оптимизации (пока не найдётся виновная)
3. Если нет — ой, оно даже не парсится, только упрощать генерируемую функцию.
Ну и, как уже тут упоминалось, при оптимизации больше всего проблем вызывают большие функции — так как функция является единицей работы оптимизатора, практически всё считается для неё. А если там ещё и здоровые basic block (куча кода без ветвлений) — пиши пропало. Потому можно просто попробовать поставить лимит генерации по количеству операторов в функции, дальше создавать новую.