并行比串行慢

计算科学 并行计算
2021-12-05 09:10:55

问题

大家好,我有一个程序(来自网络),我打算通过使用将其转换为并行版本来加快速度,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,HDD:340 Gb,

1个回答

部分问题是有一个线程可以处理所有数字:

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++)
  {
...

请注意您如何正确初始化j,然后立即将其再次设置为零。换句话说,每个线程应该在哪里做间隔[n,m),而不是他们做[0,m)然后,您的最后一个线程会执行所有素数,当然不会比顺序程序快。您当然应该看到这一点,因为您在最后打印的素数数量对于顺序版本和并行版本是不同的。

另一个问题是您的代码如下所示:

 for(int h = 0; h < NTHREADS; h++)
  {
    ...
    pthread_create(&thread_id[h], NULL, &parallel_launcher, (void *)&args);
    pthread_join(thread_id[h], NULL);

换句话说,您创建一个线程,然后在启动下一个线程之前立即等待它完成。这意味着您永远不会有两个线程并行运行——因此无法预期加速。