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

时间:2015-01-15 14:44:18

标签: c macos unix parallel-processing

我有一个小型的数字运算部分的更大的程序,这似乎是执行瓶颈;它计算并将数据序列写入各个缓冲区的任意位置,并对结果求和。通过监视cpu性能,我可以看到在一个cpu核心上执行的所有内容,而剩余的核心保持空闲状态。

我可以做些什么来确保所有人不会在同一个处理器核心中结束而其他人处于闲置状态?

是否有一个如何在OSX(Darwin UNIX或BSD)中处理此问题的示例,而不必使用Cocoa代码和/或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线路程序,仅仅是因为这一小部分。提前谢谢!

我正在尝试改进帖子。感谢用户 dmg 指示我 OpenMP ,并展示了如何简单它可以在它工作的时候,我遗憾地发现OS X / Darwin只有部分OpenMP支持的漫长历史。由于不知道我如何能够为改善这段历史做出贡献,我还在考虑使用 p_threads 的不同场景。

这里我遇到两个问题:

  1. 两个线程仍然没有暗示系统会理解将这些线程并行放在两个核心上。

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

2 个答案:

答案 0 :(得分:0)

您可以尝试使用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
}

如果您使用-fopenmp,请使用gcc进行编译,添加#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行代码。

答案 1 :(得分:0)

根据我自己的研究,这里有一个可能的答案,尽管它涉及以下列方式调整原始发布的源代码的一部分。为方便阅读,我保持简单,但如果一段代码显然可以并行化,这很可能会解决它。可以从中轻松地推导出更精细的编程实践代码。

#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;
}