0

让我首先说这是为了学校,但我真的不需要帮助,我只是因为我得到的一些结果而感到困惑。C-pthreads似乎只使用一个核心

我有一个简单的程序,使用辛普森的规则逼近pi,在一个任务中,我们必须通过产生4个子进程来完成此任务,现在在这个任务中我们必须使用4个内核级线程。我已经这样做了,但是当我计划使用子进程的时候,运行速度似乎更快(我得到的印象我应该会看到相反的结果)。

下面是使用并行线程的程序:

#include <stdio.h> 
#include <unistd.h> 
#include <pthread.h> 
#include <stdlib.h> 

// This complicated ternary statement does the bulk of our work. 
// Basically depending on whether or not we're at an even number in our 
// sequence we'll call the function with x/32000 multiplied by 2 or 4. 
#define TERN_STMT(x) (((int)x%2==0)?2*func(x/32000):4*func(x/32000)) 

// Set to 0 for no 100,000 runs 
#define SPEED_TEST 1 

struct func_range { 
    double start; 
    double end; 
}; 

// The function defined in the assignment 
double func(double x) 
{ 
    return 4/(1 + x*x); 
} 

void *partial_sum(void *r) 
{ 
    double *ret = (double *)malloc(sizeof(double)); 
    struct func_range *range = r; 
#if SPEED_TEST 
    int k; 
    double begin = range->start; 
    for (k = 0; k < 25000; k++) 
    { 
    range->start = begin; 
    *ret = 0; 
#endif 
    for (; range->start <= range->end; ++range->start) 
     *ret += TERN_STMT(range->start); 
#if SPEED_TEST 
    } 
#endif 

    return ret; 
} 

int main() 
{ 
    // An array for our threads. 
    pthread_t threads[4]; 
    double total_sum = func(0); 
    void *temp; 
    struct func_range our_range; 
    int i; 

    for (i = 0; i < 4; i++) 
    { 
    our_range.start = (i == 0) ? 1 : (i == 1) ? 8000 : (i == 2) ? 16000 : 24000; 
    our_range.end = (i == 0) ? 7999 : (i == 1) ? 15999 : (i == 2) ? 23999 : 31999; 
    pthread_create(&threads[i], NULL, &partial_sum, &our_range); 
    pthread_join(threads[i], &temp); 
    total_sum += *(double *)temp; 
    free(temp); 
    } 

    total_sum += func(1); 

    // Final calculations 
    total_sum /= 3.0; 
    total_sum *= (1.0/32000.0); 

    // Print our result 
    printf("%f\n", total_sum); 

    return EXIT_SUCCESS; 
} 

下面是一个使用子进程:

#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 

// This complicated ternary statement does the bulk of our work. 
// Basically depending on whether or not we're at an even number in our 
// sequence we'll call the function with x/32000 multiplied by 2 or 4. 
#define TERN_STMT(x) (((int)x%2==0)?2*func(x/32000):4*func(x/32000)) 

// Set to 0 for no 100,000 runs 
#define SPEED_TEST 1 

// The function defined in the assignment 
double func(double x) 
{ 
    return 4/(1 + x*x); 
} 

int main() 
{ 
    // An array for our subprocesses. 
    pid_t pids[4]; 
    // The pipe to pass-through information 
    int mypipe[2]; 
    // Counter for subproccess loops 
    double j; 
    // Counter for outer loop 
    int i; 
    // Number of PIDs 
    int n = 4; 
    // The final sum 
    double total_sum = 0; 
    // Temporary variable holding the result from a subproccess 
    double temp; 
    // The partial sum tallied by a subproccess. 
    double sum = 0; 
    int k; 

    if (pipe(mypipe)) 
    { 
    perror("pipe"); 
    return EXIT_FAILURE; 
    } 

    // Create the PIDs 
    for (i = 0; i < 4; i++) 
    { 
    // Abort if something went wrong 
    if ((pids[i] = fork()) < 0) 
    { 
     perror("fork"); 
     abort(); 
    } 
    else if (pids[i] == 0) 
    // Depending on what PID number we are we'll only calculate 
     // 1/4 the total. 
#if SPEED_TEST 
     for (k = 0; k < 25000; ++k) 
     { 
     sum = 0; 
#endif 
     switch (i) 
     { 
      case 0: 
      sum += func(0); 
      for (j = 1; j <= 7999; ++j) 
       sum += TERN_STMT(j); 
      break; 
      case 1: 
      for (j = 8000; j <= 15999; ++j) 
       sum += TERN_STMT(j); 
      break; 
      case 2: 
      for (j = 16000; j <= 23999; ++j) 
       sum += TERN_STMT(j); 
      break; 
      case 3: 
      for (j = 24000; j < 32000; ++j) 
       sum += TERN_STMT(j); 
      sum += func(1); 
      break; 
     } 
#if SPEED_TEST 
     } 
#endif 
     // Write the data to the pipe 
     write(mypipe[1], &sum, sizeof(sum)); 
     exit(0); 
    } 
    } 

    int status; 
    pid_t pid; 
    while (n > 0) 
    { 
    // Wait for the calculations to finish 
    pid = wait(&status); 
    // Read from the pipe 
    read(mypipe[0], &temp, sizeof(total_sum)); 
    // Add to the total 
    total_sum += temp; 
    n--; 
    } 

    // Final calculations 
    total_sum /= 3.0; 
    total_sum *= (1.0/32000.0); 

    // Print our result 
    printf("%f\n", total_sum); 

    return EXIT_SUCCESS; 
} 

这里是并行线程版本time结果运行10万次:

real 11.15 
user 11.15 
sys 0.00 

这里是子进程版本:

real 5.99 
user 23.81 
sys 0.00 

拥有23.81的用户时间意味着这是每个内核执行代码所花时间的总和。在pthread分析中,实际/用户时间相同,这意味着仅使用一个内核。为什么不使用全部4核?我认为默认情况下它可能比子进程更好。

希望这个问题是有道理的,这是我第一次用pthread编程,而且我对OS级编程一般都很陌生。

感谢您花时间阅读这个冗长的问题。

回答

2

当您在pthread_create之后立即说出pthread_join时,您将有效地序列化所有线程。直到创建完所有线程并完成了所有不需要线程计算结果的其他工作之后,才能加入线程。

+0

我将for循环外面的'pthread_join'移动到了它自己的for循环中,并添加了free和free,但是现在每次都有不同的答案......显然,我并不了解线程的一些内容,是有什么我能读懂的,展示了如何解决这类问题? – LainIwakura

+0

@LainIwakura:你的意思是“请解释多线程编程如何在双行注释中起作用”?恐怕这有点超出范围。你的班级没有教这个吗? –

+0

我讨厌说这个,但是这个班很烂。我想出了为什么我得到了不同的值 - 线程共享范围结构。我通过建立一系列结构来解决这个问题......现在我得到了正确的答案,但程序更慢了! (16.52实时,65.05用户时间!) 它看起来像我的核心正在使用,但性能没有超过程序的版本使用fork() – LainIwakura

相关问题