web-dev-qa-db-ja.com

スワップスペースに関連するリンカーのパフォーマンス?

静的メモリの大きなチャンクを使用する小さな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プログラムの(大きな)静的メモリが使用可能なスワップスペースよりわずかに少ない場合、リンカはプログラムをリンクするのに永遠にかかります。ただし、静的スペースが使用可能なスワップスペースよりもわずかに大きいである場合は、非常にきびきびします。どうしたの!?

43
Rooke

これを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の新しいバージョンで修正されていますか?

25
SoapBox

私は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などの古いバージョンをダウンロードして、何が起こるかを確認してください

1
RedComet

私はこの振る舞いを観察しません(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リンカーとともにインストールすることは、そのようなプログラムにとって重要であると私は信じています。

スワップが関係しているとは聞きません。