GCC编译错误> 2 GB的代码

我有大量的函数总共大约2.8 GB的目标代码(不幸的是,没有办法,科学计算…)

当我尝试链接它们,我得到(预期) relocation truncated to fit: R_X86_64_32S错误,我希望通过指定编译器标志-mcmodel=medium规避。 除了我所控制的链接之外,所有链接库都使用-fpic标志进行编译。

尽pipe如此,错误仍然存​​在,我假设我链接到的一些库不是用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[]是一个预先计算好的值的映射,它也是在单独的目标文件中进行评估的(每个目录文件也有几百个,大小约为1MB),其格式如下:

 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]并将结果总结起来。

关于这是一个相当特殊和不寻常的事实:是的。 当人们试图对粒子物理进行高精度计算时,这就是人们必须要面对的问题。

编辑2:我还应该补充说,x12,x13等并不是真正的常量。 它们被设置为特定值,所有这些函数都被运行并返回结果,然后select一组新的x12,x13等来产生下一个值。 这要做10 ^ 5到10 ^ 6次…

编辑3:谢谢你的build议和讨论到目前为止…我会尝试在代码生成滚动循环不知何故,不知道如何完全,说实话,但这是最好的select。

顺便说一句,我没有试图躲在“这是科学计算 – 没有办法优化”。 只是这个代码的基础是从一个“黑匣子”出来的,我没有真正的访问权限,而且整个事情都用简单的例子很好地工作,而且我主要为真实情况感到不知所措世界应用…

编辑4:所以,我设法通过在计算机代数系统( Mathematica )中简化expression式来减lesscsc定义的代码大小。 我现在也看到了一些方法,在生成代码之前通过应用一些其他技巧来降低它(这将使这部分降低到大约100 MB),我希望这个想法能够工作。

现在与你的答案有关:我试图在func重新使用循环,CAS对此没有太大的帮助,但是我已经有了一些想法。 例如,通过像x12, x13,...这样的variables对expression式进行sorting,使用Pythonparsingcsc ,并生成相互关联的表。 那么我至less可以生成这些部分作为循环。 由于这似乎是迄今为止最好的解决scheme,所以我将其标记为最佳答案。

不过,我也要赞扬VJO。 GCC 4.6的确效果更好,生成的代码更小,速度更快。 按照原样使用大型模型。 所以在技术上这是正确的答案,但改变整个概念是一个更好的方法。

谢谢大家的build议和帮助。 如果有人有兴趣,我会尽快公布最后的结果。

备注:只是一些其他答案的评论:我试图运行的代码不是源于简单的函数/algorithm和愚蠢的不必要的展开的扩展。 实际上发生的事情是,我们开始的东西是相当复杂的math对象,并将它们带到数字可计算的forms生成这些expression式。 问题实际上在于潜在的物理理论。 中间expression式的复杂性是有规律地缩放的,这是众所周知的,但是当把所有这些东西结合到一个物理可测量的东西 – 一个可观察的东西上时,它只归结为构成expression式基础的一小部分非常小的函数。 (在这方面肯定有一些“错误”的东西, 只有一般的和唯一可以被称为“微扰理论”的ansatz)我们试图把这个ansatz带到另一个层面,这在分析上是不可行的,而且所需function的基础是未知。 所以我们试图像这样蛮力。 不是最好的方法,但希望能帮助我们理解最终的物理现象。

最后编辑:感谢所有的build议,我已经设法大大减less代码的大小,使用Mathematica和func的代码生成器的修改有点沿着顶部的答案:)

我已经使用Mathematica简化了csc函数,将其降至92 MB。 这是不可或缺的部分。 第一次尝试是永久的,但经过一些优化之后,现在在单个CPU上运行大约10分钟。

func的影响是显着的:整个代码大小约为9 MB,所以现在的代码总计在100 MB的范围内。 现在开启优化并且执行速度非常快。

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

所以,你已经有一个产生这个文本的程序:

 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次并添加结果 – 或类似的东西),那么我认为你可以这样做:

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

数组+评估器将代表与您的一个函数相同的逻辑,但只有评估者将成为代码。 该数组是“数据”,可以在运行时生成或保存在磁盘上,读取我的块或内存映射文件。

对于func1中的特定示例,想象一下,如果您可以访问scsc的基地址,并且还可以像常量的表示和需要添加到基址的偏移量那样通过评估程序重写函数到x14ds8csc[51370]

您需要创build一种新的“数据”forms,以描述如何处理传递给您的大量function的实际数据。

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

由于你的函数占用了2.8GB,所以你运气不好,因为gcc不支持大型模型。 你可以做的是以这样一种方式重组你的代码,这样你就可以将它分割成你将dynamic链接的共享库。

如果这是不可能的,正如有人build议,而不是把你的数据代码(编译和链接),因为它是巨大的,你可以加载它在运行时(作为一个普通的文件,或者你可以mmap它)。

编辑

好像大型模型是由gcc 4.6支持的(参见本页 )。 你可以尝试一下,但是以上内容仍然适用于重组你的代码。

有了这方面的程序,caching未命中代码很可能会超过在运行时循环的成本。 我build议你回到你的代码生成器,让它生成一些简单的表示,以便对它进行评估(即,可能适用于D-cache的代码),然后在程序中使用解释器执行它。 你也可以看看你是否可以分解出仍然有大量操作的小内核,然后在解释的代码中使用这些内容作为“指令”。

发生错误是因为你有太多的代码,而不是数据! 这由__libc_csu_fini (这是一个函数)被_start引用,重定位被截断以适合。 这意味着_start (程序的真正入口点)试图通过SIGNED 32位偏移量来调用该函数,该偏移量只有2 GB的范围。 由于您的目标代码的总数是〜2.8 GB,事实检查。

如果你可以重新devise你的数据结构,那么你的大部分代码都可以通过将巨大的expression式重写为简单循环来“压缩”。

另外,您可以在另一个程序中计算csc[] ,将结果存储在一个文件中,并在必要时加载它们。

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

如果我正确地理解了你的问题,你可以使用某种代码生成器G来生成一堆函数func1...N ,这些函数需要一堆地图csc1...M作为input。 你想要做的是计算csc1...M ,并为不同的input运行一百万次循环,每次finds = func1 + func2 + ... + funcN 。 你没有指定fucn1...N如何与csc1...M相关。

如果这一切都是真的,那么你应该能够以不同的方式来解决问题,这可能会更容易pipe理,甚至可能更快(即让你的机器的caching实际上起作用)。

除了目标文件大小的实际问题之外,由于目前的程序没有对数据进行本地化访问(太多庞大的地图),也没有本地化的代码执行(太长的函数),所以目前的程序效率不高。

如何将您的程序分为三个阶段:阶段1构buildcsc1...M并存储它们。 阶段2一次构build一个func ,每次input运行100万次并存储结果。 阶段3找出存储的func1...N结果的总和,每次运行1,000,000次。 这个解决scheme的好处在于它可以很容易地在几台独立的机器上并行运行。

编辑:@bbtrb,你可以做一个func和一个csc可用somehwere? 他们似乎是非常规则和可压缩的。 例如,func1似乎只是一个expression式的总和,每个expression式由1个系数,2个索引到s中的variables和1个到csc中的索引组成。 所以它可以减less到一个很好的循环。 如果您提供完整的示例,我相信可以find方法将它们压缩成循环而不是长expression式。

如果我正确地读了你的错误,是什么让你超过极限是初始化数据部分(如果是代码,你将有更多的错误恕我直言)。 你有大量的全球数据? 如果是这样的话,我会重组程序,以便dynamic分配它们。 如果数据被初始化,我会从configuration文件中读取数据。

顺便说一句:

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

我想你还有一个问题。

在我看来,代码是使用某种自适应深度方法进行数值积分的。 不幸的是,代码生成器(或者代码生成器的作者)是如此愚蠢 ,以至于为每个补丁生成一个函数,而不是每个补丁types生成一个函数。 因此,它产生了太多的代码进行编译,即使可以编译,它的执行也会很痛苦,因为从来没有任何东西被共享过。 (你能想象不得不从磁盘加载目标代码的每一页,因为没有任何东西是共享的,所以它一直是操作系统驱逐的候选者,更不用说指令caching了,这将是没有用的。

解决办法是停止展开一切; 对于这种types的代码,您希望最大限度地共享,因为额外指令访问更复杂模式中的数据的开销将被处理大概底层数据集的成本所吸收。 代码生成器甚至可能会默认这样做,并且科学家看到了一些展开选项(注意这些选项有时可以提高速度),并立即将它们全部打开,现在坚持认为这样的混乱是可以接受的而不是接受机器的真正限制,并使用默认生成的数字正确的版本。 但是,如果代码生成器不能做到这一点,得到一个将(或破坏现有的代码)。

底线:编译和链接2.8GB的代码不起作用,不应该被迫工作。 寻找另一种方式。

几个build议: – 优化大小(-Os)。 进行内联函数调用,正常函数调用。 启用string池。

尝试将其分成不同的DLL(共享对象,.so for Linux,Mac OS X的.dylib)。 确保它们可以被卸载。 然后实现一些东西按需加载,并在不需要的时候释放它们。

如果没有的话,把你的代码分解成不同的可执行文件,然后用它们之间的通信(pipe道,套接字,甚至写/读文件)。 笨拙,但你有什么select?

完全替代: – 使用dynamic语言与JIT 。 在我头上 – 使用LuaJIT – 并在Lua中重写(重新生成)很多这些expression式,或者其他允许代码被垃圾收集的语言和运行时。

LuaJIT非常高效,有时甚至会跳过C / C ++来处理某些事情,但通常非常接近(有时可能由于糟糕的垃圾收集而变慢)。 检查你自己:

http://luajit.org/performance_x86.html

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

链接器试图在二进制文件中产生32位的重定位偏移量,这个偏移量超出了这些限制。 尽量减less主程序的地址空间要求。

你能把一些/大部分目标代码分成一个或多个库(也用-fpic / -fPIC编译)? 然后生成一个链接到这些库的非静态二进制文件。 这些库将存储在不连续的内存块中,而您的重定位偏移将是dynamic/绝对(64位)而不是相对(32位)。

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

这看起来像代码生成出错的结果,也许是由符号代数和/或手动展开。 众所周知,符号操作在expression式树或计算图的深度中呈指数级增长。 在这里可能会使用自动分化,这将使代码尺寸变得非常小,同时也大大加快了执行速度。