并行比串行慢

时间:2015-06-15 05:42:09

标签: c parallel-processing pthreads

问题

大家好,我有一个程序(来自网络),我打算通过使用pthreads将其转换为并行版本来加快速度。但令人惊讶的是,它比串行版本运行更慢。以下是该计划:

# include <stdio.h>

//fast square root algorithm
double asmSqrt(double x) 
{
  __asm__ ("fsqrt" : "+t" (x));
  return x;
}

//test if a number is prime
bool isPrime(int n)
{   
    if (n <= 1) return false;
    if (n == 2) return true;
    if (n%2 == 0) return false;

    int sqrtn,i;
    sqrtn = asmSqrt(n);

    for (i = 3; i <= sqrtn; i+=2) if (n%i == 0) return false;
    return true;
}

//number generator iterated from 0 to n
int main()
{
    n = 1000000; //maximum number
    int k,j;

    for (j = 0; j<= n; j++)
    {
        if(isPrime(j) == 1) k++;
        if(j == n) printf("Count: %d\n",k);
    }
    return 0;
}

首次尝试并行化

我让pthread管理for loop

# include <stdio.h>
.
.

int main()
{
    .
    .
    //----->pthread code here<----
    for (j = 0; j<= n; j++)
    {
        if(isPrime(j) == 1) k++;
        if(j == n) printf("Count: %d\n",k);
    }
    return 0;
}

嗯,它的运行速度比串行的慢

第二次尝试

我将for loop划分为两个主题并使用pthreads并行运行它们

然而,它仍然运行较慢,我打算它可能运行速度快两倍或更快。但它不是!

顺便说一句,这是我的并行代码:

# include <stdio.h>
# include <pthread.h>
# include <cmath>

# define NTHREADS 2

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
int k = 0;

double asmSqrt(double x) 
{
  __asm__ ("fsqrt" : "+t" (x));
  return x;
}

struct arg_struct
{
    int initialPrime;
    int nextPrime;
};

bool isPrime(int n)
{   
    if (n <= 1) return false;

    if (n == 2) return true;

    if (n%2 == 0) return false;

    int sqrtn,i;
    sqrtn = asmSqrt(n);

    for (i = 3; i <= sqrtn; i+=2) if (n%i == 0) return false;

    return true;
}

void *parallel_launcher(void *arguments)
{
    struct arg_struct *args = (struct arg_struct *)arguments;

    int j = args -> initialPrime;
    int n = args -> nextPrime - 1;

    for (j = 0; j<= n; j++)
    {
        if(isPrime(j) == 1)
        {
            printf("This is prime: %d\n",j);
pthread_mutex_lock( &mutex1 );
            k++;
pthread_mutex_unlock( &mutex1 );
        }

        if(j == n) printf("Count: %d\n",k);
    }
pthread_exit(NULL);
}

int main()
{
    int f = 100000000;
    int m;

    pthread_t thread_id[NTHREADS];
    struct arg_struct args;

    int rem = (f+1)%NTHREADS;
    int n = floor((f+1)/NTHREADS);

    for(int h = 0; h < NTHREADS; h++)
    {
        if(rem > 0)
        {
            m = n + 1;
            rem-= 1;
        }
        else if(rem == 0)
        {
            m = n;
        }

        args.initialPrime = args.nextPrime;
        args.nextPrime = args.initialPrime + m;

        pthread_create(&thread_id[h], NULL, &parallel_launcher, (void *)&args);
        pthread_join(thread_id[h], NULL);
    }
   // printf("Count: %d\n",k);
    return 0;
}

注意: 操作系统:Fedora 21 x86_64, 编译器:gcc-4.4, 处理器:Intel Core i5(2个物理内核,4个逻辑), 记忆:6 Gb, 硬盘:340 Gb,

3 个答案:

答案 0 :(得分:2)

主要设计缺陷:您必须让每个线程都有自己的私有计数器变量,而不是使用共享变量。否则,他们将花费更多的时间等待和处理该互斥锁,而不是他们将在实际计算上做的事情。您实质上是强制线程以串行方式执行。

相反,使用私有计数器变量对所有内容求和,一旦线程完成其工作,返回计数器变量并在main()中对它们求和。

此外,您不应该从线程内部调用printf()。如果在printf调用的中间有一个上下文切换,你最终会得到一些糟糕的输出,例如This is This is prime: 2。在这种情况下,您必须在线程之间同步printf调用,这将再次降低程序的速度。此外,printf()调用本身可能是该线程正在进行的工作的90%。因此,根据您想要对结果做什么,对某人进行打印的某种重新设计可能是一个好主意。

答案 1 :(得分:1)

您需要将正在检查素数的范围拆分为 n 部分,其中 n 是线程数。

每个线程运行的代码变为:

typedef struct start_end {
    int start;
    int end;
} start_end_t;

int find_primes_in_range(void *in) {
    start_end_t *start_end = (start_end_t *) in;

    int num_primes = 0;
    for (int j = start_end->start; j <= start_end->end; j++) {
       if (isPrime(j) == 1)
           num_primes++;
    }
    pthread_exit((void *) num_primes;
}

main例程首先启动调用find_primes_in_range的所有线程,然后为每个线程调用pthread_join。它汇总了find_primes_in_range返回的所有值。这可以避免锁定和解锁共享计数变量。

这将使工作并行化,但每个线程的工作量将不相等。这可以解决,但更复杂。

答案 2 :(得分:0)

<强>摘要

确实,使用PThread加快了我的代码。我的编程缺陷是在第一个pthread_join之后放置pthread_create以及我在参数上设置的公共计数器。在解决了这个问题后,我测试了我的并行代码以确定1亿个数字的素数,然后将其处理时间与串行代码进行比较。以下是结果。

http://i.stack.imgur.com/gXFyk.jpg(我无法附上图片,因为我还没有很多声誉,相反,我要包含一个链接)

我为每个试验进行了三次试验,以说明不同操作系统活动引起的变化。我们加快了使用PThread并行编程的速度。令人惊讶的是,在一个线程中运行的PThread代码比纯串行代码快一点。我无法解释这一点,但是使用PThreads很好,肯定值得一试。

以下是代码的更正并行版本(gcc-c ++):

# include <stdio.h>
# include <pthread.h>
# include <cmath>

# define NTHREADS 4

double asmSqrt(double x) 
{
  __asm__ ("fsqrt" : "+t" (x));
  return x;
}

struct start_end_f
{
    int start;
    int end;
};

//test if a number is prime
bool isPrime(int n)
{
    if (n <= 1) return false;
    if (n == 2) return true;
    if (n%2 == 0) return false;

    int sqrtn = asmSqrt(n);
    for (int i = 3; i <= sqrtn; i+=2) if (n%i == 0) return false;

    return true;
}

//executes the tests for prime in a certain range, other threads will test the next range and so on..
void *find_primes_in_range(void *in) 
{
    int k = 0;

    struct start_end_f *start_end_h = (struct start_end_f *)in;

    for (int j = start_end_h->start; j < (start_end_h->end +1); j++) 
    {
        if(isPrime(j) == 1) k++;
    }

    int *t = new int;
    *t = k;
    pthread_exit(t);
}

int main() 
{
    int f = 100000000; //maximum number to be tested for prime

    pthread_t thread_id[NTHREADS];
    struct start_end_f start_end[NTHREADS];

    int rem = (f+1)%NTHREADS;
    int n = (f+1)/NTHREADS;
    int rem_change = rem;
    int m;

    if(rem>0) m = n+1;
    else if(rem == 0) m = n;

    //distributes task 'evenly' to the number of parallel threads requested
    for(int h = 0; h < NTHREADS; h++)
    {
        if(rem_change > 0)
        {
            start_end[h].start = m*h;
            start_end[h].end = start_end[h].start+m-1;
            rem_change -= 1;
        }
        else if(rem_change<= 0)
        {
            start_end[h].start = m*(h+rem_change)-rem_change*n;
            start_end[h].end = start_end[h].start+n-1;
            rem_change -= 1;
        }
        pthread_create(&thread_id[h], NULL, find_primes_in_range, &start_end[h]);
    }   

    //retreiving returned values
    int *t;
    int c = 0;
    for(int h = 0; h < NTHREADS; h++)
    {
        pthread_join(thread_id[h], (void **)&t);
        int b = *((int *)t);
        c += b;
        b = 0;
    }

    printf("\nNumber of Primes: %d\n",c);
    return 0;
}
相关问题