Macos 在Mac OS X上,fork之后的内存访问速度非常慢

Macos 在Mac OS X上,fork之后的内存访问速度非常慢,macos,performance,fork,Macos,Performance,Fork,下面的代码在MacOSX上的执行速度大约是Linux上的200倍。我不知道为什么,这个问题似乎并不重要。我怀疑Mac或MacOSX本身或我的硬件上的gcc中存在漏洞 该代码派生了一个进程,该进程将复制页表entires,但不复制Mac OS X上的内存。当写入内存时,会复制内存,这发生在run方法末尾的for循环中。在那里,对于run的前4个调用,必须复制所有页面,因为每个页面都被触摸。对于要在skip为512的情况下运行的第二个4个调用,需要复制每一页,因为每一页都会被触摸。直观地说,前4次通

下面的代码在MacOSX上的执行速度大约是Linux上的200倍。我不知道为什么,这个问题似乎并不重要。我怀疑Mac或MacOSX本身或我的硬件上的gcc中存在漏洞

该代码派生了一个进程,该进程将复制页表entires,但不复制Mac OS X上的内存。当写入内存时,会复制内存,这发生在run方法末尾的for循环中。在那里,对于run的前4个调用,必须复制所有页面,因为每个页面都被触摸。对于要在skip为512的情况下运行的第二个4个调用,需要复制每一页,因为每一页都会被触摸。直观地说,前4次通话的时间应该是后4次通话的两倍,但事实并非如此。对我来说,程序的输出如下:

169.655ms
670.559ms
2784.18ms
16007.1ms
16.207ms
25.018ms
42.712ms
79.676ms
在Linux上是这样的

5.306ms
10.69ms
20.91ms
41.042ms
6.115ms
12.203ms
23.939ms
40.663ms
Mac OS X上的总运行时间大约为20秒,Linux上的运行时间大约为0.5秒,这两次都是使用gcc编译的完全相同的程序。我试过用gcc4、4.2和4.4编译mac os版本-没有变化

有什么想法吗

代码:

#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
类时间戳
{
私人:
timeval时间;
公众:
Timestamp(){gettimeofday(&time,0);}
双运算符-(const Timestamp&other)const{return static_cast((static_cast(time.tv_sec)*1000000+(time.tv_usec))-(static_cast(other.time.tv_sec)*1000000+(other.time.tv_usec))/1000.0;]
};
叉牛
{
公众:
无效运行(uint64大小,uint64跳过){
//分配和初始化数组
空*阵列状;
posix_memalign(&arrayVoid,4096,尺寸f(uint64_t)*尺寸);
uint64_t*数组=静态_强制转换(arrayVoid);
对于(uint64_t i=0;icerr长时间睡眠的唯一原因是这句话:

sleep(300000);

这将导致300秒的睡眠时间(300*1000)。可能mac os x上的
fork()
的实现与您预期的不同(它总是返回0)。

睡眠时间如此长的唯一原因是这行代码:

sleep(300000);

这会导致300秒的睡眠时间(300*1000)。可能mac os x上的
fork()
的实现与您预期的不同(它总是返回0).

我怀疑您的问题在于Linux上的执行顺序是先运行父级,然后父级执行,子级终止,因为其父级已消失,但在Mac OS上,它先运行子级,这需要300秒的睡眠时间

在任何Unix标准中,绝对不能保证fork之后的两个进程将并行运行。尽管您对操作系统这样做的能力做出了断言

为了证明这是睡眠时间,我用“睡眠时间”替换了“30000”代码,并用
g++-DSLEEPTIME=?foo.c&&./a.out
编译并运行它:

SLEEPTIME   output
20          20442.1
30          30468.5
40          40431.4
10          10449  <just to prove it wasn't getting longer each run>
睡眠时间输出
20          20442.1
30          30468.5
40          40431.4

10 10449我怀疑您的问题在于Linux上的执行顺序是先运行父级,然后父级执行,子级终止,因为其父级已消失,但在Mac OS上,它先运行子级,这需要300秒的睡眠时间

在任何Unix标准中,绝对不能保证fork之后的两个进程将并行运行。尽管您对操作系统这样做的能力做出了断言

为了证明这是睡眠时间,我用“睡眠时间”替换了“30000”代码,并用
g++-DSLEEPTIME=?foo.c&&./a.out
编译并运行它:

SLEEPTIME   output
20          20442.1
30          30468.5
40          40431.4
10          10449  <just to prove it wasn't getting longer each run>
睡眠时间输出
20          20442.1
30          30468.5
40          40431.4

10 10449您一次分配400兆字节,另一次从
fork()
分配(因为包括内存分配在内的过程是重复的)

速度缓慢的原因可能很简单,因为通过使用两个进程的
fork()
,可用的物理内存不足,并且正在使用磁盘中的
交换
内存

这通常比使用物理内存慢得多

编辑以下评论

我建议您更改代码,在写入数组的第一个元素后开始计时测量

  array[0] = 1;
  Timestamp start;
    for (int64_t i = 1; i < size; i++) {
       array[i] = 1;
数组[0]=1;
时间戳开始;
对于(int64_t i=1;i

这样,第一次写入之后的内存分配使用的时间将不会在时间戳中考虑。

您一次分配400兆字节,另一次从
fork()
分配(因为该过程是重复的,包括内存分配)

速度缓慢的原因可能很简单,因为通过使用两个进程的
fork()
,可用的物理内存不足,并且正在使用磁盘中的
交换
内存

这通常比使用物理内存慢得多

编辑以下评论

我建议您更改代码,在写入数组的第一个元素后开始计时测量

  array[0] = 1;
  Timestamp start;
    for (int64_t i = 1; i < size; i++) {
       array[i] = 1;
数组[0]=1;
时间戳开始;
对于(int64_t i=1;i

这样,第一次写入之后的内存分配使用的时间将不会在时间戳中考虑。

您是否已验证fork()是否正常工作:

int main() 
{
    pid_t pid = fork();

    if( pid > 0 ) {
        std::cout << "Parent\n";
    } else if( pid == 0 ) {
        std::cout << "Child\n";
    } else {
        std::cout << "Failed to fork!\n";
    }
}
intmain()
{
pid_t pid=fork();
如果(pid>0){

std::cout您是否已验证fork()是否正常工作:

int main() 
{
    pid_t pid = fork();

    if( pid > 0 ) {
        std::cout << "Parent\n";
    } else if( pid == 0 ) {
        std::cout << "Child\n";
    } else {
        std::cout << "Failed to fork!\n";
    }
}
intmain()
{
pid_t pid=fork();
如果(pid>0){

std::cout当您在子进程上有父进程
waitpid()
并确保它已退出时会发生什么情况(为了安全起见,请处理
SIGCHLD
,以确保进程是r