Ошибка компиляции GCC s> 2 ГБ кода

У меня есть огромное количество функций на общую сумму около 2,8 Гб объектного кода (к сожалению, там нет способа научного вычисления...)

Когда я пытаюсь связать их, я получаю (ожидаемые) relocation truncated to fit: R_X86_64_32S ошибки, которые я надеялся обойти, указав флаг компилятора -mcmodel=medium. Все библиотеки, которые связаны, кроме того, что я контролирую, скомпилированы с флагом -fpic.

Тем не менее, ошибка сохраняется, и я предполагаю, что некоторые библиотеки, на которые я ссылаюсь, не скомпилированы с помощью PIC.

Здесь ошибка:

/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crt1.o: In function `_start':
(.text+0x12): relocation truncated to fit: R_X86_64_32S against symbol `__libc_csu_fini'     defined in .text section in /usr/lib64/libc_nonshared.a(elf-init.oS)
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crt1.o: In function `_start':
(.text+0x19): relocation truncated to fit: R_X86_64_32S against symbol `__libc_csu_init'    defined in .text section in /usr/lib64/libc_nonshared.a(elf-init.oS)
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crti.o: In function    `call_gmon_start':
(.text+0x7): relocation truncated to fit: R_X86_64_GOTPCREL against undefined symbol      `__gmon_start__'
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtbegin.o: In function `__do_global_dtors_aux':
crtstuff.c:(.text+0xb): relocation truncated to fit: R_X86_64_PC32 against `.bss' 
crtstuff.c:(.text+0x13): relocation truncated to fit: R_X86_64_32 against symbol `__DTOR_END__' defined in .dtors section in /usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtend.o
crtstuff.c:(.text+0x19): relocation truncated to fit: R_X86_64_32S against `.dtors'
crtstuff.c:(.text+0x28): relocation truncated to fit: R_X86_64_PC32 against `.bss'
crtstuff.c:(.text+0x38): relocation truncated to fit: R_X86_64_PC32 against `.bss'
crtstuff.c:(.text+0x3f): relocation truncated to fit: R_X86_64_32S against `.dtors'
crtstuff.c:(.text+0x46): relocation truncated to fit: R_X86_64_PC32 against `.bss'
crtstuff.c:(.text+0x51): additional relocation overflows omitted from the output
collect2: ld returned 1 exit status
make: *** [testsme] Error 1

И системные библиотеки, с которыми я ссылаюсь:

-lgfortran -lm -lrt -lpthread

Любые подсказки, где искать проблему?

EDIT: Прежде всего, спасибо за обсуждение... Чтобы немного уточнить, у меня есть сотни функций (каждый примерно размером 1 МБ в отдельных объектных файлах):

double func1(std::tr1::unordered_map<int, double> & csc, 
             std::vector<EvaluationNode::Ptr> & ti, 
             ProcessVars & s)
{
    double sum, prefactor, expr;

    prefactor = +s.ds8*s.ds10*ti[0]->value();
    expr =       ( - 5/243.*(s.x14*s.x15*csc[49300] + 9/10.*s.x14*s.x15*csc[49301] +
           1/10.*s.x14*s.x15*csc[49302] - 3/5.*s.x14*s.x15*csc[49303] -
           27/10.*s.x14*s.x15*csc[49304] + 12/5.*s.x14*s.x15*csc[49305] -
           3/10.*s.x14*s.x15*csc[49306] - 4/5.*s.x14*s.x15*csc[49307] +
           21/10.*s.x14*s.x15*csc[49308] + 1/10.*s.x14*s.x15*csc[49309] -
           s.x14*s.x15*csc[51370] - 9/10.*s.x14*s.x15*csc[51371] -
           1/10.*s.x14*s.x15*csc[51372] + 3/5.*s.x14*s.x15*csc[51373] +
           27/10.*s.x14*s.x15*csc[51374] - 12/5.*s.x14*s.x15*csc[51375] +
           3/10.*s.x14*s.x15*csc[51376] + 4/5.*s.x14*s.x15*csc[51377] -
           21/10.*s.x14*s.x15*csc[51378] - 1/10.*s.x14*s.x15*csc[51379] -
           2*s.x14*s.x15*csc[55100] - 9/5.*s.x14*s.x15*csc[55101] -
           1/5.*s.x14*s.x15*csc[55102] + 6/5.*s.x14*s.x15*csc[55103] +
           27/5.*s.x14*s.x15*csc[55104] - 24/5.*s.x14*s.x15*csc[55105] +
           3/5.*s.x14*s.x15*csc[55106] + 8/5.*s.x14*s.x15*csc[55107] -
           21/5.*s.x14*s.x15*csc[55108] - 1/5.*s.x14*s.x15*csc[55109] -
           2*s.x14*s.x15*csc[55170] - 9/5.*s.x14*s.x15*csc[55171] -
           1/5.*s.x14*s.x15*csc[55172] + 6/5.*s.x14*s.x15*csc[55173] +
           27/5.*s.x14*s.x15*csc[55174] - 24/5.*s.x14*s.x15*csc[55175] +
           // ...
           ;

        sum += prefactor*expr;
    // ...
    return sum;
}

Объект s относительно невелик и сохраняет необходимые константы x14, x15,..., ds0,... и т.д., а ti просто возвращает double из внешней библиотеки. Как вы можете видеть, csc[] представляет собой предварительно вычисленную карту значений, которая также оценивается в отдельных объектных файлах (опять же сотни с размером приблизительно 1 Мбайт каждого) следующей формы:

void cscs132(std::tr1::unordered_map<int,double> & csc, ProcessVars & s)
{
    {
    double csc19295 =       + s.ds0*s.ds1*s.ds2 * ( -
           32*s.x12pow2*s.x15*s.x34*s.mbpow2*s.mWpowinv2 -
           32*s.x12pow2*s.x15*s.x35*s.mbpow2*s.mWpowinv2 -
           32*s.x12pow2*s.x15*s.x35*s.x45*s.mWpowinv2 -
           32*s.x12pow2*s.x25*s.x34*s.mbpow2*s.mWpowinv2 -
           32*s.x12pow2*s.x25*s.x35*s.mbpow2*s.mWpowinv2 -
           32*s.x12pow2*s.x25*s.x35*s.x45*s.mWpowinv2 +
           32*s.x12pow2*s.x34*s.mbpow4*s.mWpowinv2 +
           32*s.x12pow2*s.x34*s.x35*s.mbpow2*s.mWpowinv2 +
           32*s.x12pow2*s.x34*s.x45*s.mbpow2*s.mWpowinv2 +
           32*s.x12pow2*s.x35*s.mbpow4*s.mWpowinv2 +
           32*s.x12pow2*s.x35pow2*s.mbpow2*s.mWpowinv2 +
           32*s.x12pow2*s.x35pow2*s.x45*s.mWpowinv2 +
           64*s.x12pow2*s.x35*s.x45*s.mbpow2*s.mWpowinv2 +
           32*s.x12pow2*s.x35*s.x45pow2*s.mWpowinv2 -
           64*s.x12*s.p1p3*s.x15*s.mbpow4*s.mWpowinv2 +
           64*s.x12*s.p1p3*s.x15pow2*s.mbpow2*s.mWpowinv2 +
           96*s.x12*s.p1p3*s.x15*s.x25*s.mbpow2*s.mWpowinv2 -
           64*s.x12*s.p1p3*s.x15*s.x35*s.mbpow2*s.mWpowinv2 -
           64*s.x12*s.p1p3*s.x15*s.x45*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.p1p3*s.x25*s.mbpow4*s.mWpowinv2 +
           32*s.x12*s.p1p3*s.x25pow2*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.p1p3*s.x25*s.x35*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.p1p3*s.x25*s.x45*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.p1p3*s.x45*s.mbpow2 +
           64*s.x12*s.x14*s.x15pow2*s.x35*s.mWpowinv2 +
           96*s.x12*s.x14*s.x15*s.x25*s.x35*s.mWpowinv2 +
           32*s.x12*s.x14*s.x15*s.x34*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.x14*s.x15*s.x35*s.mbpow2*s.mWpowinv2 -
           64*s.x12*s.x14*s.x15*s.x35pow2*s.mWpowinv2 -
           32*s.x12*s.x14*s.x15*s.x35*s.x45*s.mWpowinv2 +
           32*s.x12*s.x14*s.x25pow2*s.x35*s.mWpowinv2 +
           32*s.x12*s.x14*s.x25*s.x34*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.x14*s.x25*s.x35pow2*s.mWpowinv2 -
           // ...

       csc.insert(cscMap::value_type(192953, csc19295));
    }

    {
       double csc19296 =      // ... ;

       csc.insert(cscMap::value_type(192956, csc19296));
    }

    // ...
}

Что об этом. Затем последний шаг состоит в вызове всех этих func[i] и суммировании результата.

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

EDIT2: Следует также добавить, что x12, x13 и т.д. На самом деле не являются константами. Они устанавливаются в определенные значения, все эти функции запускаются, и результат возвращается, а затем выбирается новый набор x12, x13 и т.д. Для получения следующего значения. И это нужно сделать 10 ^ 5 до 10 ^ 6 раз...

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

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

EDIT4: Таким образом, мне удалось уменьшить размер кода в определениях csc примерно на четвертый, упростив выражения в системе компьютерной алгебры (Mathematica). Я также вижу способ уменьшить его еще на один порядок, применив некоторые другие трюки, прежде чем генерировать код (который доведёт эту часть до 100 МБ), и я надеюсь, что эта идея будет работать.

Теперь о ваших ответах: Я пытаюсь снова запустить петли в func s, где CAS не поможет, но у меня уже есть некоторые идеи. Например, сортировка выражений с помощью переменных типа x12, x13,..., разбора csc с помощью Python и генерации таблиц, которые связывают их друг с другом. Тогда я могу по крайней мере сгенерировать эти части как петли. Поскольку это, кажется, лучшее решение до сих пор, я считаю это лучшим ответом.

Однако я хотел бы также отдать должное VJo. GCC 4.6 действительно работает намного лучше, производит меньший код и работает быстрее. Использование большой модели работает в коде как есть. Так технически это правильный ответ, но изменение всей концепции - гораздо лучший подход.

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

ПРИМЕЧАНИЯ: Просто некоторые замечания к некоторым другим ответам: Код, который я пытаюсь запустить, не возникает из-за расширения простых функций/алгоритмов и глупой ненужной разворачивания. Что на самом деле происходит, так это то, что материал, с которого мы начинаем, представляет собой довольно сложные математические объекты и вывод их в числовую вычислимую форму, генерирует эти выражения. Проблема заключается в лежащей в основе физической теории. Сложность промежуточных выражений оценивается факториально, что хорошо известно, но при объединении всего этого материала с чем-то физически измеряемым - наблюдаемым - оно просто сводится к лишь нескольким очень маленьким функциям, которые составляют основу выражений. (В этом отношении определенно "неправильно" с общим и доступным ansatzкоторый называется "теорией возмущений" ). Мы пытаемся довести этот анзац до другого уровня, который уже не является выполнимым аналитически и где база необходимых функций неизвестна. Поэтому мы пытаемся сделать это как можно более грубо. Не лучший способ, но, надеюсь, тот, который помогает в нашем понимании физики в конце концов...

ПОСЛЕДНИЙ РЕДАКТ: Благодаря всем вашим предложениям мне удалось значительно уменьшить размер кода, используя Mathematica и модификацию генератора кода для func несколько по строкам верхнего ответа:)

Я упростил функции csc с Mathematica, доведя ее до 92 Мб. Это неприводимая часть. Первые попытки выполнялись навсегда, но после некоторых оптимизаций это теперь проходит через около 10 минут на одном CPU.

Эффект от func был драматичным: весь размер кода для них составляет примерно 9 Мб, поэтому код теперь суммируется в диапазоне 100 Мб. Теперь имеет смысл включить оптимизацию, и выполнение выполняется довольно быстро.

Снова, спасибо всем за ваши предложения, я многому научился.

Ответ 1

Итак, у вас уже есть программа, которая создает этот текст:

prefactor = +s.ds8*s.ds10*ti[0]->value();
expr = ( - 5/243.*(s.x14*s.x15*csc[49300] + 9/10.*s.x14*s.x15*csc[49301] +
       1/10.*s.x14*s.x15*csc[49302] - 3/5.*s.x14*s.x15*csc[49303] -...

и

double csc19295 =       + s.ds0*s.ds1*s.ds2 * ( -
       32*s.x12pow2*s.x15*s.x34*s.mbpow2*s.mWpowinv2 -
       32*s.x12pow2*s.x15*s.x35*s.mbpow2*s.mWpowinv2 -
       32*s.x12pow2*s.x15*s.x35*s.x45*s.mWpowinv2 -...

верно?

Если все ваши функции имеют аналогичный "формат" (умножьте n чисел m раз и добавьте результаты - или что-то подобное), то я думаю, вы можете это сделать:

  • измените программу-генератор на выходные смещения вместо строк (т.е. вместо строки "s.ds0" она произведет offsetof(ProcessVars, ds0)
  • создать массив таких смещений
  • напишите оценщика, который принимает приведенный выше массив и базовые адреса указателей на структуру и создает результат

Анализатор массива + будет представлять ту же логику, что и одна из ваших функций, но только оценщик будет кодом. Массив - это "данные" и может быть либо сгенерирован во время выполнения, либо сохранен на диске, и читать куски я или с отображенным в памяти файлом.

В вашем конкретном примере в func1 представьте, как вы могли бы переписать функцию через оценщика, если бы у вас был доступ к базовому адресу s и csc, а также векторное представление констант и смещений, которое вам нужно добавьте к базовым адресам, чтобы добраться до x14, ds8 и csc[51370]

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

Ответ 2

x86-64 ABI, используемый Linux, определяет "большую модель" специально, чтобы избежать таких ограничений по размеру, в том числе 64-битные типы перемещения для GOT и PLT. (См. Таблицу в разделе 4.4.2 и последовательности инструкций в 3.5.5, которые показывают, как они используются.)

Поскольку ваши функции занимают 2,8 ГБ, вам не повезло, потому что gcc не поддерживает большие модели. Что вы можете сделать, это реорганизовать ваш код таким образом, чтобы вы могли разделить его на разделяемые библиотеки, которые вы бы динамически связывали.

Если это невозможно, как было предложено кем-то, вместо того, чтобы помещать ваши данные в код (компилируя и связывая его), поскольку он огромен, вы можете загрузить его во время выполнения (либо как обычный файл, либо вы можете mmap она).

ИЗМЕНИТЬ

Похоже, что большая модель поддерживается gcc 4.6 (см. на этой странице). Вы можете попробовать это, но выше все еще применяется о реорганизации вашего кода.

Ответ 3

С программой этой стороны промахи кэша для кода, скорее всего, превысят затраты на цикл во время выполнения. Я бы порекомендовал вам вернуться к генератору кода и заставить его сгенерировать некоторое компактное представление для того, что он хочет оценить (то есть, скорее всего, поместится в D-кеше), а затем выполнить это с помощью интерпретатора в вашей программе. Вы также можете увидеть, можете ли вы отбросить меньшие ядра, которые все еще имеют значительное количество операций, а затем использовать их как "инструкции" в интерпретируемом коде.

Ответ 4

Ошибка возникает из-за слишком большого количества CODE, а не данных! Это указывается, например, на __libc_csu_fini (которая является функцией), ссылающейся на _start, и перемещение усекается, чтобы соответствовать. Это означает, что _start (истинная точка входа в программу) пытается вызвать эту функцию через 32-битное смещение SIGNED, которое имеет только 2 & GB. Поскольку общая сумма вашего объектного кода составляет ~ 2.8 GB, проверка фактов.

Если вы можете перепроектировать свои структуры данных, большая часть вашего кода может быть "сжата", переписав огромные выражения в виде простых циклов.

Кроме того, вы можете вычислить csc[] в другой программе, сохранить результаты в файле и просто загрузить их при необходимости.

Ответ 5

Я думаю, все согласны, что должен быть другой способ делать то, что вы хотите сделать. Составляя сотни мегабайт (гигабайт?) Кода, связывая его с исполняемым файлом с несколькими гигабайтами и запуская его, просто звучит очень неэффективно.

Если я правильно понимаю вашу проблему, вы используете какой-то генератор кода G, чтобы сгенерировать кучу функций func1...N, которые берут кучу карт csc1...M в качестве входных данных. То, что вы хотите сделать, это вычислить csc1...M и запустить цикл в 1 000 000 раз для разных входов и каждый раз находить s = func1 + func2 + ... + funcN. Вы не указали, как fucn1...N связаны с csc1...M.

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

Помимо практической проблемы размеров объектных файлов, ваша текущая программа не будет эффективной, поскольку она не локализует доступ к данным (слишком много огромных карт) и не имеет локализованного выполнения кода (слишком много очень длинных функций).

Как разбить вашу программу на 3 фазы: Фаза 1 построит csc1...M и сохранит их. Фаза 2 строит один func за раз, запускайте его 1 000 000 раз с каждым входом и сохраняйте результаты. Фаза 3 набирает сумму результатов сохраненных результатов func1...N для каждого из 100 000 раз. Хорошая часть этого решения заключается в том, что его можно легко выполнить параллельно на нескольких независимых машинах.

Изменить: @bbtrb, можете ли вы создать один func и один csc-доступный? Они кажутся очень регулярными и сжимаемыми. Например, func1 представляется всего лишь суммой выражений, каждая из которых состоит из 1 коэффициента, 2 индекса для переменных по s и 1 индекса в csc. Поэтому его можно свести к хорошему циклу. Если вы делаете полные примеры доступными, я уверен, что можно найти способы сжать их в циклы, а не длинные выражения.

Ответ 6

Если я правильно прочитал ваши ошибки, то что заставляет вас переносить предел - это раздел инициализированных данных (если бы это был код, у вас было бы гораздо больше ошибок IMHO). У вас есть большие массивы глобальных данных? Если это так, я бы реструктурировал программу, чтобы они были распределены динамически. Если данные инициализированы, я бы прочитал их из файла конфигурации.

Кстати, видя это:

(. text + 0x20): undefined ссылка на `main '

Я думаю, у вас есть еще одна проблема.

Ответ 7

Несколько предложений: - Оптимизируйте размер (-O). Выполняйте свои встроенные функции, обычные вызовы функций. Включить объединение строк.

Попробуйте разделить вещи на разные DLL (общие объекты,.so для linux,.dylib для Mac OS X). Убедитесь, что они могут быть выгружены. Затем реализуйте что-то, чтобы загружать вещи по требованию, и освобождайте их, когда они не нужны.

Если нет, разделите свой код на разные исполняемые файлы и используйте что-то для связи между ними (каналы, сокеты, даже запись/чтение в файл). Неуклюжий, но какие у вас есть варианты?

Полностью альтернатива: - Используйте динамический язык с JIT. Прямо на моей голове - используйте LuaJIT - и перепишите (регенерируйте?) Много этих выражений в Lua или другие такие языки и время автономной работы, которые позволяют собирать мусор.

LuaJIT довольно эффективен, иногда избивая C/С++ для определенных вещей, но часто очень близко (иногда может быть медленным из-за плохой коллекции мусора, но там). Убедитесь сами:

http://luajit.org/performance_x86.html

Загрузите файл scimark2.lua и сравните его с версией "C" (google it) - часто результаты очень близки.

Ответ 8

Мне кажется, что код выполняет численное интегрирование, используя какой-то метод адаптивной глубины. К сожалению, генератор кода (или, скорее, автор генератора кода) настолько глуп, что генерирует одну функцию на патч, а не один тип патча. Таким образом, он продуцировал слишком много кода для компиляции, и даже если его можно было бы скомпилировать, его выполнение было бы болезненным, потому что ничто никогда не делилось нигде. (Можете ли вы представить себе боль, возникающую из-за необходимости загружать каждую страницу объектного кода с диска, потому что ничто никогда не делится, и поэтому всегда кандидат на вытеснение ОС. Не говоря уже о кэшах команд, которые будут бесполезны.)

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

Нижняя строка: компиляция и привязка 2.8 ГБ кода не работает и не должна быть принудительно работать. Найдите другой способ.

Ответ 9

Эти выражения очень похожи на меняющиеся ряды. Я не знаю, как выглядит остальная часть кода, но не похоже, что было бы сложно получить генерирующее выражение. Вероятно, это будет стоить и во время выполнения, особенно если у вас 2,8-килобайтный 2-килограммовый разворачиваемый код KB.

Ответ 10

Компонент пытается создать 32-битные смещения смещения в двоичном формате, который каким-то образом превысил эти ограничения. Попробуйте уменьшить требования к адресному пространству основной программы.

Можете ли вы разделить часть/часть объектного кода на одну или несколько библиотек (также скомпилированных с -fpic/-fPIC)? Затем создайте нестатический двоичный файл, который связывается с этими lib. Библиотеки будут жить в дискретных блоках памяти, а смещения смещения будут динамическими/абсолютными (64-разрядными), а не относительными (32-разрядными).

Ответ 11

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