Ошибка компиляции GCC с> 2 ГБ кода - PullRequest
108 голосов
/ 09 июня 2011

У меня есть огромное количество функций, в общей сложности около 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

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

РЕДАКТИРОВАТЬ:Прежде всего, спасибо за обсуждение ... Чтобы уточнить, у меня есть сотни функций (каждый размером примерно 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, где CAS не сильно поможет, но у меня уже есть некоторые идеи.Например, сортируя выражения по переменным, таким как x12, x13,..., анализируйте csc s с помощью Python и генерируйте таблицы, которые связывают их друг с другом.Тогда я могу, по крайней мере, генерировать эти части как петли.Поскольку это, похоже, лучшее решение на данный момент, я отмечаю это как лучший ответ.

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

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

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

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

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

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

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

Ответы [ 11 ]

53 голосов
/ 10 июня 2011

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

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 -...

верно?

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

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

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

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

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

45 голосов
/ 09 июня 2011

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

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

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

EDIT

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

37 голосов
/ 10 июня 2011

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

21 голосов
/ 09 июня 2011

Ошибка возникает из-за того, что у вас слишком много КОДА, а не данных!На это указывает, например, ссылка __libc_csu_fini (которая является функцией) из _start, и перемещение усекается для соответствия.Это означает, что _start (истинная точка входа в программу) пытается вызвать эту функцию через 32-разрядное смещение SIGNED, которое имеет диапазон только 2 ГБ.Поскольку общий объем вашего объектного кода составляет ~ 2,8 ГБ, факты подтверждаются.

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

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

15 голосов
/ 10 июня 2011

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

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

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

5 голосов
/ 09 июня 2011

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

Кстати, увидев это:

(. Text + 0x20): неопределенная ссылка на `main '

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

3 голосов
/ 11 июня 2011

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

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

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

3 голосов
/ 10 июня 2011

Пара предложений: - Оптимизировать по размеру (-Os). Сделайте ваши встроенные вызовы функций, нормальные вызовы функций. Включить пул строк.

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

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

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

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

http://luajit.org/performance_x86.html

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

2 голосов
/ 10 июня 2011

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

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

2 голосов
/ 10 июня 2011

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

...