几次操作后多线程性能下降

Posted

技术标签:

【中文标题】几次操作后多线程性能下降【英文标题】:Multithread performance drops down after a few operations 【发布时间】:2015-06-13 08:23:01 【问题描述】:

我在 linux 上的一个 c++ 多线程程序中遇到了这个奇怪的错误。多线程部分基本上执行一个循环。一次迭代首先加载一个包含一些特征的 sift 文件。然后它根据树查询这些特征。由于我有很多图像,因此我使用了多个线程来进行此查询。这是代码sn-ps。

struct MultiMatchParam

    int thread_id;
    float *scores;
    double *scores_d;
    int *perm;
    size_t db_image_num;
    std::vector<std::string> *query_filenames;
    int start_id;
    int num_query;
    int dim;
    VocabTree *tree;
    FILE *file;
;

// multi-thread will do normalization anyway
void MultiMatch(MultiMatchParam &param)

    // Clear scores
    for(size_t t = param.start_id; t < param.start_id + param.num_query; t++)
    
        for (size_t i = 0; i < param.db_image_num; i++)
            param.scores[i] = 0.0;

        DTYPE *keys;
        int num_keys;

        keys = ReadKeys_sfm((*param.query_filenames)[t].c_str(), param.dim, num_keys);

        int normalize = true;
        double mag = param.tree->MultiScoreQueryKeys(num_keys, normalize, keys, param.scores);

        delete [] keys;
    

我在 8 核 cpu 上运行它。起初它运行完美,所有 8 个内核的 cpu 使用率接近 100%。在每个线程查询了几张图片(大约 20 张图片)之后,性能(cpu 使用率)突然急剧下降,在所有八个内核中下降到大约 30%。

我怀疑这个错误的关键在于这行代码。

double mag = param.tree->MultiScoreQueryKeys(num_keys, normalize, keys, param.scores);

因为如果我用另一个代价高昂的操作(例如,包含 sqrt 的大型 for 循环)替换它。 cpu 使用率始终接近 100%。这个 MultiScoreQueryKeys 函数对树执行复杂的操作。由于所有八个内核都可能读取同一棵树(对这棵树没有写操作),我想知道读操作是否具有某种阻塞效应。但它不应该有这种效果,因为我在这个函数中没有写操作。循环中的操作也基本相同。如果要阻止 cpu 使用,它会在最初的几次迭代中发生。如果您需要查看此功能的详细信息或此项目的其他部分,请告诉我。

【问题讨论】:

您的代码包含一堆可能不相关的内容,而其他部分则丢失了。根据***.com/help/how-to-ask提取一个最小的例子。 使用分析器检查那里发生了什么怎么样? 分析器应该有助于缩小范围。但是考虑到行为的性质,似乎在某个地方存在线程锁,代码运行时间越长,在锁中花费的时间越多,运行时间越长,争用越多,cpu 使用率越低。所以我会在某个地方寻找一个共享资源/锁,在锁内运行的时间越长,积累的工作就越多。 至于树是非阻塞的,如果它对读写都是线程安全的,那不一定是真的。在这种情况下,即使是直接读取也可能涉及锁定整个树(对于树来说不是一个非常有效的多线程设计,但并非不常见)。如果是这种情况,可能值得为每个线程保留一个单独的树副本(如果这足够便宜的话)。 【参考方案1】:

使用 std::async() 代替 zeta::SimpleLock 锁

【讨论】:

这个锁是我们自己的实现,与性能下降无关。即使我注释了“锁定”部分(写入文件),经过多次迭代后,cpu 使用率仍会急剧下降。

以上是关于几次操作后多线程性能下降的主要内容,如果未能解决你的问题,请参考以下文章

如何理解node是单线程异步I/O

26 多线程——线程安全 synchronized

将 GetDC 代码移动到线程中时性能大幅下降

fread OpenMP 线程中的性能下降

Java多线程之线程池

多线程编程