TL;DR
It seems that even though the data of the allocated/used memory itself is not being copied, thanks to the copy-on-write feature, the internal virtual memory structures in the kernel, which hold the information about how much and what memory the parent process has allocated, are being copied in an inefficient way while the child process is being created by fork().
forkしたときコピー・オン・ライトによりmallocされたメモリそのものはコピーされないが、カーネル内部での仮想メモリの構造が非効率な方法でコピーされている。
https://blog.famzah.net/2009/11/20/fork-gets-slower-as-parent-process-use-more-memory/
実験用プログラム
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int count = 3000; // forkする回数
int mb = 10; // mallocするサイズ(MiB)
struct timeval tv1;
struct timeval tv2;
if (argc >= 2)
mb = atoi(argv[1]);
if (argc >= 3)
count = atoi(argv[2]);
printf("count = %d\n", count);
printf("mb = %d\n", mb);
int len = 1024 * 1024 * mb;
int size = sizeof(int) * len;
int* p = malloc(size);
// mallocしただけではカーネルはメモリを確保しないので書き込んでおく
for (int i = 0; i < len; i++) {
p[i] = i;
}
gettimeofday(&tv1, NULL);
// fork
for (int i = 0; i < count; i++) {
if (fork() == 0) {
exit(0);
}
}
// 全子プロセスが終了するまで待つ
while (wait(NULL) != -1) {
}
gettimeofday(&tv2, NULL);
double elapsed = tv2.tv_sec - tv1.tv_sec + (tv2.tv_usec - tv1.tv_usec) / (1000.0 * 1000.0);
printf("elapsed = %f\n", elapsed);
return 0;
}
実行例
(10の部分を1から40まで変えて実行してみた)
./a.out 10
実験結果
FreeBSD
1 0.645625
10 1.102455
20 1.150587
30 4.327679
40 6.803514
Linux
1 0.445254
10 1.767612
20 3.032894
30 4.407658
40 5.822945