多线程性能

时间:2011-06-02 10:07:04

标签: c++ multithreading performance boost-thread

我创建了一个非常简单的应用来弄清楚boost :: thread是如何工作的。 我发现这个测试的结果令人惊讶。 4个执行线程完成计算比1个线程快2倍。 我期待4倍的提升。 另一个问题是为什么8个线程没有带来任何性能提升?

我正在使用boost 1.46.1和VS2008。完整的源代码如下。 程序在Core i5 750机器上运行。

#include <iostream>
#include <vector>
#include <cmath>

#include <boost/thread.hpp>
#include <boost/timer.hpp>

typedef unsigned int uint;


struct Vector {
    float x, y, z;

    Vector() : x(0.f), y(0.f), z(0.f) {}

    float len() {
        return sqrtf(x*x + y*y + z*z);
    }

};


float norm(int a) {
    return float((a % 10) + 1) / 10.f;
}


void genVectors(std::vector<Vector>& examples) {
    srand(GetTickCount());

    for (uint i = 0; i < examples.size(); ++i) {
        examples[i].x = norm(rand());
        examples[i].y = norm(rand());
        examples[i].z = norm(rand());
    }

}

typedef std::vector<Vector> Data;
typedef Data::iterator DataIter;

typedef std::vector<float> Result;
typedef Result::iterator ResultIter;


struct Worker {
    Data   data;
    Result result;

    Worker(DataIter& dataStart,
           const DataIter& dataEnd,
           ResultIter& resultStart,
           const ResultIter& resultEnd) : data(dataStart, dataEnd), result(resultStart, resultEnd) {
        assert(data.size() == result.size());
    }

    void operator()() {
        DataIter di = data.begin();
        ResultIter ri = result.begin();

        const DataIter dend = data.end();

        for (; di != dend; ++di, ++ri) {
            *ri = di->len();
        }
    }
};


int main(int argc, char **argv) {
    const uint numThreads = 4;
    const uint seqLen = 13107200;

    std::vector<Vector> a;
    a.resize(seqLen);

    genVectors(a);  

    std::vector<float> singleThreadResult(a.size());
    assert(a.size() == singleThreadResult.size());

    boost::timer singleThreadTimer;
    for (uint i = 0; i < a.size(); ++i) {
        singleThreadResult[i] = a[i].len();
    }
    double singleThreadTime = singleThreadTimer.elapsed();

    std::vector<float> multiThreadResult(a.size());

    Worker* workers[numThreads];
    for (uint i = 0; i < numThreads; ++i) {
        uint chunkSize = seqLen / numThreads;
        assert(numThreads * chunkSize == seqLen);

        workers[i] = new Worker(a.begin() + i*chunkSize,
                                a.begin() + (i+1)*chunkSize,
                                multiThreadResult.begin() + i*chunkSize,
                                multiThreadResult.begin() + (i+1)*chunkSize);
    }

    boost::timer multiThreadTimer;
    boost::thread_group threads;
    for (uint i = 0; i < numThreads; ++i) {
        threads.create_thread(boost::ref(*workers[i]));
    }
    threads.join_all();
    double multiThreadTime = multiThreadTimer.elapsed();

    using namespace std;
    cout << "Single thread time: " << singleThreadTime << endl;
    cout << numThreads << " threads time: " << multiThreadTime << endl;

    return 0;
}

5 个答案:

答案 0 :(得分:2)

根据英特尔网站的说法,Core i5 750处理器有4个内核并支持4个线程,所以你不应该期望8个线程的性能比4个线程更多。通过为你的软件添加比你拥有的线程更多的线程处理器(或超线程)只是增加了更多的上下文切换开销。

至于为什么4个线程不快于2,我猜它是与工作数据集的大小有关。数据集比8MB缓存大得多,因此您的测试应用程序可能内存带宽有限。

要对此进行测试,请尝试使用适合缓存的数据集进行基准测试。

答案 1 :(得分:1)

您的Core i5 750计算机中可能有4个核心,但您仍然拥有单个数据总线。所有使用的数据(13107200 * 3 * sizeof(float)= 157 MB)必须通过此数据总线。然后有一个“仅仅”13107200 * sizeof(float)= 52 MB的结果向量,它采用相同的资源。所有这些都在缓存上很重,4个内核花费大量时间等待内存可用于读取或写入。

答案 2 :(得分:0)

我一直发现,对于在给定硬件配置上运行的给定问题,无法预测“最佳”线程数。我的方法是从命令行参数化线程数,并尝试各种数字,直到我达到“甜蜜点”。

答案 3 :(得分:0)

对于这样的场景,无论如何我更喜欢OpenMP #pragma parallel for,或者只是使用gcc -fopenmp -D_GLIBCXX_PARALLEL并且(可能)获得自动并行化......

答案 4 :(得分:0)

使用系统线程时,无法保证每个线程都在单独的核心上运行。您不能为线程分配核心 - 这是OS任务。鉴于您的应用程序中有4个线程,操作系统可以在单个核心上运行它们,具体取决于整体CPU负载和数十亿其他因素。