GCC编译错误,代码> 2 GB


108

我有大量的功能,总计约2.8 GB的目标代码(不幸的是,科学计算无法实现...)

当我尝试链接它们时,我遇到了(预期的)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 MB),如下所示:

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 MB)中进行评估:

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 MB),我希望这个想法行得通。

现在与您的答案有关:我正在尝试在funcs中重新循环,CAS并没有多大帮助,但是我已经有了一些想法。例如,使用变量如排序表达式,用Python x12, x13,...解析cscs并生成将它们相互关联的表。然后,我至少可以将这些部分生成为循环。由于这似乎是迄今为止最好的解决方案,因此我将其标记为最佳答案。

但是,我还要感谢VJo。GCC 4.6确实工作好,生成较小的代码和更快。使用大型模型按原样运行代码。因此,从技术上讲,这是正确的答案,但是更改整个概念是一种更好的方法。

谢谢大家的建议和帮助。如果有人感兴趣,我将在准备好后立即发布最终结果。

备注:对其他答案的一些评论:我尝试运行的代码并非源自简单函数/算法的扩展以及愚蠢的不必要的展开。实际发生的是,我们开始的工作是相当复杂的数学对象,并将它们转换为可数字计算的形式会生成这些表达式。问题实际上出在潜在的物理理论上。众所周知,中间表达式的复杂度会按比例缩放,但是当将所有这些东西组合为可物理测量的东西(可观察到的东西)时,它只能归结为构成表达式基础的少数几个很小的函数。(在这方面,在一般情况下肯定存在“错误”,并且只能使用ansatz,被称为“扰动理论”)我们试图将这种ansatz提升到一个新的水平,这在分析上已经不再可行,并且所需功能的基础未知。因此,我们尝试像这样暴力破解它。并不是最好的方法,但希望最终可以帮助我们更好地理解手边的物理...

最后编辑: 由于您的所有建议,我设法使用Mathematica并针对funcs 修改了代码生成器,从而大大减少了代码大小:

csc使用Mathematica 简化了功能,将其减小到92 MB。这是不可还原的部分。第一次尝试花了很多时间,但是经过一些优化后,现在可以在单个CPU上运行大约10分钟。

funcs 的影响是巨大的:它们的整个代码大小降低到大约9 MB,因此现在的代码总数为100 MB。现在,打开优化很有意义,并且执行速度非常快。

再次感谢大家的建议,我学到了很多东西。


17
如果有那么多数据,则应mmap在运行时将其从源文件中移出,而自己从外部二进制文件中移出。
R .. GitHub停止帮助ICE,

3
您能举一个(或两个)这些功能的例子吗?这看起来真的很奇怪。您也可以使用dl *函数动态加载这些函数。
PatrickSchlüter

7
@bbtrb:我的第一个直觉类似于R ..,听起来像是一个设计问题。诚然,我不知道科学计算界有什么共同点,但是我从未听说有人试图链接2.8GB目标文件或任何接近它的文件,而且我不确定GCC是否会真正支持它。坦白说,我希望任何大小的代码blob都是纯意大利面。
尼古拉斯·奈特

46
绝对不可能为该问题的最佳解决方案涉及2gb的目标文件。
David Heffernan

35
不要将您的数据放入代码中
David Heffernan

Answers:


53

因此,您已经有一个生成此文本的程序:

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

对?

如果您所有的函数都具有相似的“格式”(m次乘以n个数字,然后将结果相加-或类似的结果),那么我认为您可以这样做:

  • 将生成器程序更改为输出偏移量而不是字符串(即,它将生成的字符串而不是字符串“ s.ds0”) offsetof(ProcessVars, ds0)
  • 创建这样的偏移量数组
  • 编写一个评估器,接受上面的数组和结构指针的基地址并产生结果

array + evaluator表示与您的函数之一相同的逻辑,但只有评估器是代码。该数组是“数据”,可以在运行时生成,也可以保存在磁盘上并读取i块或使用内存映射文件。

对于FUNC1您的具体的例子想象你将如何通过评估重写功能,如果你有机会访问的基地址scsc和也喜欢常量,你需要添加到基址去的偏移表示一个向量x14ds8csc[51370]

您需要创建一种新的“数据”形式,该形式将描述如何处理传递给大量函数的实际数据。


45

Linux使用x86-64 ABI专门定义了一个“大型模型”以避免这种大小限制,其中包括GOT和PLT的64位重定位类型。(请参阅第4.4.2节中的表以及第3.5.5节中的指令序列,其中显示了如何使用它们。)

由于您的功能占用了2.8 GB的空间,因此您不走运,因为gcc不支持大型模型。您可以做的是,以一种允许您将其拆分为动态链接的共享库的方式来重组代码。

如某人所建议的那样,如果这不可能,则不必将数据放入代码中(进行编译和链接),因为它庞大,因此可以在运行时加载(作为普通文件,也可以将其映射)。

编辑

好像大型模型受gcc 4.6支持(请参阅本页)。您可以尝试这样做,但是以上内容仍然适用于重组代码。


因此,您的意思是,当我将目标文件分组到几个小型共享库中时,我将克服这些限制吗?
bbtrb 2011年

3
@bbtrb对。但是我仍然会寻找实现您的功能的另一种方法。我敢打赌,你的编译需要永远
BЈовић

18
WTF?此代码必须由某些脚本生成。没有人手工编写兆字节的代码!生成代码的相同逻辑也可以用于运行计算。
zvrba 2011年

6
我强烈建议您尝试使用gcc 4.6,它很可能为此程序产生比gcc 4.1更好的代码。它甚至可以将整个文件压缩到2GB,而无需您做任何聪明的事情,从而消除了问题(尝试将-Os,-fwhole-program和-flto组合在一起,使用此代码量,针对大小进行优化优化速度)。但是,如果这样做还不能解决问题,您还应该意识到,要使大型模型正常工作,您将不得不在大型模型(crt * .o,libc_nonshared.a)中重建至少部分C库。 ,以及libpthread_nonshared.a)。
zwol 2011年

1
@bdonlan也可以进行静态链接。
zvrba 2011年

37

使用该方面的程序,代码的高速缓存未命中很有可能超过运行时循环的成本。我建议您回到代码生成器,让它为要评估的内容生成一个紧凑的表示形式(即,一个可能适合D-cache的形式),然后在程序中使用解释器执行它。您还可以看到是否可以排除仍然具有大量操作的较小内核,然后将其用作解释代码中的“指令”。


21

发生此错误是因为您有太多的CODE,而不是数据!例如__libc_csu_fini,这是通过引用(是一个函数)来指示的,_start并且重定位被截断以适合。这意味着_start(程序的真正入口点)正在尝试通过带符号的32位偏移量(仅2 GB范围)来调用该函数。由于您的目标代码总量约为2.8 GB,因此请查看事实。

如果可以重新设计数据结构,则可以通过将巨大的表达式重写为简单循环来“压缩”很多代码。

同样,您可以csc[]在其他程序中进行计算,将结果存储在文件中,并在必要时加载它们。


您能否提供一个示例,说明如何使用简单的循环重写函数?我没有完全跟着你。csc[]必须经常计算,我想避免磁盘I / O。
bbtrb 2011年

4
@bbtr:例如,func1以上所述:for (int i = 0; i < N; ++i) expr += constants[i].*s.x14*s.x15*csc[49300 + i];
HighCommander4 2011年

@ HighCommander4:绝对,我同意。如何自动生成类似内容只是我的头等大事。也许有一个存储索引的单独数组...
bbtrb 2011年

2
@bbtrb:由于没有任何人敢于编写任何代码来手工生成2.8GB的目标代码,特别是在使用此类非助记符符号名称的情况下,因此必须使用代码生成器。一起工作。
多纳研究员

15

我认为每个人都同意应该有一种不同的方式来做自己想做的事情。编译数百兆字节(GB)的代码,将其链接到一个数千兆字节大小的可执行文件中,然后运行它听起来效率很低。

如果我正确理解了您的问题,则可以使用某种代码生成器G生成一堆函数func1...N,这些函数将一堆地图csc1...M作为输入。您要做的是计算csc1...M,并为不同的输入和每次查找运行1,000,000次循环s = func1 + func2 + ... + funcN。您没有指定fucn1...N与的关系csc1...M

如果一切正确,那么看来您应该可以以不同的方式解决问题,这可能会更容易管理,甚至可能更快(例如,使计算机的缓存实际运行)。

除了目标文件大小的实际问题外,您当前的程序效率不高,因为它没有本地化对数据的访问(太多的巨大映射)并且没有本地化的代码执行(太多的很长的功能)。

如何将程序分为三个阶段:第一阶段构建csc1...M并存储它们。第2阶段func一次构建一个,对每个输入运行1,000,000次并存储结果。第3阶段查找func1...N每次用完1,000,000次后存储的结果的总和。该解决方案的优点在于,可以轻松地使其在多台独立的计算机上并行运行。

编辑:@bbtrb,您可以在某个位置提供一个func和一个csc吗?它们似乎是高度规则且可压缩的。例如,func1似乎只是表达式的总和,每个表达式由1个系数,s中的变量的2个索引和csc中的1个索引组成。因此,它可以简化为一个不错的循环。如果您提供完整的示例,我相信可以找到将它们压缩为循环而不是长表达式的方法。


是的,您的理解是正确的:)尽管您的建议存在几个问题:1.最差的funcs几乎取决于所有cscs,这些数字也必须计算10 ^ 6次。2.输入将从自适应蒙特卡洛积分器获得,这意味着积分器必须知道每个点的完整结果,以便能够根据需要通过在点附近细化网格来减少结果误差。3. csc持久的大表情...
bbtrb 2011年

1
因此,这是否意味着您不能csc在每个迭代中独立计算每个迭代?如果它们是独立的,则您仍然可以每次运行10 ^ 6次并存储结果。但是,如果它们之间存在依赖关系,也许您需要找出与哪个依赖关系相关的东西(如依赖关系图),然后尝试查看是否可以将其分解为多个独立的子图。总而言之,我认为关键是将问题分解为多个独立的子问题。
AlefSin 2011年

5

如果我正确地读取了错误,那么使您超出限制的是初始化的数据部分(如果是代码,恕我直言,您将会遇到更多错误)。您是否拥有大量的全球数据?如果是这种情况,我将对程序进行重组,以便动态分配它们。如果数据已初始化,我将从配置文件中读取它。

顺便说一句,看到这个:

(.text + 0x20):对“ main”的未定义引用

我认为您还有另一个问题。


1
是的,您是对的,愚蠢的错误,但是并不能解决其他错误。
bbtrb 2011年

3

在我看来,代码正在使用某种自适应深度方法进行数值积分。不幸的是,代码生成器(或者更确切地说,码发生器的作者)是如此以产生每一个补丁函数,而不是每一个类型贴剂。这样,它产生了太多的代码,无法编译,即使可以编译,其执行也很痛苦,因为任何地方都没有共享。(您能想象一下由于没有共享任何东西而不得不从磁盘加载目标代码的每一页所导致的痛苦,因为它永远都是OS驱逐的候选对象。更不用说指令缓存了,这将是无用的。)

解决方法是停止展开所有内容。对于这种类型的代码,您希望最大程度地共享,因为无论如何,处理(可能是)大型基础数据集的成本将吸收额外指令以更复杂的模式访问数据的开销。代码生成器甚至有可能默认情况下也会这样做,并且科学家看到了一些展开的选项(注意有时会提高速度)并立即将它们全部打开,现在坚持认为这样产生的混乱是可以接受的而不是接受计算机的实际限制,而是使用默认情况下生成的数字正确版本。但是,如果代码生成器无法做到这一点,请选择一个可以生成的代码(或破解现有代码)。

最重要的是:编译和链接2.8GB的代码不起作用,也不应该被迫起作用。寻找另一种方式。


3

一些建议:-优化大小(-Os)。进行内联函数调用,普通函数调用。启用字符串池。

尝试将事物拆分为不同的DLL(共享对象,对于Linux为.so,对于Mac OS X为.dylib)。确保可以卸载它们。然后实施一些东西来按需加载东西,并在不需要时释放它们。

如果没有,请将您的代码分成不同的可执行文件,并在它们之间使用某种方式进行通信(管道,套接字,甚至写入/读取文件)。笨拙,但是您有什么选择?

完全可以选择:-在JIT中使用动态语言。在我的头上-使用LuaJIT-并用Lua或其他允许代码被垃圾回收的其他语言和运行时重写(重新生成?)很多这些表达式。

LuaJIT非常高效,有时在某些方面击败C / C ++,但通常非常接近(由于那里垃圾收集不佳,有时可能很慢)。检查一下自己:

http://luajit.org/performance_x86.html

scimark2.lua从那里下载文件,然后将其与“ C”版本进行比较(谷歌下载)-通常结果非常接近。


2

链接器正在尝试在某种程度上超出这些限制的二进制文件中生成32位重定位偏移量。尝试减少主程序的地址空间要求。

您能否将部分/大部分目标代码拆分为一个或多个库(也可以使用-fpic / -fPIC编译)?然后生成链接到这些库的非静态二进制文件。这些库将驻留在离散的内存块中,并且您的重定位偏移量将是动态/绝对(64位)而不是相对(32位)。


2

在我看来,这些表情非常像一个交替的系列。我不知道其余的代码是什么样的,但是似乎很难派生生成的表达式。在执行时也可能值得,尤其是如果您有2.8 GB的2 KB展开代码。


1

这看起来好像是代码生成的结果出错了,可能是由于符号代数和/或手动展开。众所周知,符号操作会在表达式树或计算图的深度呈指数增长。在这里可能会使用自动微分,这将使代码大小非常小,并显着加快执行速度。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.