静的メモリの大きなチャンクを使用する小さなCプログラムで何かをモックアップすると便利な場合があります。 Fedora 15に変更した後、プログラムのコンパイルにlong時間がかかったことに気づきました。私たちは30秒対0.1秒を話している。さらに奇妙なことに、ld(リンカー)がCPUを使い果たし、使用可能なすべてのメモリをゆっくりと使い始めました。少しいじった後、私はこの新しい問題とスワップファイルのサイズとの相関関係を見つけることができました。このディスカッションの目的のためのサンプルプログラムは次のとおりです。
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define M 1000000
#define GIANT_SIZE (200*M)
size_t g_arr[GIANT_SIZE];
int main( int argc, char **argv){
int i;
for(i = 0; i<10; i++){
printf("This should be zero: %d\n",g_arr[i]);
}
exit(1);
}
このプログラムには、約200 * 8MB = 1.6GBの静的メモリの宣言されたサイズを持つ巨大な配列があります。このプログラムのコンパイルには、途方もない時間がかかります。
[me@bleh]$ time gcc HugeTest.c
real 0m12.954s
user 0m6.995s
sys 0m3.890s
[me@bleh]$
13秒〜13行のCプログラムの場合!?そうではありません。キー番号は、静的メモリスペースのサイズです。合計スワップスペースよりも大きくなるとすぐに、コンパイルが再開されます。たとえば、5.3GBのスワップスペースがあるため、GIANT_SIZEを(1000 * M)に変更すると、次の時間が得られます。
[me@bleh]$ time gcc HugeTest.c
real 0m0.087s
user 0m0.026s
sys 0m0.027s
ああ、それはもっと似ている!スワップスペースが確かにマジックナンバーであることを自分自身(そして自宅でこれを試している場合は自分自身)にさらに納得させるために、使用可能なスワップスペースを本当に巨大な19GBに変更し、(1000 * M)バージョンを再度コンパイルしてみました:
[me@bleh]$ ls -ALi /extraswap
5986 -rw-r--r-- 1 root root 14680064000 Jul 26 15:01 /extraswap
[me@bleh]$ Sudo swapon /extraswap
[me@bleh]$ time gcc HugeTest.c
real 4m28.089s
user 0m0.016s
sys 0m0.010s
4.5分経っても完成しませんでした!
リンカがここで何か問題を起こしていることは明らかですが、プログラムを書き直すか、スワップスペースをいじる以外に、これを回避する方法がわかりません。解決策があるかどうか、またはいくつかの不可解なバグに遭遇したかどうかを知りたいです。
ちなみに、すべてのスワップビジネスに関係なく、プログラムはすべて正しくコンパイルおよび実行されます。
参考までに、関連する可能性のある情報をいくつか示します。
[]$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 27027
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 1024
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
[]$ uname -r
2.6.40.6-0.fc15.x86_64
[]$ ld --version
GNU ld version 2.21.51.0.6-6.fc15 20110118
Copyright 2011 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.
[]$ gcc --version
gcc (GCC) 4.6.1 20110908 (Red Hat 4.6.1-9)
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
[]$ cat /proc/meminfo
MemTotal: 3478272 kB
MemFree: 1749388 kB
Buffers: 16680 kB
Cached: 212028 kB
SwapCached: 368056 kB
Active: 489688 kB
Inactive: 942820 kB
Active(anon): 401340 kB
Inactive(anon): 803436 kB
Active(file): 88348 kB
Inactive(file): 139384 kB
Unevictable: 32 kB
Mlocked: 32 kB
SwapTotal: 19906552 kB
SwapFree: 17505120 kB
Dirty: 172 kB
Writeback: 0 kB
AnonPages: 914972 kB
Mapped: 60916 kB
Shmem: 1008 kB
Slab: 55248 kB
SReclaimable: 26720 kB
SUnreclaim: 28528 kB
KernelStack: 3608 kB
PageTables: 63344 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 21645688 kB
Committed_AS: 11208980 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 139336 kB
VmallocChunk: 34359520516 kB
HardwareCorrupted: 0 kB
AnonHugePages: 151552 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 730752 kB
DirectMap2M: 2807808 kB
TL; DR:cプログラムの(大きな)静的メモリが使用可能なスワップスペースよりわずかに少ない場合、リンカはプログラムをリンクするのに永遠にかかります。ただし、静的スペースが使用可能なスワップスペースよりもわずかに大きいである場合は、非常にきびきびします。どうしたの!?
これをUbuntu10.10システム(GNU ld (GNU Binutils for Ubuntu) 2.20.51-system.20100908
)で再現できますが、あなたの答えはあると思います。まず、いくつかの方法論。
これが小さなVM(512MB ram、2GBスワップ)で発生することを確認した後、ここから、gccをstraceして、すべてが正確に何が起こっているかを確認するのが最も簡単な方法であると判断しました。地獄に行きました:
~# strace -f gcc swap.c
それは以下を照らしました:
vfork() = 3589
[pid 3589] execve("/usr/lib/gcc/x86_64-linux-gnu/4.4.5/collect2", ["/usr/lib/gcc/x86_64-linux-gnu/4."..., "--build-id", "--eh-frame-hdr", "-m", "elf_x86_64", "--hash-style=gnu", "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2", "-o", "swap", "-z", "relro", "/usr/lib/gcc/x86_64-linux-gnu/4."..., "/usr/lib/gcc/x86_64-linux-gnu/4."..., "/usr/lib/gcc/x86_64-linux-gnu/4."..., "-L/usr/lib/gcc/x86_64-linux-gnu/"..., ...], [/* 26 vars */]) = 0
...
[pid 3589] vfork() = 3590
...
[pid 3590] execve("/usr/bin/ld", ["/usr/bin/ld", "--build-id", "--eh-frame-hdr", "-m", "elf_x86_64", "--hash-style=gnu", "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2", "-o", "swap", "-z", "relro", "/usr/lib/gcc/x86_64-linux-gnu/4."..., "/usr/lib/gcc/x86_64-linux-gnu/4."..., "/usr/lib/gcc/x86_64-linux-gnu/4."..., "-L/usr/lib/gcc/x86_64-linux-gnu/"..., ...], [/* 27 vars */]) = 0
...
[pid 3590] lseek(13, 4096, SEEK_SET) = 4096
[pid 3590] read(13, ".\4@\0\0\0\0\0>\4@\0\0\0\0\0N\4@\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096
[pid 3590] mmap(NULL, 1600004096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1771931000
<system comes to screeching halt>
ご想像のとおり、ld
が実際にこの配列の静的メモリ空間全体(またはプログラム全体)を匿名でmmap
しようとしているように見えます。わかりにくいです。プログラムの残りの部分は非常に小さいので、すべてがその余分な4096に収まる可能性があります。
これですべてうまくいきましたが、システムで使用可能なスワップを超えたときになぜ機能するのでしょうか。 swapoff
を回して、strace -f
をもう一度実行しましょう...
[pid 3618] lseek(13, 4096, SEEK_SET) = 4096
[pid 3618] read(13, ".\4@\0\0\0\0\0>\4@\0\0\0\0\0N\4@\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096
[pid 3618] mmap(NULL, 1600004096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
[pid 3618] brk(0x60638000) = 0x1046000
[pid 3618] mmap(NULL, 1600135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
[pid 3618] mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7fd011864000
...
当然のことながら、ldは前回試したのと同じことを実行して、スペース全体をmmapするようです。しかし、システムはそれを行うことができなくなり、失敗します! ldは再試行し、再度失敗します。その後、ldは予期しない処理を実行します...メモリが少なくなります。
奇妙なことに、 ld
コード を見てみたほうがいいと思います。 Drat、明示的なmmap
は実行しません。これは、昔ながらのmalloc
の内部から来ているに違いありません。これを追跡するには、いくつかのデバッグシンボルを使用してldをビルドする必要があります。残念ながら、bin-utils 2.21.1をビルドすると、問題は解決しました。おそらく、bin-utilsの新しいバージョンで修正されていますか?
私はOpenSuse11.4を拷問テストしました(1週間で12.1になります)
私は4GiBRAM + 2GiBスワップを持っていて、深刻な速度低下に気づかなかった。システムが時々ゴミ箱に入るかもしれないが、それでもコンパイル時間は短かった。
ヘビースワッピング中の最長は6秒でした。
[tester@ulises ~]$ free -m
total used free shared buffers cached
Mem: 3456 3426 30 0 4 249
-/+ buffers/cache: 3172 284
Swap: 2055 1382 672
[tester@ulises ~]$ time cc -Wall -O test2.c
test2.c: In function ‘main’:
test2.c:13:2: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘size_t’
real 0m6.501s
user 0m0.101s
sys 0m0.078s
[tester@ulises ~]$ free -m
total used free shared buffers cached
Mem: 3456 3389 67 0 5 289
-/+ buffers/cache: 3094 362
Swap: 2055 1455 599
[tester@ulises ~]$ free -m
total used free shared buffers cached
Mem: 3456 3373 82 0 4 264
-/+ buffers/cache: 3104 352
Swap: 2055 1442 612
[tester@ulises ~]$ time cc -Wall -O test2.c
test2.c: In function ‘main’:
test2.c:13:2: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘size_t’
real 0m1.122s
user 0m0.086s
sys 0m0.045s
[tester@ulises ~]$ time cc -Wall -O test2.c
test2.c: In function ‘main’:
test2.c:13:2: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘size_t’
real 0m0.095s
user 0m0.047s
sys 0m0.032s
[tester@ulises ~]$ free -m
total used free shared buffers cached
Mem: 3456 3376 79 0 4 252
-/+ buffers/cache: 3119 336
Swap: 2055 1436 618
[tester@ulises ~]$ time cc -Wall -O test2.c
test2.c: In function ‘main’:
test2.c:13:2: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘size_t’
real 0m0.641s
user 0m0.054s
sys 0m0.040s
実行の間に、Virtualbox Box VM、Eclipse、大きなpdfファイル、800以上のMiBを使用したmifirefoxのみをロードおよびアンロードしました。私は制限を超えませんでした。さもないと、多くのアプリがOSによって強制終了されてしまいます。 Firefoxを強制終了することを優先します。:-)
私はまた、極端な定義に行きました:
#define M 1048576
#define GIANT_SIZE (20000*M)
それでも、大きな変化はありません。
[tester@ulises ~]$ time cc -Wall -O test2.c
test2.c:7:14: warning: integer overflow in expression
test2.c:7:8: error: size of array ‘g_arr’ is negative
test2.c:7:1: warning: variably modified ‘g_arr’ at file scope
test2.c: In function ‘main’:
test2.c:13:2: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘size_t’
real 0m0.661s
user 0m0.043s
sys 0m0.031s
編集:Fedora16を使用してVM、512MiB RAMおよび1.5GiBスワップで再テストしましたが、「最大」のエラーメッセージを除いて同様でした。ストレスバージョン」では、20000メガバイトがアレイに割り当てられました。エラーは、アレイサイズが負であったことを示しています。
[ricardo@localhost ~]$ time gcc -Wall test2.c
test2.c:7:14: warning: integer overflow in expression [-Woverflow]
test2.c:7:8: error: size of array ‘g_arr’ is negative
test2.c:7:1: warning: variably modified ‘g_arr’ at file scope [enabled by default]
test2.c: In function ‘main’:
test2.c:13:2: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘size_t’ [-Wformat]
real 0m1.053s
user 0m0.050s
sys 0m0.137s
同じ応答がopenSUSE12.1VMでも発生します。 Fedora 16のインストールは継ぎ目が遅く、メモリが不足しています(インストール中、OpenSuse 512 MiBに対して800MiBを使用する必要がありました)。多くのスワップスペースを使用していたため、Fedoraでスワップオフを使用できませんでした。 OpenSuse12.1およびで動作が遅くなったりメモリに問題があったりすることはありませんでした。どちらも基本的に同じバージョンのカーネル、gccなどを備えています。どちらもデスクトップ環境としてKDEを使用したストックインストールを使用しています
私はあなたの問題を再現できませんでした、多分gcc関連の問題です。 4.5などの古いバージョンをダウンロードして、何が起こるかを確認してください
私はこの振る舞いを観察しません(8Gbデスクトップ上のDebian/Sid/AMD64、gcc 4.6.2、binutils gold ld(Debian2.22用のGNUBinutils)1.11)。これが変更されたプログラムです(メモリマップをpmap
で表示します)。
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define M 1000000
#define GIANT_SIZE (2000*M)
size_t g_arr[GIANT_SIZE];
int main( int argc, char **argv){
int i;
char cmd[80];
for(i = 0; i<10; i++){
printf("This should be zero: %d\n",g_arr[i*1000]);
}
sprintf (cmd, "pmap %d", (int)getpid());
system(cmd);
exit(0);
}
これがその編集です:
% time gcc -v -O big.c -o big
Using built-in specs.
COLLECT_GCC=/usr/bin/gcc-4.6.real
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.6/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.6.2-4' --with-bugurl=file:///usr/share/doc/gcc-4.6/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++,go --prefix=/usr --program-suffix=-4.6 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.6 --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-plugin --enable-objc-gc --with-Arch-32=i586 --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.6.2 (Debian 4.6.2-4)
COLLECT_GCC_OPTIONS='-v' '-O' '-o' 'big' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-linux-gnu/4.6/cc1 -quiet -v -imultilib . -imultiarch x86_64-linux-gnu big.c -quiet -dumpbase big.c -mtune=generic -march=x86-64 -auxbase big -O -version -o /tmp/ccWThBP5.s
GNU C (Debian 4.6.2-4) version 4.6.2 (x86_64-linux-gnu)
compiled by GNU C version 4.6.2, GMP version 5.0.2, MPFR version 3.1.0, MPC version 0.9
warning: MPFR header version 3.1.0 differs from library version 3.1.0-p3.
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.6/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/4.6/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/4.6/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
GNU C (Debian 4.6.2-4) version 4.6.2 (x86_64-linux-gnu)
compiled by GNU C version 4.6.2, GMP version 5.0.2, MPFR version 3.1.0, MPC version 0.9
warning: MPFR header version 3.1.0 differs from library version 3.1.0-p3.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 4b128876859f8f310615c7040fa3cb67
COLLECT_GCC_OPTIONS='-v' '-O' '-o' 'big' '-mtune=generic' '-march=x86-64'
as --64 -o /tmp/ccm7905b.o /tmp/ccWThBP5.s
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.6/:/usr/lib/gcc/x86_64-linux-gnu/4.6/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.6/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.6/:/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-O' '-o' 'big' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-linux-gnu/4.6/collect2 --build-id --no-add-needed --eh-frame-hdr -m elf_x86_64 --hash-style=both -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o big /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.6/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.6 -L/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.6/../../.. /tmp/ccm7905b.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/4.6/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crtn.o
gcc -v -O big.c -o big 0.07s user 0.01s system 90% cpu 0.089 total
とその実行:
% time ./big
This should be zero: 0
This should be zero: 0
This should be zero: 0
This should be zero: 0
This should be zero: 0
This should be zero: 0
This should be zero: 0
This should be zero: 0
This should be zero: 0
This should be zero: 0
8835: ./big
0000000000400000 4K r-x-- /home/basile/tmp/big
0000000000401000 4K rw--- /home/basile/tmp/big
0000000000402000 15625000K rw--- [ anon ]
00007f2d15a44000 1512K r-x-- /lib/x86_64-linux-gnu/libc-2.13.so
00007f2d15bbe000 2048K ----- /lib/x86_64-linux-gnu/libc-2.13.so
00007f2d15dbe000 16K r---- /lib/x86_64-linux-gnu/libc-2.13.so
00007f2d15dc2000 4K rw--- /lib/x86_64-linux-gnu/libc-2.13.so
00007f2d15dc3000 20K rw--- [ anon ]
00007f2d15dc8000 124K r-x-- /lib/x86_64-linux-gnu/ld-2.13.so
00007f2d15fb4000 12K rw--- [ anon ]
00007f2d15fe4000 12K rw--- [ anon ]
00007f2d15fe7000 4K r---- /lib/x86_64-linux-gnu/ld-2.13.so
00007f2d15fe8000 4K rw--- /lib/x86_64-linux-gnu/ld-2.13.so
00007f2d15fe9000 4K rw--- [ anon ]
00007ffff5b5b000 132K rw--- [ stack ]
00007ffff5bff000 4K r-x-- [ anon ]
ffffffffff600000 4K r-x-- [ anon ]
total 15628908K
./big 0.00s user 0.00s system 0% cpu 0.004 total
最近のGCC(例:GCC 4.6)をbinutils Goldリンカーとともにインストールすることは、そのようなプログラムにとって重要であると私は信じています。
スワップが関係しているとは聞きません。