如何修复GCC编译超过2GB代码时的编译错误?

110

我有大量的函数,总共约2.8GB的目标代码(不幸的是,科学计算中无法避免...)

当我尝试链接它们时,我会得到(预期的)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[]是预先计算的值映射,也在以下形式的单独对象文件中进行评估(再次有数百个,每个大小约为~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等并不是真正的常数。它们被设定为特定的值,运行所有这些函数并返回结果,然后选择一个新的x12、x13等来产生下一个值。而这必须重复105到106次……

编辑3:

感谢至今为止提出的建议和讨论……我会设法在代码生成过程中将循环合并起来,但是说实话,我不确定该如何做到这一点,但这是最好的办法。

顺便说一句,我没有试图掩饰“这是科学计算——无法优化”的事实。
只是因为这个代码的基础是从一个“黑盒子”中得出的,我并没有真正的访问权限,而且整个过程在简单的例子中表现良好,我主要是感到在实际应用中会发生什么而感到不知所措……

编辑4:

所以,我通过简化计算机代数系统(Mathematica)中的表达式,成功将csc定义的代码大小减少了四分之一左右。现在我看到了通过在生成代码之前应用其他技巧来将其减少一个数量级左右的方法(这将使这部分代码降至约100 MB),我希望这个想法能够奏效。

现在关于你的答案:

我正在尝试将循环重新合并到func中,其中计算机代数系统并没有什么帮助,但我已经有了一些想法。例如,按变量(如x12、x13等)对表达式进行排序,使用Python解析csc并生成将它们彼此相关联的表格。然后我至少可以将这些部分生成为循环。由于这似乎是目前最好的解决方案,因此我将其标记为最佳答案。

但是,我也要赞扬VJo。 GCC 4.6确实工作得更好,产生的代码更小,速度更快。使用大型模型可以直接运行代码。因此,在技术上,这是正确的答案,但改变整个概念是更好的方法。

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

备注:

只是对其他答案的一些评论:我试图运行的代码并不源于简单函数/算法的扩展和愚蠢无用的展开。实际上,我们开始的东西是相当复杂的数学对象,将它们带到一个可以计算的形式会生成这些表达式。问题实际上在于基础物理理论。中间表达式的复杂度呈阶乘增长,这是众所周知的,但当将所有这些东西组合成某个物理可测量的东西(即可观测量)时,它最终只归结为少量非常小的函数,这些函数构成了表达式的基础。(这里有一个“错误”,就是唯一可用的ansatz——微扰理论——存在问题)我们试图将这个假设带到另一个水平,这在解析上不再可行,需要的函数基础也不清楚。所以我们试图像这样 brute-force 它。这不是最好的方法,但希望这种方法能最终帮助我们理解手头的物理问题...

最后编辑:

感谢您所有的建议,我使用 Mathematica 和对 func 的代码生成器进行了修改,成功地将代码大小大大简化,这与顶部答案的思路有些类似 :)

我用 Mathematica 简化了 csc 函数,将其缩小到了 92 MB。这是不可约的部分。最初的尝试花费了很长时间,但经过一些优化,现在在单个 CPU 上大约需要 10 分钟。

< p > 对于func而言,这种效果是惊人的:它们的整个代码大小缩小到了约9 MB左右,因此现在的代码总量在100 MB范围内。现在开启优化也有意义,并且执行速度非常快。

再次感谢您的建议,我学到了很多。


18
如果你有那么多数据,应该将其从源文件中移出,而是在运行时从外部二进制文件自己使用 mmap 映射。 - R.. GitHub STOP HELPING ICE
7
我的第一反应和R..类似,这听起来像是一个设计问题。诚然,我不知道在科学计算领域中什么是常见的,但我从未听说过有人尝试链接一个2.8GB的目标文件,或者任何接近它的东西,而且我不确定GCC是否真正支持它。坦白地说,我期望那样大小的代码块是纯粹的“意大利面条式代码”。 - Nicholas Knight
46
最佳解决方案绝对不可能涉及2GB的目标文件。 - David Heffernan
35
不要将您的数据放在代码中。 - David Heffernan
4
“高精度计算”用双精度?嗯?为什么不像其他人一样从“数据”文件中加载你的数据呢?(注意,“data”在这里指的是同一个东西,翻译时需要改变词序) - houbysoft
显示剩余32条评论
11个回答

1

这看起来像是代码生成出了问题,可能是由于符号代数和/或手动展开引起的。众所周知,符号操作在表达式树或计算图深度上呈指数级增长。很可能可以在这里使用自动微分,这将使代码大小非常小,同时大大加快执行速度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接