线程像非线程一样“慢”

时间:2014-04-28 22:11:23

标签: c multithreading algorithm pthreads theory

今天我在使用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

5 个答案:

答案 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一秒钟来确认计时器费率,以查看您每秒获得多少刻度。