OSX中的多线程C程序比Linux慢得多

OSX中的多线程C程序比Linux慢得多,c,linux,multithreading,macos,pthreads,C,Linux,Multithreading,Macos,Pthreads,我写这篇文章是为了一个我已经完成并提交的操作系统类作业。我昨天发布了这个问题,但由于“学术诚信”的规定,我把它推迟到提交截止日期之后 目的是学习如何使用关键部分。有一个data数组,其中包含100个单调递增的数字、0…99和40个线程,每个线程随机交换两个元素2000000次。每隔一秒钟,一个检查器检查一次,确保每个数字中只有一个(这意味着没有并行访问发生) 以下是Linux时代: real 0m5.102s 用户0m5.087s 系统0m0.000s 还有OSX时代 real 6m54.

我写这篇文章是为了一个我已经完成并提交的操作系统类作业。我昨天发布了这个问题,但由于“学术诚信”的规定,我把它推迟到提交截止日期之后

目的是学习如何使用关键部分。有一个
data
数组,其中包含100个单调递增的数字、0…99和40个线程,每个线程随机交换两个元素2000000次。每隔一秒钟,一个
检查器检查一次,确保每个数字中只有一个(这意味着没有并行访问发生)

以下是Linux时代:

real 0m5.102s
用户0m5.087s
系统0m0.000s
还有OSX时代

real 6m54.139s
用户0m41.873s
系统6m43.792s
我在运行OS X的同一台机器上运行了一个带有ubuntu/trusty64的vagrant box。它是一个四核i7 2.3Ghz(高达3.2Ghz)2012 rMBP

如果我理解正确的话,
sys
是系统开销,我无法控制,即使如此,41秒的用户时间表明线程可能是串行运行的

如果需要,我可以发布所有代码,但我会发布我认为相关的代码。我正在使用
pthreads
,因为这是Linux提供的,但我认为它们可以在OSX上工作

创建
swapper
线程以运行
swapManyTimes
例程:

for(inti=0;i
Swapper
线程关键部分,在for循环中运行200万次:

pthread_mutex_lock(&mutex);//开始临界段
int tmpFirst=数据[第一];
数据[第一]=数据[第二];
数据[秒]=tmpFirst;
pthread_mutex_unlock(&mutex);//端部临界截面
只创建一个
检查器
线程,方法与
交换程序
相同。它通过遍历
数据
数组并用
true
标记每个值对应的索引来操作。然后,它检查有多少索引是空的。因此:

pthread\u mutex\u lock(&mutex);
对于(int i=0;i
它在运行
while(1)
循环后,通过调用
sleep(1)
每秒运行一次。所有
swapper
线程加入后,此线程也将被取消并加入


我很乐意提供更多的信息来帮助你弄清楚为什么Mac电脑上这么糟糕。我并不是真的在寻找代码优化方面的帮助,除非这是OS X的绊脚石。我已经尝试在OS X上使用
clang
gcc-4.9
来构建它。

我已经在很大程度上复制了您的结果(没有清理程序):

在我的MacBook Pro(约塞米蒂10.10.2)上,2.6 GHz i7,16 GB内存

john-schultzs-macbook-pro:~ jschultz$ time ./a.out 1

real    0m0.057s
user    0m0.054s
sys 0m0.002s
john-schultzs-macbook-pro:~ jschultz$ time ./a.out 2

real    0m5.684s
user    0m1.148s
sys 0m5.353s
john-schultzs-macbook-pro:~ jschultz$ time ./a.out 3

real    0m8.946s
user    0m1.967s
sys 0m8.034s
john-schultzs-macbook-pro:~ jschultz$ time ./a.out 4

real    0m11.980s
user    0m2.274s
sys 0m10.801s
john-schultzs-macbook-pro:~ jschultz$ time ./a.out 5

real    0m15.680s
user    0m3.307s
sys 0m14.158s
john-schultzs-macbook-pro:~ jschultz$ time ./a.out 40

real    2m7.377s
user    0m23.926s
sys 2m2.434s
我的Mac用了大约12倍的挂钟时间才完成了40个线程,这与非常旧的Linux+gcc版本相比

注意:我将代码更改为每个线程进行1M交换

在竞争之下,OSX似乎比Linux做了更多的工作。也许它比Linux更精细地交错它们

编辑更新的代码以记录线程立即重新捕获锁的平均次数:

Linux

MacOSX

john-schultzs-macbook-pro:~ jschultz$ time ./a.out 10
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000

real    0m34.163s
user    0m5.902s
sys 0m30.329s

因此,OSX更均匀地共享其锁,因此有更多的线程挂起和恢复。

MacOSX和Linux以不同的方式实现pthread,导致这种缓慢的行为。具体而言,MacOSX不使用旋转锁(根据ISO C标准,旋转锁是可选的)。这可能会导致像这样的示例的代码性能非常非常慢

The OP does not mention/show any code that indicates the thread(s) sleep, wait, give up execution, etc and all the threads are at the same 'nice' level.  
因此,一个单独的线程可能会获得CPU,并且在完成所有2mil执行之前不会释放它

这将导致在linux上执行上下文切换的时间最短

然而,在MAC OS上,一个执行只被赋予一个“时间片”来执行,而另一个“准备执行”线程/进程才被允许执行

这意味着更多的上下文切换

上下文切换在“sys”时间内执行

结果是MAC操作系统的执行时间要长得多

为了更公平地竞争,您可以通过插入nanosleep()或通过调用释放执行来强制进行上下文切换

#include <sched.h>

then calling

int sched_yield(void);
#包括
然后打电话
int sched_收益率(无效);

与问题无关:pthreads意味着POSIX线程,因此如果OS X兼容POSIX,“我认为它是!”,那么
pthreads
应该在OS X中工作。问题:编译代码时是否使用了优化?我用
-O0
尝试过,
-O1
-O2
无difference@JS1检查程序每次运行时都会打印一个星号。我大约每秒看到一个星号,所以我怀疑这是问题所在。检查者不必与交换者竞争。如果它从未运行过,那也没关系。交换程序运行一个1…2000000 for循环,然后退出。也许OS X有一个糟糕的pthreads实现?@jschultz410这当然是可能的,但我认为很难像这样把它搞得一团糟。这就是我认为错误可能是我的原因。快速搜索“osx pthread mutex实现”表明其他人也看到了类似的数字,并且osx pthread mutex可能没有使用用户空间自旋锁。如果每个锁调用都是一个系统调用,这也许可以解释结果。为什么不使用自旋锁会导致行为缓慢?我认为自旋锁是对CPU的浪费,而互斥锁只会导致进程放弃其执行状态,除非它成功通过。如果你能解释一点为什么不使用自旋锁会慢一些,我会很高兴地将此标记为回答:)@Alex我发现这解释了使用自旋锁和不使用自旋锁的权衡。此外,ans
john-schultzs-macbook-pro:~ jschultz$ time ./a.out 10
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000
Thread exiting with avg streak length 1.000000

real    0m34.163s
user    0m5.902s
sys 0m30.329s
The OP does not mention/show any code that indicates the thread(s) sleep, wait, give up execution, etc and all the threads are at the same 'nice' level.  
#include <sched.h>

then calling

int sched_yield(void);