【问题标题】:Puzzling behaviour of async令人费解的异步行为
【发布时间】:2012-10-14 20:29:37
【问题描述】:

这可能是一些奇怪的 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;
}

【问题讨论】:

  • 10GB 虚拟还是真实?虚拟内存使用量很容易爆炸,但实际内存使用量不应该那么高。
  • @nneonneo 是的,虚拟就像 50GB :-D
  • 在 64 位操作系统上,虚拟内存并不稀缺。因此,尝试减少使用或跟踪使用了多少是没有意义的。 (至少,直到你对结核病大发雷霆。)

标签: c++ linux multithreading asynchronous c++11


【解决方案1】:

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

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

【讨论】:

  • 你试过运行代码吗?这实际上看起来不是问题。线程过多会成为问题,但即使将其减少到四个(在我的四边形上)实际上也不会使其性能更好。我什至重写了代码以提前优化调度块。
  • 但是看看这些数字:quora.com/… 对于这种问题,程序将花费大部分时间从主存读取输入,这是顺序的(DRAM 不允许并发访问)。
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-30
    • 2018-07-02
    • 2020-06-19
    • 2023-03-12
    • 1970-01-01
    • 2011-03-27
    • 2010-11-15
    相关资源
    最近更新 更多