答案 0 :(得分: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 -...
正确?
如果你的所有函数都有类似的“格式”(将n个数乘以m次并添加结果 - 或类似的东西),那么我认为你可以这样做:
offsetof(ProcessVars, ds0)
数组+求值程序将表示与您的某个函数相同的逻辑,但只有求值程序才是代码。该数组是“数据”,可以在运行时生成,也可以保存在磁盘上,读取数据块或内存映射文件。
对于你在func1中的特定例子,想象一下如果你有权访问s
和csc
的基地址以及像常量表示的向量那样你将如何通过求值程序重写函数您需要添加到基地址的偏移量才能转到x14
,ds8
和csc[51370]
您需要创建一种新形式的“数据”,描述如何处理传递给大量函数的实际数据。
答案 1 :(得分:45)
x86-64 ABI used by Linux定义了一个“大型模型”,专门用于避免这种大小限制,其中包括GOT和PLT的64位重定位类型。 (参见4.4.2节中的表格,以及3.5.5中的指令序列,说明如何使用它们。)
由于你的功能占用2.8 GB,你运气不好,因为gcc不支持大型号。你可以做的是重新组织你的代码,这样你就可以把它分成你动态链接的共享库。
如果那是不可能的,就像有人建议的那样,而不是将数据放入代码(编译和链接它),因为它很大,你可以在运行时加载它(作为普通文件,或者你可以mmap它)。
修改强>
似乎gcc 4.6支持大型模型(参见this page)。您可以尝试这样做,但上述内容仍适用于重新组织代码。
答案 2 :(得分:37)
使用该方程序,代码的缓存未命中很可能超过运行时循环的成本。我建议你回到你的代码生成器,让它生成一些 compact 表示它想要评估的东西(即,一个可能适合D-cache),然后用解释器执行它在你的程序中。您还可以看看是否可以分解仍然具有大量操作的较小内核,然后将其用作解释代码中的“指令”。
答案 3 :(得分:21)
发生错误是因为你有太多的CODE,而不是数据!这由例如从__libc_csu_fini
引用的_start
(这是一个函数)表示,并且重定位被截断以适合。这意味着_start
(程序的真正入口点)试图通过SIGNED 32位偏移调用该函数,该偏移的范围仅为2 GB。由于您的目标代码总量约为2.8 GB,因此可以查看事实。
如果您可以重新设计数据结构,可以通过将大表达式重写为简单循环来“压缩”大部分代码。
此外,您可以在其他程序中计算csc[]
,将结果存储在文件中,并在必要时加载它们。
答案 4 :(得分:15)
我认为每个人都同意应该有不同的方式去做你想做的事情。编译数百兆字节(千兆字节?)的代码,将其链接到一个数千兆字节大小的可执行文件并运行它听起来非常低效。
如果我正确理解你的问题,你可以使用某种代码生成器G来生成一堆函数func1...N
,它们将一堆映射csc1...M
作为输入。您要做的是计算csc1...M
,并为不同的输入运行1,000,000次循环,每次都找到s = func1 + func2 + ... + funcN
。您没有指定fucn1...N
与csc1...M
的关联方式。
如果一切正确,那么您似乎应该能够以不同的方式解决问题,这可能更易于管理,甚至可能更快(即让您的机器的缓存实际运行)。
除了目标文件大小的实际问题之外,您当前的程序效率不高,因为它不会本地化对数据的访问(太多巨大的地图)并且没有本地化的代码执行(太多非常长的函数)。 / p>
如何将程序分为3阶段:阶段1构建csc1...M
并存储它们。阶段2一次构建一个func
,每次输入运行1,000,000次并存储结果。第3阶段查找每次运行1,000,000次的存储func1...N
结果的结果总和。关于这个解决方案的好处是它可以很容易地在几台独立的机器上并行。
编辑:@bbtrb,你可以制作一个功能和一个csc可用吗?它们似乎是高度规则和可压缩的。例如,func1似乎只是表达式的总和,每个表达式由1个系数组成,2个索引指向s中的变量,1个索引指向csc。所以它可以简化为一个很好的循环。如果您提供完整的示例,我确信可以找到将它们压缩成循环而不是长表达式的方法。
答案 5 :(得分:5)
如果我正确地阅读了你的错误,那么让你超越限制的是初始化数据部分(如果是代码,你会有更多的错误恕我直言)。你有大量的全球数据吗?如果是这种情况,我会重新构建程序,以便动态分配它们。如果数据已初始化,我会从配置文件中读取它。
BTW看到了这个:
(。text + 0x20):对'main'的未定义引用
我认为你还有另一个问题。
答案 6 :(得分:3)
一些建议: - 优化尺寸(-Os)。进行内联函数调用,正常函数调用。启用字符串池。
尝试将事物拆分为不同的DLL(共享对象,Linux为.so,Mac OS X为.dylib)。确保可以卸载它们。然后实现一些按需加载的东西,并在不需要时释放它们。
如果没有,将代码拆分为不同的可执行文件,并使用某些东西在它们之间进行通信(管道,套接字,甚至写入/读取文件)。笨拙,但你有什么选择?
完全替代方案: - 使用JIT的动态语言。就在我的头顶 - 使用LuaJIT - 并在Lua中重写(重新生成?)很多这些表达式,或其他允许代码被垃圾收集的运行时和。
LuaJIT非常高效,有时会为某些事情击败C / C ++,但往往非常接近(有时可能因为垃圾收集不良而很慢)。自己检查一下:
http://luajit.org/performance_x86.html
从那里下载scimark2.lua
文件,并将其与“C”版本(谷歌版)进行比较 - 通常结果非常接近。
答案 7 :(得分:3)
在我看来,代码正在使用某种自适应深度方法进行数值积分。不幸的是,代码生成器(或者更确切地说是代码生成器的作者)是如此愚蠢,因为每个补丁生成一个函数,而不是每个类型补丁生成一个函数。因此,它产生了太多的代码来编译,即使它可以被编译,它的执行也会很痛苦,因为任何地方都没有任何共享。 (你能想象由于必须从磁盘加载每页目标代码而导致的痛苦,因为没有任何东西可以共享,所以它总是操作系统的候选者。更不用说指令缓存,这将是无用的。)
解决方法是停止展开所有内容;对于这类代码,您希望最大化共享,因为更复杂模式中访问数据的额外指令的开销将被处理(可能)大型底层数据集的成本所吸收。默认情况下,代码生成器甚至可以执行此操作,并且科学家看到了一些展开的选项(注意这些选项有时可以提高速度)并立即将它们全部打开并且现在坚持认为这样的结果会被接受通过计算机,而不是接受机器的实际限制并使用默认生成的数字正确版本。但是如果代码生成器不会这样做,那就得到一个(或者破解现有代码)。
底线:编译和链接2.8GB的代码不起作用,不应强制使用。 找另一种方式。
答案 8 :(得分:2)
这些表达看起来很像我的交替系列。我不知道代码的其余部分是什么样的,但似乎并不是很难导出生成表达式。它在执行时也许值得,特别是如果你有2.8 GB的2 KB展开代码。
答案 9 :(得分:2)
链接器正在尝试在二进制文件中生成32位重定位偏移量,这些偏移量已经超出了这些限制。尝试减少主程序的地址空间要求。
你能将一些/大部分目标代码分成一个或多个库(也用-fpic / -fPIC编译)吗?然后生成一个链接这些库的非静态二进制文件。这些库将存在于离散的内存块中,您的重定位偏移将是动态/绝对(64位)而不是相对(32位)。
答案 10 :(得分:1)
这看起来像代码生成出错的结果,可能是通过符号代数和/或手动展开。众所周知,符号操作在表达式树或计算图的深度中呈指数增长。这里可以使用自动区分,这将使代码大小非常小,并且还可以大大加快执行速度。