【问题标题】:How to get multithreads working properly using pthreads and not boost in class using C++如何使用 pthreads 使多线程正常工作,而不是使用 C++ 在类中提升
【发布时间】:2012-11-28 15:44:23
【问题描述】:

我有一个项目,我在这里总结了一些伪代码来说明问题。 我没有编译器问题,无论是使用 boost 还是 pthreads,我的代码都能很好地编译。请记住,这是用于说明问题的伪代码,不能直接编译。

我遇到的问题是,对于多线程函数,内存使用和处理时间总是比使用串行编程(例如 for/while 循环)实现相同的函数要长。

这是我面临的问题的简化版本:

class aproject(){

public:

typedef struct
{
char** somedata;
double output,fitness;
}entity;

entity **entity_array;

int whichthread,numthreads;
pthread_mutex_t mutexdata;



aproject(){
numthreads = 100;
*entity_array=new entity[numthreads];
for(int i;i<numthreads;i++){
entity_array[i]->somedata[i] = new char[100];
}

/*.....more memory allocations for entity_array.......*/
this->initdata();
this->eval_thread();

}
void initdata(){
/**put zeros and ones in entity_array**/

}
float somefunc(char *somedata){

float output=countzero();       //someother function not listed

return output;
}

void* thread_function()
{
    pthread_mutex_lock (&mutexdata);

    int currentthread = this->whichthread;
    this->whichthread+=1;

        pthread_mutex_unlock (&mutexdata);

    entity *ent = this->entity_array[currentthread];


    double A=0,B=0,C=0,D=0,E=0,F=0;
    int i,j,k,l;



         A = somefunc(ent->somedata[0]);
         B = somefunc(ent->somedata[1]);

        t4 = anotherfunc(A,B);



        ent->output   = t4;
        ent->fitness  = sqrt(pow(t4,2)); 





                    }



static void* staticthreadproc(void* p){


return reinterpret_cast<ga*>(p)->thread_function();

}


void eval_thread(){

//use multithreading to evaluate individuals in parallel 

int i,j,k;
nthreads = this->numthreads;
pthread_t threads[nthreads];

//create threads
pthread_mutex_init(&this->mutexdata,NULL);
this->whichthread=0;

for(i=0;i<nthreads;i++){

pthread_create(&threads[i],NULL,&ga::staticthreadproc,this);

//printf("creating thread, %d\n",i);
}


//join threads
for(i=0;i<nthreads;i++){

pthread_join(threads[i],NULL);

}

}

};

我在这里使用 pthreads 是因为它在内存较少的机器上比 boost 效果更好。 每个线程都在 eval_thread 中启动并在那里终止。我正在使用互斥锁来确保每个线程都以 entity_array 的正确索引开始,因为每个线程仅将其工作应用于由变量 this->whichthread 索引的相应 entity_array。这个变量是唯一需要被互斥锁锁定的东西,因为它是为每个线程更新的,并且不能被其他线程更改。您可以愉快地忽略除 thread_function、eval_threads 和 staticthreadproc 之外的所有其他函数,因为它们是唯一相关的函数,假设除 init 之外的所有其他函数都是处理器和内存密集型的。

所以我的问题是,为什么以这种方式使用多线程在内存和速度方面比完全使用线程的传统方法更昂贵?

我必须重申代码是伪代码,问题不在于它是否会编译

谢谢,如果您对 pthread 和/或 boost 解决方案提出任何建议,我将不胜感激。

【问题讨论】:

    标签: c++


    【解决方案1】:

    每个线程都需要自己的调用堆栈,这会消耗内存。函数的每个局部变量(以及调用堆栈上的所有其他函数)都计入该内存。

    创建新线程时,为其调用堆栈保留空间。我不知道 pthreads 的默认值是什么,但您可能想研究一下。如果您知道所需的堆栈空间比默认保留的要少,则可以通过在生成线程时显式指定所需的堆栈大小来显着减少内存消耗。

    至于性能部分 - 它可能取决于几个问题。通常,您不应该期望将数字运算操作并行化到比您拥有的内核更多的线程上来提高性能(不知道这里是否是这种情况)。由于上下文切换的额外开销、缓存未命中数量的增加等原因,这最终可能会变慢。有一些方法可以对此进行分析,具体取决于您的平台(例如,the Visual Studio profiler can count cache-misses,还有@987654322 @ 也一样)。

    【讨论】:

    • 它使用与 for 循环相同的内存量,但需要大约两倍的时间。谢谢我在这里找到了设置堆栈大小的链接link
    • 编辑了我的答案以包含运行时问题。不过,这些都是猜测,也许其中的某些内容对您有所帮助。
    【解决方案2】:

    创建线程是一项相当昂贵的操作。如果每个线程只做非常少量的工作,那么你的程序可能会被创建它们所花费的时间所支配。此外,大量活动线程会增加调度它们所需的工作,从而降低系统性能。而且,正如另一个答案所提到的,每个线程都需要自己的堆栈内存,因此内存使用量会随着线程数的增加而增加。

    另一个问题可能是缓存失效;当一个线程将其结果写入其entity 结构时,它可能会使附近的缓存内存无效并强制其他线程从更高级别的缓存或主内存中获取数据。

    如果您使用较少数量的线程,每个线程处理较大的数据子集,您可能会获得更好的结果。对于这样的 CPU 密集型任务,每个 CPU 一个线程可能是最好的——这意味着您可以让所有 CPU 保持忙碌,并且无需浪费时间在每个 CPU 上调度多个线程。确保每个线程处理的实体都位于数组中,这样不会使其他线程缓存的实体无效。

    【讨论】:

    • 我想使用尽可能多的线程来执行成本高昂的功能,从而节省速度和内存。然而,这似乎是不可能的。但是我确实注意到,当使用 boost::thread 时,x 个线程的内存保持不变,而对于每个额外的循环,for/loop 替代方案的内存增加了。我想可能有一种优化线程的方法,但为此可能需要一些高级内存分配。
    • 如果内存已经分配给实体数组,那么在它们上运行的线程如何改变这一点?另外关于 cpu,我应该担心线程数与 cpu 的数量相同,毕竟多线程比双核更老。例如阿帕奇。我认为你的权利在于它是用于设置线程本身的内存/cpu,与 for/loop 相比,从长远来看,它不会节省时间或内存,尤其是对于许多线程而言。
    • 使用“尽可能多的线程”只会在所有线程都可以同时运行的情况下节省时间。一旦活动线程多于 CPU,其中一些必须等待,并且您不会获得进一步的速度提升 - 并且可能会因调度成本而损失一些。
    • “多线程比双核更早,例如 apache”——除了加速 CPU 密集型程序之外,多线程还有其他用途。在 I/O 绑定服务器的情况下,它允许您通过使用阻塞 I/O 操作(每个连接运行一个线程)来简化编程逻辑。但是,如果您想提高 CPU 密集型任务的速度,那么最好的办法就是在所有 CPU 上同时运行它,因此拥有比这更多的线程也无济于事。 (此外,多核服务器已经存在了很长时间,即使该技术最近才变得足够便宜,可以用于桌面。)
    • "如果内存已经分配给实体数组,那么在它们上运行的线程将如何改变这一点?"我没说会。我唯一关于内存的问题是(a)如果两个线程访问附近的数据,缓存失效可能会损害性能,并且(b)每个线程都需要一些内存来存储它的堆栈。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-18
    • 1970-01-01
    • 1970-01-01
    • 2012-07-05
    • 2021-04-19
    • 2014-03-05
    • 2012-06-16
    相关资源
    最近更新 更多