Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 多线程程序的负加速_C++_Multithreading_Performance_Multicore - Fatal编程技术网

C++ 多线程程序的负加速

C++ 多线程程序的负加速,c++,multithreading,performance,multicore,C++,Multithreading,Performance,Multicore,在装有英特尔奔腾双核处理器T2370(Acer Extensa)的笔记本电脑上,我运行了一个简单的多线程加速测试。我正在使用Linux。代码粘贴在下面。虽然我预计会加速2-3倍,但我惊讶地看到减速了2倍。我尝试了同样的gcc优化级别-O0-O3,但每次我都得到同样的结果。我正在使用pthreads。我也尝试了同样的方法,只使用了两个线程(而不是代码中的3个线程),但性能相似 原因可能是什么?更快的版本花费了相当长的时间——大约20秒——所以这似乎不是启动开销的问题 注意:这段代码有很多错误(实际

在装有英特尔奔腾双核处理器T2370(Acer Extensa)的笔记本电脑上,我运行了一个简单的多线程加速测试。我正在使用Linux。代码粘贴在下面。虽然我预计会加速2-3倍,但我惊讶地看到减速了2倍。我尝试了同样的gcc优化级别-O0-O3,但每次我都得到同样的结果。我正在使用pthreads。我也尝试了同样的方法,只使用了两个线程(而不是代码中的3个线程),但性能相似

原因可能是什么?更快的版本花费了相当长的时间——大约20秒——所以这似乎不是启动开销的问题

注意:这段代码有很多错误(实际上,它没有多大意义,因为串行和并行版本的输出会有所不同)。其目的只是为了“获得”相同数量指令的加速比

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

class Thread{
    private:
            pthread_t thread;
            static void *thread_func(void *d){((Thread *)d)->run();}
    public:
            Thread(){}
            virtual ~Thread(){}

            virtual void run(){}
            int start(){return pthread_create(&thread, NULL, Thread::thread_func, (void*)this);}
            int wait(){return pthread_join(thread, NULL);}
};


#include <iostream>

const int ARR_SIZE = 100000000;
const int N = 20;
int arr[ARR_SIZE];

int main(void)
{

    class Thread_a:public Thread{
            public:
                    Thread_a(int* a): arr_(a) {}
                    void run()
                    {
                            for(int n = 0; n<N; n++)
                            for(int i=0; i<ARR_SIZE/3; i++){ arr_[i] += arr_[i-1];}
                    }
            private:
                    int* arr_;
    };
    class Thread_b:public Thread{
            public:
                    Thread_b(int* a): arr_(a) {}
                    void run()
                    {
                            for(int n = 0; n<N; n++)
                            for(int i=ARR_SIZE/3; i<2*ARR_SIZE/3; i++){ arr_[i] += arr_[i-1];}
                    }
            private:
                    int* arr_;
    };

    class Thread_c:public Thread{
            public:
                    Thread_c(int* a): arr_(a) {}
                    void run()
                    {
                            for(int n = 0; n<N; n++)
                            for(int i=2*ARR_SIZE/3; i<ARR_SIZE; i++){ arr_[i] += arr_[i-1];}
                    }
            private:
                    int* arr_;
    };

    {
            Thread *a=new Thread_a(arr);
            Thread *b=new Thread_b(arr);
            Thread *c=new Thread_c(arr);

            clock_t start = clock();

            if (a->start() != 0) {
                    return 1;
            }

            if (b->start() != 0) {
                    return 1;
            }
            if (c->start() != 0) {
                    return 1;
            }

            if (a->wait() != 0) {
                    return 1;
            }

            if (b->wait() != 0) {
                    return 1;
            }

            if (c->wait() != 0) {
                    return 1;
            }

            clock_t end = clock();
            double duration = (double)(end - start) / CLOCKS_PER_SEC;

            std::cout << duration << "seconds\n";
            delete a;
            delete b;

    }
    {
            clock_t start = clock();
            for(int n = 0; n<N; n++)
            for(int i=0; i<ARR_SIZE; i++){ arr[i] += arr[i-1];}
            clock_t end = clock();
            double duration = (double)(end - start) / CLOCKS_PER_SEC;

            std::cout << "serial: " << duration << "seconds\n";
    }

    return 0;
  }
#包括
#包括
#包括
#包括
类线程{
私人:
pthread\u t线程;
静态void*thread_func(void*d){((thread*)d)->run();}
公众:
线程(){}
虚拟~Thread(){}
虚拟空运行(){}
int start(){return pthread_create(&thread,NULL,thread::thread_func,(void*)this);}
int wait(){return pthread_join(thread,NULL);}
};
#包括
const int ARR_SIZE=100000000;
常数int N=20;
int arr[arr_SIZE];
内部主(空)
{
类线程\u a:公共线程{
公众:
线程a(int*a):arr(a){
无效运行()
{

对于(int n=0;n您的线程所做的唯一事情就是添加一些元素,因此您的应用程序应该是IO绑定的。当您添加额外的线程时,您有两个CPU共享内存总线,因此它不会更快,相反,您会有缓存未命中等。

线程将您带到速度提升的承诺之地(TM)当您有一个合适的向量实现时。这意味着您需要:

  • 算法的适当并行化
  • 一种编译器,它知道并可以将算法作为并行过程在硬件上展开
  • 并行化的硬件支持
很难想出第一个。您需要能够有冗余,并确保它不会影响您的性能,正确合并数据以处理下一批数据等等

但这只是一种理论观点


当您只有一个处理器和一个糟糕的算法时,运行多个线程不会给您带来太多好处。请记住,只有一个处理器,所以您的线程必须等待一个时间片,实际上您正在进行顺序处理。

正如其他人所指出的,线程不一定能提高速度。在本部分中例如,在每个线程中花费的时间远远少于执行上下文切换和同步所需的时间。

我认为,您的算法本质上使您的缓存变得无用


可能你看到的是(非)的影响三个线程之间引用的局部性。本质上是因为每个线程都在与其他线程大范围分离的不同数据段上运行,所以当一个线程的数据段替换缓存中另一个线程的数据段时,会导致缓存未命中。如果您的程序构造为使线程在扇区I上运行更小(以便它们都可以保存在内存中)或更紧密(以便所有线程都可以在缓存页中使用相同的)的数据集,您将看到性能提升。因此,我怀疑您的速度减慢是因为必须从主内存而不是缓存中满足大量内存引用。

与线程问题无关,但代码中存在边界错误。 你有:

for(int i=0; i<ARR_SIZE; i++){ arr[i] += arr[i-1];}

您报告的时间是使用时钟功能测量的:

该函数返回程序使用的处理器时间的近似值

对于多处理器任务,实时时间会更少,但处理器时间通常会更长

当您使用多个线程时,工作可能由多个处理器完成,但工作量是相同的,此外还可能存在一些开销,例如争夺有限的资源。
clock()
测量处理器的总时间,即工作+任何争用开销。因此,在单个线程中执行工作时,处理器的总时间不应小于处理器的时间

从这个问题上很难判断您是否知道这一点,并且很惊讶,
clock()
返回的值是单个线程的两倍,而不是仅仅多一点,或者您希望它会少一点

改用(您将需要实时库librt,
g++-lrt
等)提供:

这仍然不如人们所希望的那么快,但至少这些数字是有意义的


100000000*20/2.5s=800Hz,总线频率为1600MHz,因此我怀疑每次迭代都有读写操作(假设有一些缓存),正如tstenner建议的那样,内存带宽是有限的,
clock()
值显示大部分时间您的处理器都在等待数据。(有人知道
clock()
时间包括这样的暂停吗?

特纳基本上是对的

这主要是操作系统“分配并映射新页面”算法的基准。该数组分配分配800MB的虚拟内存;操作系统在需要之前不会实际分配真正的物理内存。“分配并映射新页面”通常由互斥锁保护,因此更多的内核将无济于事

您的基准测试还强调了内存总线(传输的最小内存为800MB;在OSs上,在他们给您之前,内存为零,最坏的情况是800MB*7次传输)
arr[0] += arr[-1];
$ time bin/amit_kumar_threads.cpp
6.62seconds
serial: 2.7seconds

real    0m5.247s
user    0m9.025s
sys 0m0.304s
$ time bin/amit_kumar_threads.cpp
2.524 seconds
serial: 2.761 seconds

real    0m5.326s
user    0m9.057s
sys 0m0.344s