GCC 编译非常慢(大文件)
GCC compilation very slow (large file)
我正在尝试编译一个大型 C 文件(专门用于 MATLAB mexing)。 C 文件大约有 20 MB(如果您想使用它,可以使用 from the GCC bug tracker)。
下面是我正在 运行ning 的命令和屏幕输出。这已经 运行ning 几个小时了,如您所见,优化已被禁用 (-O0)。为什么这么慢?有什么办法可以让它更快吗?
(供参考:Ubuntu12.04(Precise Pangolin)64 位和 GCC 4.7.3)
/usr/bin/gcc -c -DMX_COMPAT_32 -D_GNU_SOURCE -DMATLAB_MEX_FILE -I"/usr/local/MATLAB/R2015a/extern/include" -I"/usr/local/MATLAB/R2015a/simulink/include" -ansi -fexceptions -fPIC -fno-omit-frame-pointer -pthread -O0 -DNDEBUG path/to/test4.c -o /tmp/mex_198714460457975_3922/test4.o -v
Using built-in specs.
COLLECT_GCC=/usr/bin/gcc
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.7.3-2ubuntu1~12.04' --with-bugurl=file:///usr/share/doc/gcc-4.7/README.Bugs --enable-languages=c,c++,go,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.7 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.7 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --with-system-zlib --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.7.3 (Ubuntu/Linaro 4.7.3-2ubuntu1~12.04)
COLLECT_GCC_OPTIONS='-c' '-D' 'MX_COMPAT_32' '-D' '_GNU_SOURCE' '-D' 'MATLAB_MEX_FILE' '-I' '/usr/local/MATLAB/R2015a/extern/include' '-I' '/usr/local/MATLAB/R2015a/simulink/include' '-ansi' '-fexceptions' '-fPIC' '-fno-omit-frame-pointer' '-pthread' '-O0' '-D' 'NDEBUG' '-o' '/tmp/mex_198714460457975_3922/test4.o' '-v' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1 -quiet -v -I /usr/local/MATLAB/R2015a/extern/include -I /usr/local/MATLAB/R2015a/simulink/include -imultilib . -imultiarch x86_64-linux-gnu -D_REENTRANT -D MX_COMPAT_32 -D _GNU_SOURCE -D MATLAB_MEX_FILE -D NDEBUG path/to/test4.c -quiet -dumpbase test4.c -mtune=generic -march=x86-64 -auxbase-strip /tmp/mex_198714460457975_3922/test4.o -O0 -ansi -version -fexceptions -fPIC -fno-omit-frame-pointer -fstack-protector -o /tmp/ccxDOA5f.s
GNU C (Ubuntu/Linaro 4.7.3-2ubuntu1~12.04) version 4.7.3 (x86_64-linux-gnu)
compiled by GNU C version 4.7.3, GMP version 5.0.2, MPFR version 3.1.0-p3, MPC version 0.9
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/local/MATLAB/R2015a/extern/include
/usr/local/MATLAB/R2015a/simulink/include
/usr/lib/gcc/x86_64-linux-gnu/4.7/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/4.7/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
GNU C (Ubuntu/Linaro 4.7.3-2ubuntu1~12.04) version 4.7.3 (x86_64-linux-gnu)
compiled by GNU C version 4.7.3, GMP version 5.0.2, MPFR version 3.1.0-p3, MPC version 0.9
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: c119948b394d79ea05b6b3986ab084cf
编辑:后续:我听从了 chqrlie 的建议,tcc 在 <5 秒内编译了我的函数(我只需要删除 -ansi 标志并将 "gcc" 变为 "tcc"),这非常了不起,真的。我只能想象 GCC 的复杂性。
然而,当尝试 mex 时,通常还需要 mex 的另一个命令。第二个命令通常是:
/usr/bin/gcc -pthread -Wl,--no-undefined -Wl,-rpath-link,/usr/local/MATLAB/R2015a/bin/glnxa64 -shared -O -Wl,--version-script,"/usr/local/MATLAB/R2015a/extern/lib/glnxa64/mexFunction.map" /tmp/mex_61853296369424_4031/test4.o -L"/usr/local/MATLAB/R2015a/bin/glnxa64" -lmx -lmex -lmat -lm -lstdc++ -o test4.mexa64
我不能 运行 使用 tcc,因为其中一些标志不兼容。如果我尝试使用 GCC 运行 这第二个编译步骤,我得到:
/usr/bin/ld: test4.o: relocation R_X86_64_PC32 against undefined symbol `mxGetPr' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status
编辑:解决方案似乎是 clang。 tcc 可以编译文件,但是 mexing 中第二步的参数与 tcc 的参数选项不兼容。 Clang 速度非常快,生成了一个漂亮的、小的、优化的文件。
几乎整个文件都是一个表达式,赋值给double f[24] = ...
。这将生成一个 gigantic 抽象语法树。如果只有专门的编译器才能有效地处理这个问题,我会感到很惊讶。
20 兆字节的文件本身可能没问题,但一个巨大的表达式可能是导致问题的原因。尝试作为初步步骤,将行拆分为 double f[24] = {0}
,然后分配 24 个 f[0] = ...; f[1] = ...
,看看会发生什么。最坏的情况是,您可以将 24 个赋值拆分为 24 个函数,每个函数都在自己的 .c
文件中,然后分别编译它们。这不会减少 AST 的大小,它只会重新组织它,但 GCC 在处理许多语句时可能更优化,这些语句加在一起会构成大量代码,而不是一个巨大的表达式。
最终的方法是以更优化的方式生成代码。例如,如果我搜索 s4*s5*s6
,我会得到 77,783 次匹配。这些 s[4-6]
变量不会改变。您应该生成一个临时变量 double _tmp1 = s4*s5*s6;
然后使用它而不是重复表达式。您刚刚从抽象语法树中删除了 311,132 个节点(假设 s4*s5*s6
是 5 个节点,_tmp1
是一个节点)。这就是 GCC 要做的更少的处理。这也应该生成 更快 代码(您不会重复相同的乘法 77,783 次)。
如果您以递归方式巧妙地执行此操作(例如 s4*s5*s6
--> _tmp1
、(c4*c6+s4*s5*s6)
--> (c4*c6+_tmp1)
--> _tmp2
, c5*s6*(c4*c6+s4*s5*s6)
--> c5*s6*_tmp2
-> _tmp3
, 等等...),您可能可以消除生成代码的大部分大小。
尝试 Fabrice Bellard 的微型 C 编译器 tcc
来自 http://tinycc.org:
chqrlie$ time tcc -c test4.c
real 0m1.336s
user 0m1.248s
sys 0m0.084s
chqrlie$ size test4.o
text data bss dec hex filename
38953877 3170632 0 42124509 282c4dd test4.o
是的,这是 1.336 秒 在一台非常基本的 PC 上!
当然我无法测试生成的可执行文件,但目标文件应该可以与您的程序和库的其余部分链接。
对于这个测试,我使用了一个虚拟版本的文件 mex.h
:
typedef struct mxArray mxArray;
double *mxGetPr(const mxArray*);
enum { mxREAL = 0 };
mxArray *mxCreateDoubleMatrix(int nx, int ny, int type);
gcc
还没编译完...
编辑:gcc
设法霸占了我的 Linux 盒子,我无法再连接到它了:(
经过测试,我发现Clang编译器编译大文件的问题似乎较少。尽管 Clang 在编译过程中消耗了近 GB 的内存,但它成功地将 OP 的源代码形式转换为 70 kB 的目标文件。这适用于我测试的所有优化级别。
如果打开优化,gcc 也能够快速编译此文件,并且不会消耗太多内存。这个 bug in gcc 来自 OPs 代码中的大表达式,这给寄存器分配器带来了巨大的负担。启用优化后,编译器执行称为 公共子表达式消除 的优化,它能够从 OP 代码中删除大量冗余,将编译时间和目标文件大小减少到可管理的值。
以下是使用上述错误报告中的测试用例进行的一些测试:
$ time gcc5 -O3 -c -o testcase.gcc5-O3.o testcase.c
real 0m39,30s
user 0m37,85s
sys 0m1,42s
$ time gcc5 -O0 -c -o testcase.gcc5-O0.o testcase.c
real 23m33,34s
user 23m27,07s
sys 0m5,92s
$ time tcc -c -o testcase.tcc.o testcase.c
real 0m2,60s
user 0m2,42s
sys 0m0,17s
$ time clang -O3 -c -o testcase.clang-O3.o testcase.c
real 0m13,71s
user 0m12,55s
sys 0m1,16s
$ time clang -O0 -c -o testcase.clang-O0.o testcase.c
real 0m17,63s
user 0m16,14s
sys 0m1,49s
$ time clang -Os -c -o testcase.clang-Os.o testcase.c
real 0m14,88s
user 0m13,73s
sys 0m1,11s
$ time clang -Oz -c -o testcase.clang-Oz.o testcase.c
real 0m13,56s
user 0m12,45s
sys 0m1,09
这是生成的目标文件大小:
text data bss dec hex filename
39101286 0 0 39101286 254a366 testcase.clang-O0.o
72161 0 0 72161 119e1 testcase.clang-O3.o
72087 0 0 72087 11997 testcase.clang-Os.o
72087 0 0 72087 11997 testcase.clang-Oz.o
38683240 0 0 38683240 24e4268 testcase.gcc5-O0.o
87500 0 0 87500 155cc testcase.gcc5-O3.o
78239 0 0 78239 1319f testcase.gcc5-Os.o
69210504 3170616 0 72381120 45072c0 testcase.tcc.o
我正在尝试编译一个大型 C 文件(专门用于 MATLAB mexing)。 C 文件大约有 20 MB(如果您想使用它,可以使用 from the GCC bug tracker)。
下面是我正在 运行ning 的命令和屏幕输出。这已经 运行ning 几个小时了,如您所见,优化已被禁用 (-O0)。为什么这么慢?有什么办法可以让它更快吗?
(供参考:Ubuntu12.04(Precise Pangolin)64 位和 GCC 4.7.3)
/usr/bin/gcc -c -DMX_COMPAT_32 -D_GNU_SOURCE -DMATLAB_MEX_FILE -I"/usr/local/MATLAB/R2015a/extern/include" -I"/usr/local/MATLAB/R2015a/simulink/include" -ansi -fexceptions -fPIC -fno-omit-frame-pointer -pthread -O0 -DNDEBUG path/to/test4.c -o /tmp/mex_198714460457975_3922/test4.o -v
Using built-in specs.
COLLECT_GCC=/usr/bin/gcc
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.7.3-2ubuntu1~12.04' --with-bugurl=file:///usr/share/doc/gcc-4.7/README.Bugs --enable-languages=c,c++,go,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.7 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.7 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --with-system-zlib --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.7.3 (Ubuntu/Linaro 4.7.3-2ubuntu1~12.04)
COLLECT_GCC_OPTIONS='-c' '-D' 'MX_COMPAT_32' '-D' '_GNU_SOURCE' '-D' 'MATLAB_MEX_FILE' '-I' '/usr/local/MATLAB/R2015a/extern/include' '-I' '/usr/local/MATLAB/R2015a/simulink/include' '-ansi' '-fexceptions' '-fPIC' '-fno-omit-frame-pointer' '-pthread' '-O0' '-D' 'NDEBUG' '-o' '/tmp/mex_198714460457975_3922/test4.o' '-v' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1 -quiet -v -I /usr/local/MATLAB/R2015a/extern/include -I /usr/local/MATLAB/R2015a/simulink/include -imultilib . -imultiarch x86_64-linux-gnu -D_REENTRANT -D MX_COMPAT_32 -D _GNU_SOURCE -D MATLAB_MEX_FILE -D NDEBUG path/to/test4.c -quiet -dumpbase test4.c -mtune=generic -march=x86-64 -auxbase-strip /tmp/mex_198714460457975_3922/test4.o -O0 -ansi -version -fexceptions -fPIC -fno-omit-frame-pointer -fstack-protector -o /tmp/ccxDOA5f.s
GNU C (Ubuntu/Linaro 4.7.3-2ubuntu1~12.04) version 4.7.3 (x86_64-linux-gnu)
compiled by GNU C version 4.7.3, GMP version 5.0.2, MPFR version 3.1.0-p3, MPC version 0.9
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/local/MATLAB/R2015a/extern/include
/usr/local/MATLAB/R2015a/simulink/include
/usr/lib/gcc/x86_64-linux-gnu/4.7/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/4.7/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
GNU C (Ubuntu/Linaro 4.7.3-2ubuntu1~12.04) version 4.7.3 (x86_64-linux-gnu)
compiled by GNU C version 4.7.3, GMP version 5.0.2, MPFR version 3.1.0-p3, MPC version 0.9
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: c119948b394d79ea05b6b3986ab084cf
编辑:后续:我听从了 chqrlie 的建议,tcc 在 <5 秒内编译了我的函数(我只需要删除 -ansi 标志并将 "gcc" 变为 "tcc"),这非常了不起,真的。我只能想象 GCC 的复杂性。
然而,当尝试 mex 时,通常还需要 mex 的另一个命令。第二个命令通常是:
/usr/bin/gcc -pthread -Wl,--no-undefined -Wl,-rpath-link,/usr/local/MATLAB/R2015a/bin/glnxa64 -shared -O -Wl,--version-script,"/usr/local/MATLAB/R2015a/extern/lib/glnxa64/mexFunction.map" /tmp/mex_61853296369424_4031/test4.o -L"/usr/local/MATLAB/R2015a/bin/glnxa64" -lmx -lmex -lmat -lm -lstdc++ -o test4.mexa64
我不能 运行 使用 tcc,因为其中一些标志不兼容。如果我尝试使用 GCC 运行 这第二个编译步骤,我得到:
/usr/bin/ld: test4.o: relocation R_X86_64_PC32 against undefined symbol `mxGetPr' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status
编辑:解决方案似乎是 clang。 tcc 可以编译文件,但是 mexing 中第二步的参数与 tcc 的参数选项不兼容。 Clang 速度非常快,生成了一个漂亮的、小的、优化的文件。
几乎整个文件都是一个表达式,赋值给double f[24] = ...
。这将生成一个 gigantic 抽象语法树。如果只有专门的编译器才能有效地处理这个问题,我会感到很惊讶。
20 兆字节的文件本身可能没问题,但一个巨大的表达式可能是导致问题的原因。尝试作为初步步骤,将行拆分为 double f[24] = {0}
,然后分配 24 个 f[0] = ...; f[1] = ...
,看看会发生什么。最坏的情况是,您可以将 24 个赋值拆分为 24 个函数,每个函数都在自己的 .c
文件中,然后分别编译它们。这不会减少 AST 的大小,它只会重新组织它,但 GCC 在处理许多语句时可能更优化,这些语句加在一起会构成大量代码,而不是一个巨大的表达式。
最终的方法是以更优化的方式生成代码。例如,如果我搜索 s4*s5*s6
,我会得到 77,783 次匹配。这些 s[4-6]
变量不会改变。您应该生成一个临时变量 double _tmp1 = s4*s5*s6;
然后使用它而不是重复表达式。您刚刚从抽象语法树中删除了 311,132 个节点(假设 s4*s5*s6
是 5 个节点,_tmp1
是一个节点)。这就是 GCC 要做的更少的处理。这也应该生成 更快 代码(您不会重复相同的乘法 77,783 次)。
如果您以递归方式巧妙地执行此操作(例如 s4*s5*s6
--> _tmp1
、(c4*c6+s4*s5*s6)
--> (c4*c6+_tmp1)
--> _tmp2
, c5*s6*(c4*c6+s4*s5*s6)
--> c5*s6*_tmp2
-> _tmp3
, 等等...),您可能可以消除生成代码的大部分大小。
尝试 Fabrice Bellard 的微型 C 编译器 tcc
来自 http://tinycc.org:
chqrlie$ time tcc -c test4.c
real 0m1.336s
user 0m1.248s
sys 0m0.084s
chqrlie$ size test4.o
text data bss dec hex filename
38953877 3170632 0 42124509 282c4dd test4.o
是的,这是 1.336 秒 在一台非常基本的 PC 上!
当然我无法测试生成的可执行文件,但目标文件应该可以与您的程序和库的其余部分链接。
对于这个测试,我使用了一个虚拟版本的文件 mex.h
:
typedef struct mxArray mxArray;
double *mxGetPr(const mxArray*);
enum { mxREAL = 0 };
mxArray *mxCreateDoubleMatrix(int nx, int ny, int type);
gcc
还没编译完...
编辑:gcc
设法霸占了我的 Linux 盒子,我无法再连接到它了:(
经过测试,我发现Clang编译器编译大文件的问题似乎较少。尽管 Clang 在编译过程中消耗了近 GB 的内存,但它成功地将 OP 的源代码形式转换为 70 kB 的目标文件。这适用于我测试的所有优化级别。
如果打开优化,gcc 也能够快速编译此文件,并且不会消耗太多内存。这个 bug in gcc 来自 OPs 代码中的大表达式,这给寄存器分配器带来了巨大的负担。启用优化后,编译器执行称为 公共子表达式消除 的优化,它能够从 OP 代码中删除大量冗余,将编译时间和目标文件大小减少到可管理的值。
以下是使用上述错误报告中的测试用例进行的一些测试:
$ time gcc5 -O3 -c -o testcase.gcc5-O3.o testcase.c
real 0m39,30s
user 0m37,85s
sys 0m1,42s
$ time gcc5 -O0 -c -o testcase.gcc5-O0.o testcase.c
real 23m33,34s
user 23m27,07s
sys 0m5,92s
$ time tcc -c -o testcase.tcc.o testcase.c
real 0m2,60s
user 0m2,42s
sys 0m0,17s
$ time clang -O3 -c -o testcase.clang-O3.o testcase.c
real 0m13,71s
user 0m12,55s
sys 0m1,16s
$ time clang -O0 -c -o testcase.clang-O0.o testcase.c
real 0m17,63s
user 0m16,14s
sys 0m1,49s
$ time clang -Os -c -o testcase.clang-Os.o testcase.c
real 0m14,88s
user 0m13,73s
sys 0m1,11s
$ time clang -Oz -c -o testcase.clang-Oz.o testcase.c
real 0m13,56s
user 0m12,45s
sys 0m1,09
这是生成的目标文件大小:
text data bss dec hex filename
39101286 0 0 39101286 254a366 testcase.clang-O0.o
72161 0 0 72161 119e1 testcase.clang-O3.o
72087 0 0 72087 11997 testcase.clang-Os.o
72087 0 0 72087 11997 testcase.clang-Oz.o
38683240 0 0 38683240 24e4268 testcase.gcc5-O0.o
87500 0 0 87500 155cc testcase.gcc5-O3.o
78239 0 0 78239 1319f testcase.gcc5-Os.o
69210504 3170616 0 72381120 45072c0 testcase.tcc.o