gcc / g ++: ошибка при компиляции большого файла - PullRequest
2 голосов
/ 30 ноября 2009

У меня есть автоматически сгенерированный исходный файл C ++, размером около 40 МБ. Он в основном состоит из команд push_back для некоторых векторов и строковых констант, которые должны быть выдвинуты.

Когда я пытаюсь скомпилировать этот файл, g ++ завершает работу и говорит, что не может зарезервировать достаточно виртуальной памяти (около 3 ГБ). Погуглив эту проблему, я обнаружил, что с помощью командной строки переключается

--param ggc-min-expand=0 --param ggc-min-heapsize=4096

может решить проблему. Однако они, похоже, работают только при включенной оптимизации.

1) Это действительно то решение, которое я ищу?

2) Или есть более быстрый, лучший (компиляция занимает много времени с этими опциями)?

С наилучшими пожеланиями,

Александр

Обновление: Спасибо за все хорошие идеи. Я попробовал большинство из них. Использование массива вместо нескольких операций push_back () уменьшило использование памяти, но, поскольку файл, который я пытался скомпилировать, был настолько большим, он все же потерпел крах, только позже. В некотором смысле, это поведение действительно интересно, так как в таких условиях оптимизировать особо нечего - что делает GCC за кулисами, который стоит так много памяти? (Я также скомпилировал с деактивацией всех оптимизаций и получил те же результаты)

Решением, на которое я переключился, является чтение исходных данных из двоичного объектного файла, который я создал из исходного файла с использованием objcopy. Это то, что я изначально не хотел делать, потому что создание структур данных на языке более высокого уровня (в данном случае Perl) было удобнее, чем делать это в C ++.

Однако запустить его под Win32 оказалось сложнее, чем ожидалось. Кажется, что objcopy генерирует файлы в формате ELF, и кажется, что некоторые проблемы исчезли, когда я вручную установил формат вывода на pe-i386. Символы в объектном файле по стандарту названы в честь имени файла, например, преобразование файла inbuilt_training_data.bin приведет к получению следующих двух символов: binary_inbuilt_training_data_bin_start и binary_inbuilt_training_data_bin_end. Я нашел в Интернете несколько учебных пособий, в которых утверждается, что эти символы должны быть объявлены как extern char _binary_inbuilt_training_data_bin_start;, но, похоже, это неверно - у меня сработал только extern char binary_inbuilt_training_data_bin_start;.

Ответы [ 6 ]

4 голосов
/ 30 ноября 2009

Возможно, вам лучше использовать постоянную таблицу данных. Например, вместо этого:

void f() {
    a.push_back("one");
    a.push_back("two");
    a.push_back("three");
    // ...
}

попробуйте сделать это:

const char *data[] = {
    "one",
    "two",
    "three",
    // ...
};

void f() {
    for (size_t i = 0; i < sizeof(data)/sizeof(data[0]); i++) {
        a.push_back(data[i]);
    }
}

Компилятор, вероятно, будет гораздо эффективнее генерировать большую таблицу константных данных, а не огромные функции, содержащие множество вызовов push_back().

1 голос
/ 30 ноября 2009

Похоже, ваше автоматически сгенерированное приложение выглядит так:

push_back(data00001);
...
push_back(data99999);

Почему бы вам не поместить данные во внешний файл и не позволить программе прочитать эти данные в цикле?

1 голос
/ 30 ноября 2009

Можете ли вы решить ту же проблему, не создавая C ++ на 40 МБ? Это больше, чем некоторые операционные системы, которые я использовал. Возможно, цикл и некоторые файлы данных?

0 голосов
/ 30 ноября 2009

Если вы не можете реорганизовать свой код, вы можете попытаться увеличить объем пространства подкачки, если ваша операционная система поддерживает большое адресное пространство. Это должно работать на 64-битных компьютерах, но 3 гигабайта может быть слишком много для 32-битной системы.

0 голосов
/ 30 ноября 2009

Чтобы дополнить некоторые ответы здесь, вам может быть лучше создать двоичный объектный файл и связать его напрямую - в отличие от компиляции файлов, состоящих из const char[].

В последнее время у меня была похожая проблема с gcc. (Около 60 МБ данных PNG разделены на около 100 заголовочных файлов.) Включение их всех - наихудший вариант: объем необходимой памяти, по-видимому, растет в геометрической прогрессии с размером блока компиляции.

0 голосов
/ 30 ноября 2009

Если вы просто генерируете ряд вызовов на push_back() подряд, вы можете изменить его на что-то вроде этого:

// Old code:
v.push_back("foo");
v.push_back("bar");
v.push_back("baz");

// Change that to this:
{
    static const char *stuff[] = {"foo", "bar", "baz"};
    v.insert(v.end(), stuff, stuff + ARRAYCOUNT(stuff));
}

Где ARRAYCOUNT - это макрос, определяемый следующим образом:

#define ARRAYCOUNT(a) (sizeof(a) / sizeof(a[0]))

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

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

...