pthread 的工作负载真的需要以毫秒为单位才能使 pthread 受益吗?

问题描述 投票:0回答:1

我正在尝试衡量哪些工作负载的 pthread 变得有用。到目前为止,我发现工作负载需要大约 3 毫秒,pthread 才能对整体进度做出积极贡献(在 Alderlake 测试系统上)。

这是正确的数量级吗?

基准测试输出如下:

BM_dispatch<dispatch>/16/process_time/real_time                1.37 ms         1.37 ms          513
BM_dispatch<dispatch>/32/process_time/real_time                2.75 ms         2.75 ms          252
BM_dispatch<dispatch>/48/process_time/real_time                4.15 ms         4.15 ms          169
BM_dispatch<dispatch>/64/process_time/real_time                5.52 ms         5.52 ms          126
BM_dispatch<dispatch>/80/process_time/real_time                6.89 ms         6.89 ms          101
BM_dispatch<dispatch>/96/process_time/real_time                8.26 ms         8.26 ms           84
BM_dispatch<dispatch>/112/process_time/real_time               9.62 ms         9.62 ms           72

BM_dispatch<dispatch_pthread>/16/process_time/real_time        2.16 ms         4.18 ms          359
BM_dispatch<dispatch_pthread>/32/process_time/real_time        3.76 ms         7.38 ms          200
BM_dispatch<dispatch_pthread>/48/process_time/real_time        3.67 ms         7.18 ms          150
BM_dispatch<dispatch_pthread>/64/process_time/real_time        4.30 ms         8.44 ms          163
BM_dispatch<dispatch_pthread>/80/process_time/real_time        4.38 ms         8.60 ms          176
BM_dispatch<dispatch_pthread>/96/process_time/real_time        4.93 ms         9.69 ms          146
BM_dispatch<dispatch_pthread>/112/process_time/real_time       5.31 ms         10.5 ms          126

我在不同的工作负载大小下对两个函数

dispatch
dispatch_pthread
进行了基准测试。该函数执行相同的总工作,但
dispatch_pthreads
将工作分配给两个线程。当运行时间约为 1 毫秒时,pthreads 没有什么好处。大约 8 毫秒的工作负载,两个 pthread 的速度大约是单个线程的两倍。

完整程序如下:

void find_max(const float* in, size_t eles, float* out) {
    float max{0};
    for (size_t i = 0; i < eles; ++i) {
        if (in[i] > max) max = in[i];
    }
    *out = max;
}

float dispatch(const float* inp, size_t rows, size_t cols, float* out) {
    for (size_t row = 0; row < rows; row++) {
        find_max(inp + row * cols, cols, out + row);
    }
}

struct pthreadpool_context {
    const float* inp;
    size_t rows;
    size_t cols;
    float* out;
};

void* work(void* ctx) {
    const pthreadpool_context* context = (pthreadpool_context*)ctx;
    dispatch(context->inp, context->rows, context->cols, context->out);
    return NULL;
}

float dispatch_pthread(const float* inp, size_t rows, size_t cols, float* out) {
    pthread_t thread1, thread2;
    size_t rows_per_thread = rows / 2;
    const pthreadpool_context context1 = {inp, rows_per_thread, cols, out};
    const pthreadpool_context context2 = {inp + rows_per_thread * cols,
                                          rows_per_thread, cols,
                                          out + rows_per_thread};
    pthread_create(&thread1, NULL, work, (void*)&context1);
    pthread_create(&thread2, NULL, work, (void*)&context2);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
}


template <auto F>
void BM_dispatch(benchmark::State& state) {
    std::random_device rnd_device;
    std::mt19937 mersenne_engine{rnd_device()};
    std::normal_distribution<float> dist{0, 1};
    auto gen = [&]() { return dist(mersenne_engine); };
    const size_t cols = 1024 * state.range(0);
    constexpr size_t rows = 100;
    std::vector<float> inp(rows * cols);
    std::generate(inp.begin(), inp.end(), gen);
    std::vector<float> out(rows);
    for (auto _ : state) {
        F(inp.data(), rows, cols, out.data());
    }
}

BENCHMARK(BM_dispatch<dispatch>)
    ->DenseRange(16, 112, 16)
    ->MeasureProcessCPUTime()
    ->UseRealTime()
    ->Unit(benchmark::kMillisecond);
BENCHMARK(BM_dispatch<dispatch_pthread>)
    ->DenseRange(16, 112, 16)
    ->MeasureProcessCPUTime()
    ->UseRealTime()
    ->Unit(benchmark::kMillisecond);
BENCHMARK_MAIN();

程序在内核 5.15 的 Ubuntu 22.04 上使用 gcc 13.2.0 进行

O2
优化编译。

multithreading performance pthreads benchmarking
1个回答
0
投票

Linux下GLIBC的pthread维护着一个栈池。该池在进程启动时为空。当线程被创建然后终止时,堆栈不会被释放,而是保留在池中以供后续线程重用。默认堆栈大小为 8 MB 虚拟内存,池大小为 40 MB。这意味着默认情况下最多可以缓存 5 个堆栈以供重用。

因此,前两个线程的创建速度较慢,因为堆栈尚未分配。但对于后续的,这样更快。

因此,最好将线程创建放在基准测试之外。

© www.soinside.com 2019 - 2024. All rights reserved.