今天我在使用python中的线程计算素数时遇到了问题。它几乎和没有线程一样慢(参见Question)。
现在我创建了相同的代码,认为使用pthread在C中不存在python问题。
#include <stdio.h>
#include <time.h>
#include <pthread.h>
int isPrime(int number) {
int i;
for (i=2; i<number; i++) {
if (number % i == 0 && i != number) return 0;
}
return 1;
}
void calcPrimeNumbersFromNtoM(int n, int m){
for (int i = n; i <= m; i++) {
if (isPrime(i)) {
//printf("%i\n",i);
}
}
}
void *calcFirstHalf(){
calcPrimeNumbersFromNtoM(1,5000);
return NULL;
}
void *calcSecondHalf(){
calcPrimeNumbersFromNtoM(5001,10000);
return NULL;
}
void calcThreadedPrimenumbers(){
pthread_t t1, t2;
pthread_create(&t1, NULL, calcFirstHalf, NULL);
pthread_create(&t2, NULL, calcSecondHalf, NULL);
//wait for the threads to finish
pthread_join(t1, NULL);
pthread_join(t2, NULL);
}
int main(int argc, const char * argv[])
{
clock_t startNT, endNT,startT, endT;
double cpu_time_usedNT,cpu_time_usedT;
startNT = clock();
calcPrimeNumbersFromNtoM(1, 10000);
endNT = clock();
cpu_time_usedNT = ((double) (endNT - startNT)) / CLOCKS_PER_SEC;
startT = clock();
calcThreadedPrimenumbers();
endT = clock();
cpu_time_usedT = ((double) (endT - startT)) / CLOCKS_PER_SEC;
printf("--------Results-----------\n");
printf("Non threaded took: %f secs\n",cpu_time_usedNT);
printf("Threaded took: %f secs\n",cpu_time_usedT);
return 0;
}
结果是线程再次与非线程一样慢:
--------Results-----------
Non threaded took: 0.020624 secs
Threaded took: 0.027257 secs
这让我很困惑。我的代码有问题吗?线程不是比没有线程更快的必要吗?如果是,对此有何解释?
这是由需要安排相同任务的操作系统分成两部分导致相同的时间吗?
也许这很重要:我使用2.6Ghz Core i5 MacBook和OSX 10.9
答案 0 :(得分:6)
您的素数计算器为O(n^2)
。请注意5000^2 = 25000000
,而(10,000^2)/2 = 50000000
。
这使得第二个线程成为算法的瓶颈,并且正在等待第一个线程的大量时间。
换句话说,与第二个线程相比,第一个线程做的工作很少,因此第一个线程在大多数工作中都处于闲置状态。
答案 1 :(得分:2)
clock()
返回CPU时间。如果您同时使用2个CPU 1秒钟,clock()
将增加2.您将需要测量墙上时间(实际经过的实际世界时间)。此外,正如其他回答者所说,您的线程负载是不平衡的,因此一个线程的运行时间比另一个线程长得多,尽管总的时间应该仍然只有单线程情况的75%。 (工作量足够长)
答案 2 :(得分:1)
我认为你会发现你的isPrime
函数是O(n),因此大n
的后半部分将主导整个时间。你应该为无螺纹测试分别计算两半。
答案 3 :(得分:1)
专门解决您的(一般)问题
Is it true that threads are not necessary faster than using no thread?
If yes what is the explanation for this?
使用多个线程完成任务的效率主要受CPU核心数量(包括可用的超线程)的限制。例如,如果您的系统有两个核心,那么两个线程可以同时运行。在您的情况下(i5),您可能拥有2核或4核处理器。使用超线程,您的系统可以同时运行4或8个线程。
如果您的应用程序似乎只有两个主题(三个,包括父主&#39; main()&#39;主题),那么应该有一个显着的改进。但是,请记住,您的线程不是系统中唯一活动的线程。可能,你的机器上已有很多执行线程;所有竞争CPU资源。
当CPU资源可用时,线程调度程序从等待CPU的线程队列中提取另一个线程。您的某个线程不可能始终位于运行队列的顶部。因此,他们将继续在运行队列中等待轮到他们。
每当您的代码调用“阻止”代码时,函数,线程的上下文存储在内存中,线程返回到运行队列。即使是无法使用的函数,例如&gt; printf()&#39;也可能会阻塞,这会导致线程返回到运行队列。
通常,对等线程竞争CPU资源以外的资源;例如共享内存,共享文件访问等。通常这些资源受信号量,锁等的保护。这也会影响多个线程与单个线程的效率。
这些以及许多其他因素(包括Mark Ransom提到的因素)可能会对时间结果产生影响。
答案 4 :(得分:1)
您可以通过以不同方式对作品进行分区来对线程进行负载均衡。注意2是唯一的偶数素数,所以给每个线程一半的奇数用这样的代码
void *calcFirstHalf()
{
int i;
for ( i = 1; i < 1000000; i += 4 ) // 1, 5, 9, 13...
if ( isPrime( i ) )
{
}
return NULL;
}
void *calcSecondHalf()
{
int i;
for ( i = 3; i < 1000000; i += 4 ) // 3, 7, 11, 15...
if ( isPrime( i ) )
{
}
return NULL;
}
旁注:您还可以通过仅检查建议素数的平方根的因子来提高isPrime
函数的效率,因为每个非素数必须至少有一个小于或等于的因子。等于平方根。
在MAC上执行效果测量
通过mach_absolute_time
功能访问MAC上的高精度定时器,如下面的代码所示。
#include <mach/mach.h>
#include <mach/mach_time.h>
void testTimer( void )
{
uint64_t start, end;
mach_timebase_info_data_t info;
mach_timebase_info( &info );
printf( "numer=%u denom=%u\n", info.numer, info.denom );
start = mach_absolute_time();
sleep( 1 );
end = mach_absolute_time();
printf( "%llu\n", end - start );
}
请注意,计时器的精度不是固定值,但必须根据mach_timebase_info
函数返回的信息计算。计算是
timer_rate = 1Ghz * numer / denom
您可以通过拨打sleep
一秒钟来确认计时器费率,以查看您每秒获得多少刻度。