以编程方式并行化 C 程序的一部分

programmatically parallelizing portions of a C program

我有一个小的 number-c运行ching 部分更大的程序,它显示为执行瓶颈;它计算数据序列并将其写入各个缓冲区的任意位置,并对结果求和。通过监控 cpu 性能,我可以看到所有内容都在一个 cpu 核心上执行,而其余核心保持空闲状态。

我该怎么做才能确保所有处理器内核不会在其他处理器内核闲置时结束?

是否有一个示例说明如何在 OSX(Darwin UNIX 或 BSD)中处理此问题,而不必使用 Cocoa 代码 and/or Apple(或任何其他) 专有库?

这是我想并行化的代码,简化为两段工作,以便于表达我的观点。函数 work1A()work1B() 可能 运行 在两个不同的 cpu 核心上并行,因为发生在work1B() 不依赖于 work1A() 中发生的事情,反之亦然。他们只共享最后三个参数,这些参数不会被修改:

//___________________________________________________
void work1 (Float32 start, Float32 len, Float32 R, parameters** params)
{
long values = (long)R*len;
Float32* fBuffer;
Float32* fBuffA;
Float32* fBuffB;  

fBuffer = calloc(values, sizeof(Float32));
fBuffA  = calloc(values, sizeof(Float32));
fBuffB  = calloc(values, sizeof(Float32));

//begin of parallelizable code
work1A(fBuffA, start, len, params);
work1B(fBuffB, start, len, params);
//end of parallelizable code

for(long val = 0; val < values; val++)
    fBuffer[val] = fBuffA[val] + fBuffB[val];

showResult(start, len, R, fBuffer);

free(fBuffA);
free(fBuffB);    
free (fBuffer);
return;
}
//___________________________________________________

我宁愿不必干扰程序的主要功能和其他被调用的功能,它们有自己的预定义线程策略。 RAM 使用不是主要问题。我希望不需要从头开始重写一个 20000 行的程序,仅仅因为这一小部分。提前致谢!

我正在努力改进 post。 感谢用户 dmg 指点我 OpenMP,并显示它在工作时是多么简单,我遗憾地发现 OS X/Darwin 具有仅部分 OpenMP 支持的长期且波动的历史。由于看不出我如何负担得起让这段历史变得更好的贡献,我也在考虑一个不同的场景,使用 p_threads.

这里遇到两个问题:

  1. 两个线程仍然不意味着系统会理解将这些线程并行放在两个内核上。

  2. 我不知道如何将四个参数传递给包含函数 work1()work2()[ 的每个线程=44=],其中两个恰好是数组,没有重写我程序的大部分内容,它处理内部数据表示。

您可以尝试 OpenMP,因为它相当简单且无干扰:

#pragma omp parallel default(none)
#pragma omp single 
{
    #pragma omp task
    work1A(fBuffA, start, len, params);

    #pragma omp task
    work1B(fBuffB, start, len, params);

    #pragma omp taskwait
}

如果你使用 gcc,只需用 -fopenmp 编译,添加 #include <omp.h>,然后执行以下操作:

$ export OMP_NUM_THREADS=2
$ ./myexe

更不用说,如果您在没有 -fopenmp 的情况下进行编译,您仍将获得有效的单线程代码。要检查您是否已正确编译,请拨打以下电话:

printf("Thread ID %d in work1A\n", omp_get_thread_num());

和:

printf("Thread ID %d in work1B\n", omp_get_thread_num());

看看你是不是真的 运行 两个不同的线程。而且只有 5 行代码和 2 个括号。

这里有一个可能的答案,它根据我自己的研究起作用,尽管它涉及按以下方式调整最初发布的源代码的一部分。为了阅读方便,我保持简单,但如果一段代码显然可以并行化,这很可能会解决它。可以很容易地从中导出更好的编程实践的更详细的代码。

#include <pthread.h>
//variables which shouldn't be declared on the stack!
float R, start, len;
Float32* fBuffA;
Float32* fBuffB; 
Float32* fBuffer;
//___________________________________________________
void work (Float32 start, Float32 len)
{
int result;
int num = 2;
pthread_t threads[num];
int thread_args[num];
int rc;
void* rp;    

long values = (long)R*len;

fBuffer = calloc(values, sizeof(Float32));
fBuffA  = calloc(values, sizeof(Float32));
fBuffB  = calloc(values, sizeof(Float32));

//begin of parallelizable code
rc = pthread_create(&threads[0], NULL, synLT, (void *) &thread_args[0]);
rc = pthread_create(&threads[1], NULL, synRT, (void *) &thread_args[1]);
rc = pthread_join(threads[0], &rp);
rc = pthread_join(threads[1], &rp);
//end of parallelizable code

for(long val = 0; val < values; val++)
    fBuffer[val] = fBuffA[val] + fBuffB[val];

result = fbshow(start, len);
free(fBuffA);
free(fBuffB);    
free (fBuffer);
return;
}

workA() 和 workB() 函数已按以下方式进行修改以符合 pthread 规范和语法:

//___________________________________________________
void *workA  (void *A)
{
int tid;
tid = *((int *) A);
doSomething();
int *ret = calloc(1,sizeof(int));
*ret = 42;
return (void*)ret;
}
//___________________________________________________
void *workB  (void *B)
{
int tid;
tid = *((int *) B);
doSomethingElse();
int *ret = calloc(1,sizeof(int));
*ret = 42;
return (void*)ret;
}