У меня огромное количество функций на общую сумму около 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
просто возвращает двойное значение из внешней библиотеки. Как видите, 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
s с помощью Python и сгенерируйте таблицы, которые связывают их друг с другом. Тогда я могу хотя бы сгенерировать эти части в виде петель. Поскольку это пока что кажется лучшим решением, я отмечаю его как лучший ответ.
Однако я хотел бы также отдать должное VJo. GCC 4.6 действительно работает намного лучше, производит меньше кода и быстрее. Использование большой модели работает с кодом как есть. Так что технически это правильный ответ, но изменение всей концепции - гораздо лучший подход.
Спасибо всем за ваши предложения и помощь. Если кому-то интересно, я опубликую окончательный результат, как только буду готов.
ЗАМЕЧАНИЯ: просто некоторые замечания к некоторым другим ответам: код, который я пытаюсь запустить, не возникает из расширения простых функций / алгоритмов и глупого ненужного развертывания. На самом деле происходит то, что мы начинаем с довольно сложных математических объектов, и приведение их к численно вычислимой форме генерирует эти выражения. Проблема на самом деле заключается в основной физической теории. Сложность промежуточных выражений масштабируется факториально, что хорошо известно, но при объединении всего этого материала с чем-то физически измеримым - наблюдаемым - это просто сводится к небольшому количеству очень маленьких функций, которые составляют основу выражений. (Есть определенно что-то "неправильное" в этом плане с общими и единственно доступнымианзац, который называется «теорией возмущений»). Мы пытаемся вывести этот анзац на другой уровень, который аналитически уже невозможен и где не известен базис необходимых функций. Итак, мы пытаемся использовать его таким образом. Не лучший способ, но, надеюсь, тот, который, в конце концов, поможет нам понять физику ...
ПОСЛЕДНЕЕ РЕДАКТИРОВАНИЕ:
Благодаря всем вашим предложениям мне удалось значительно уменьшить размер кода, используя Mathematica и модификацию генератора кода для func
s в некоторой степени в соответствии с верхним ответом :)
Я упростил csc
функции с помощью Mathematica, уменьшив его до 92 МБ. Это неприводимая часть. Первые попытки заняли много времени, но после некоторой оптимизации теперь они выполняются примерно за 10 минут на одном процессоре.
Эффект на func
s был драматическим: размер всего кода для них уменьшился примерно до 9 МБ, поэтому теперь общий размер кода составляет 100 МБ. Теперь имеет смысл включить оптимизацию, и выполнение происходит довольно быстро.
Еще раз спасибо всем за ваши предложения, я многому научился.
источник
mmap
этого сами из внешнего двоичного файла во время выполнения.Ответы:
Итак, у вас уже есть программа, которая выводит этот текст:
и
право?
Если все ваши функции имеют похожий «формат» (умножьте n чисел m раз и сложите результаты - или что-то подобное), то я думаю, вы можете сделать это:
offsetof(ProcessVars, ds0)
Оценщик массив + будет представлять ту же логику, что и одна из ваших функций, но только оценщик будет кодом. Массив представляет собой «данные» и может быть либо сгенерирован во время выполнения, либо сохранен на диске и прочитан фрагментами или с файлом, отображаемым в память.
Для вашего конкретного примера в func1 себе представить , как бы вы переписать функцию через оценщик , если вы имели доступ к базовому адресу
s
и ,csc
а также вектор , как представление констант и смещения , которые необходимо добавить к базовым адресам , чтобы добраться доx14
,ds8
иcsc[51370]
Вам необходимо создать новую форму «данных», которая будет описывать, как обрабатывать фактические данные, которые вы передаете огромному количеству функций.
источник
X86-64 ABI используется Linux определяет «большую» модель специально , чтобы избежать таких ограничений по размеру, который включает в себя 64-битные типы перемещения для ПРТА и PLT. (См. Таблицу в разделе 4.4.2 и последовательность инструкций в разделе 3.5.5, которые показывают, как они используются.)
Поскольку ваши функции занимают 2,8 ГБ, вам не повезло, потому что gcc не поддерживает большие модели. Что вы можете сделать, так это реорганизовать свой код таким образом, чтобы вы могли разделить его на общие библиотеки, которые вы могли бы динамически связывать.
Если это невозможно, как кто-то предложил, вместо того, чтобы помещать ваши данные в код (компилировать и связывать его), поскольку он огромен, вы можете загрузить его во время выполнения (либо как обычный файл, либо вы можете его mmap).
РЕДАКТИРОВАТЬ
Похоже, что большая модель поддерживается gcc 4.6 (см. Эту страницу ). Вы можете попробовать это, но вышесказанное по-прежнему применимо к реорганизации вашего кода.
источник
С программой на этой стороне промахи в кэше для кода, скорее всего, превысят затраты на создание цикла во время выполнения. Я бы порекомендовал вам вернуться к своему генератору кода и создать некоторое компактное представление того, что он хочет оценить (то есть, которое может поместиться в D-кеш), а затем выполнить это с помощью интерпретатора в вашей программе. Вы также можете посмотреть, можете ли вы исключить меньшие ядра, которые все еще имеют значительное количество операций, а затем использовать их как «инструкции» в интерпретируемом коде.
источник
Ошибка возникает из-за того, что у вас слишком много КОДА, а не данных! На это указывает, например
__libc_csu_fini
, ссылка (которая является функцией),_start
и перемещение усекается, чтобы соответствовать. Это означает, что_start
(истинная точка входа программы) пытается вызвать эту функцию через 32-битное смещение SIGNED, которое имеет диапазон только 2 ГБ. Поскольку общий объем вашего объектного кода составляет ~ 2,8 ГБ, факты подтверждаются.Если бы вы могли перепроектировать свои структуры данных, большую часть вашего кода можно было бы «сжать», переписав огромные выражения в виде простых циклов.
Кроме того, вы можете выполнять вычисления
csc[]
в другой программе, сохранять результаты в файле и просто загружать их при необходимости.источник
csc[]
приходится вычислять очень часто, и я бы хотел избежать дискового ввода-вывода.func1
выше, что - то вроде:for (int i = 0; i < N; ++i) expr += constants[i].*s.x14*s.x15*csc[49300 + i];
.Я думаю, все согласны, что должен быть другой способ делать то, что вы хотите делать. Компиляция сотен мегабайт (гигабайт?) Кода, связывание его с исполняемым файлом размером в несколько гигабайт и его запуск звучит очень неэффективно.
Если я правильно понимаю вашу проблему, вы используете какой-то генератор кода, G, для генерации набора функций,
func1...N
которые принимают наcsc1...M
входе набор карт . Что вы хотите сделать, так это вычислитьcsc1...M
и запустить цикл 1000000 раз для разных входных данных и каждый раз найтиs = func1 + func2 + ... + funcN
. Вы не указали, какfucn1...N
связаны сcsc1...M
.Если все это правда, кажется, что вы сможете перевернуть проблему с ног на голову иным способом, который потенциально может быть намного более управляемым и даже, возможно, быстрее (т.е. позволяя кешу вашей машины действительно функционировать).
Помимо практической проблемы размеров объектных файлов, ваша текущая программа будет неэффективной, поскольку она не локализует доступ к данным (слишком много огромных карт) и не имеет локализованного выполнения кода (слишком много очень длинных функций).
Как насчет разбиения вашей программы на 3 этапа: сборка на этапе 1
csc1...M
и их сохранение. Фаза 2 строит по очередиfunc
, запускает 1000000 раз с каждым вводом и сохраняет результаты. На этапе 3 найдите сумму результатов сохраненныхfunc1...N
результатов для каждого прогона из 1000000 раз. Преимущество этого решения в том, что его можно легко сделать параллельно на нескольких независимых машинах.Изменить: @bbtrb, не могли бы вы когда-нибудь сделать доступными один func и один csc? Они кажутся очень регулярными и сжимаемыми. Например, func1 кажется просто суммой выражений, каждое из которых состоит из 1 коэффициента, 2 индексов переменных в s и 1 индекса в csc. Так что это можно свести к красивому циклу. Если вы сделаете полные примеры доступными, я уверен, что можно найти способы сжать их в циклы, а не в длинные выражения.
источник
func
s зависят почти от всехcsc
s, и эти числа также должны быть вычислены 10 ^ 6 раз. 2. Входные данные будут получены от адаптивного интегратора Монте-Карло, что означает, что интегратор должен знать полный результат в каждой точке, чтобы иметь возможность уменьшить результирующую ошибку путем уточнения сетки вблизи точки, если это необходимо. 3. Большие выражения дляcsc
csc
каждую итерацию независимо от других? Если бы они были независимыми, вы все равно могли бы запускать каждый из них 10 ^ 6 раз и сохранять результаты. Однако, если между ними есть зависимости, возможно, вам нужно выяснить, какая из них связана с какой, что-то вроде графа зависимостей, а затем попытаться посмотреть, можно ли разбить его на несколько независимых подграфов. В общем, я думаю, что главное - разбить проблему на несколько независимых подзадач.Если я правильно прочитал ваши ошибки, то, что заставляет вас переносить лимит, является раздел инициализированных данных (если бы это был код, у вас было бы гораздо больше ошибок, ИМХО). У вас есть большие массивы глобальных данных? В таком случае я бы реструктурировал программу так, чтобы они распределялись динамически. Если данные инициализированы, я бы прочитал их из файла конфигурации.
Кстати, видя это:
Думаю, у вас другая проблема.
источник
Мне кажется, что код выполняет численное интегрирование с использованием какого-то метода адаптивной глубины. К сожалению, генератор кода (или, скорее, автор генератора кода) настолько глуп, что генерирует одну функцию для каждого патча, а не одну для каждого типа. патча. Таким образом, он произвел слишком много кода для компиляции, и даже если бы он мог быть скомпилирован, его выполнение было бы болезненным, потому что ничто никогда нигде не передается. (Можете ли вы представить себе боль, возникающую из-за необходимости загружать каждую страницу объектного кода с диска, потому что ничто не используется совместно, и поэтому ОС всегда является кандидатом на выселение. Не говоря уже о кешах инструкций, которые будут бесполезны.)
Исправление - перестать все разворачивать; для такого рода кода вы хотите максимизировать совместное использование поскольку накладные расходы на дополнительные инструкции для доступа к данным в более сложных шаблонах в любом случае будут поглощены стоимостью работы с (предположительно) большим базовым набором данных. Также возможно, что генератор кода даже сделает это по умолчанию, и что ученый увидел некоторые варианты развертывания (с примечанием, что они иногда улучшают скорость), включил их все сразу и теперь настаивает на том, чтобы этот беспорядок был принят. компьютером, вместо того, чтобы принимать реальные ограничения машины и использовать правильную числовую версию, созданную по умолчанию. Но если генератор кода этого не сделает, возьмите тот, который будет (или взломайте существующий код).
Итог : компиляция и компоновка 2,8 ГБ кода не работают, и их не следует заставлять работать. Найдите другой способ.
источник
Пара предложений: - Оптимизировать по размеру (-Os). Выполняйте встроенные вызовы функций, обычные вызовы функций. Включить пул строк.
Попробуйте разделить вещи на разные DLL (общие объекты, .so для Linux, .dylib для Mac OS X). Убедитесь, что их можно выгрузить. Затем реализуйте что-нибудь, чтобы загружать вещи по запросу и освобождать их, когда они не нужны.
Если нет, разделите свой код на разные исполняемые файлы и используйте что-нибудь для связи между ними (каналы, сокеты, даже запись / чтение в файл). Коряво, а какие есть варианты?
Абсолютно альтернатива: - Используйте динамический язык с JIT . Прямо у меня в голове - используйте LuaJIT - и переписывайте (регенерируйте?) Многие из этих выражений в Lua или других подобных языках и средах выполнения, которые позволяют собирать мусор.
LuaJIT довольно эффективен, иногда опережая C / C ++ в некоторых вещах, но часто очень близко (иногда может быть медленным из-за плохой сборки мусора). Убедитесь сами:
http://luajit.org/performance_x86.html
Загрузите
scimark2.lua
файл оттуда и сравните его с версией "C" (погуглите) - часто результаты очень близки.источник
Компоновщик пытается сгенерировать 32-битные смещения перемещения в двоичном файле, который каким-то образом превысил эти ограничения. Попробуйте уменьшить требования к адресному пространству основной программы.
Можете ли вы разделить часть / большую часть объектного кода на одну или несколько библиотек (также скомпилированных с помощью -fpic / -fPIC)? Затем сгенерируйте нестатический двоичный файл, который связывается с этими библиотеками. Библиотеки будут располагаться в дискретных блоках памяти, и ваши смещения перемещения будут динамическими / абсолютными (64-разрядные), а не относительными (32-разрядные).
источник
Для меня эти выражения очень похожи на чередующуюся серию. Я не знаю, как выглядит остальная часть кода, но не похоже, что вывести генерирующее выражение будет так сложно. Вероятно, оно того стоит и во время выполнения, особенно если у вас 2,8 ГБ развернутого кода 2 КБ.
источник
Это похоже на неправильный результат генерации кода, возможно, из-за символической алгебры и / или ручного развертывания. Хорошо известно, что символические манипуляции экспоненциально растут в глубине дерева выражений или вычислительного графа. Вполне вероятно, что здесь можно использовать автоматическое дифференцирование, которое сделало бы размер кода довольно маленьким, а также значительно ускорило бы выполнение.
источник