3

这可能是一些奇怪的 Linux 怪癖,但我观察到非常奇怪的行为。

以下代码应将求和数字的同步版本与异步版本进行比较。问题是我看到了性能提升(它不是缓存,即使我将代码分成两个单独的程序也会发生),同时仍将程序视为单线程(仅使用一个内核)。

strace确实显示了一些线程活动,但像top克隆这样的监视工具仍然只显示一个使用过的核心。

我观察到的第二个问题是,如果我增加生成率,内存使用量就会激增。线程的内存开销是多少?使用 5000 个线程,我得到 ~10GB 的内存使用量。

#include <iostream>
#include <random>
#include <chrono>
#include <future>
using namespace std;


long long sum2(const vector<int>& v, size_t from, size_t to)
{
    const size_t boundary = 5*1000*1000;

    if (to-from <= boundary)
    {
        long long rsum = 0;
        for (;from < to; from++)
        {
            rsum += v[from];
        }
        return rsum;
    }
    else
    {
        size_t mid = from + (to-from)/2;
        auto s2 = async(launch::async,sum2,cref(v),mid,to);

        long long rsum = sum2(v,from,mid);
        rsum += s2.get();
        return rsum;
    }
}

long long sum2(const vector<int>& v)
{
    return sum2(v,0,v.size());
}

long long sum(const vector<int>& v)
{
    long long rsum = 0;
    for (auto i : v)
    {
        rsum += i;
    }

    return rsum;
}

int main()
{
    const size_t vsize = 100*1000*1000;

    vector<int> x;
    x.reserve(vsize);

    mt19937 rng;
    rng.seed(chrono::system_clock::to_time_t(chrono::system_clock::now()));

    uniform_int_distribution<uint32_t> dist(0,10);

    for (auto i = 0; i < vsize; i++)
    {
        x.push_back(dist(rng));
    }

    auto start = chrono::high_resolution_clock::now();
    long long suma = sum(x);
    auto end = chrono::high_resolution_clock::now();

    cout << "Sum is " << suma << endl;
    cout << "Duration " << chrono::duration_cast<chrono::nanoseconds>(end - start).count() << " nanoseconds." << endl;

    start = chrono::high_resolution_clock::now();
    suma = sum2(x);
    end = chrono::high_resolution_clock::now();

    cout << "Async sum is " << suma << endl;
    cout << "Async duration " << chrono::duration_cast<chrono::nanoseconds>(end - start).count() << " nanoseconds." << endl;

    return 0;
}
4

2 回答 2

1

你检查过正在打印的时间吗?在我的机器上,-O2 处的串行时间低于 1 秒,而并行求和时间要快几倍。因此,完全有可能 CPU 使用时间不足以让“top”之类的东西注册,因为它们通常每秒只刷新一次。

如果您通过减少每线程计数来增加线程数,那么您有效地增加了线程管理的开销。如果您有 5000 个线程处于活动状态,那么您的任务将在额外内存中占用 5000* min-thread-stack-size。在我的机器上是 20Gb!

为什么不尝试增加源容器的大小?如果您使并行部分花费的时间足够长,您将看到相应的并行 CPU 使用情况。但是,请做好准备:对整数求和很快,生成随机数所花费的时间可能比将数字相加的时间长一个或两个数量级。

于 2012-10-15T07:33:00.447 回答
1

也许您观察到正在使用一个内核,因为同时工作的线程之间的重叠太短而无法察觉。在现代硬件上,从一个连续的内存区域对 500 万个值求和应该非常快,所以当父母完成求和时,孩子可能刚刚开始,父母可能大部分时间或所有时间都在等待孩子的结果。您是否尝试增加工作单元以查看重叠是否变得明显?

关于性能提升:即使由于工作单元太小而导致线程之间的重叠为 0,多线程版本仍然可以受益于额外的 L1 缓存内存。对于这样的测试,内存可能会成为瓶颈,顺序版本将仅使用一个 L1 缓存,而多线程版本将使用与内核一样多的内核。

于 2012-10-14T22:25:59.743 回答