将数据从一个线程传递到另一个线程的最快方法

时间:2015-04-08 06:42:03

标签: c++ multithreading performance boost lock-free

我使用boost spsc_queue将我的东西从一个线程移动到另一个线程。它是我软件中的关键位置之一,所以我希望尽快完成。我写了这个测试程序:

#include <boost/lockfree/spsc_queue.hpp>
#include <stdint.h>

#include <condition_variable>
#include <thread>

const int N_TESTS = 1000;

int results[N_TESTS];

boost::lockfree::spsc_queue<int64_t, boost::lockfree::capacity<1024>> testQueue;

using std::chrono::nanoseconds;
using std::chrono::duration_cast;

int totalQueueNano(0);
int totalQueueCount(0);

void Consumer() {
    int i = 0;
    int64_t scheduledAt;
    while (i < N_TESTS - 1) {
        while (testQueue.pop(scheduledAt)) {
            int64_t dequeuedAt = (duration_cast<nanoseconds>(
                    std::chrono::high_resolution_clock::now().time_since_epoch())).count();
            auto diff = dequeuedAt - scheduledAt;
            totalQueueNano += diff;
            ++totalQueueCount;
            results[i] = diff;
            ++i;
        }
    }
    for (int i = 0; i < N_TESTS; i++) {
        printf("%d ", results[i]);
    }
    printf("\nspsc_queue latency average nano = %d\n", totalQueueNano / totalQueueCount);
}

int main() {
    std::thread t(Consumer);
    usleep(1000000);
    for (int i = 0; i < N_TESTS; i++) {
        usleep(1000);
        int64_t scheduledAt = (duration_cast<nanoseconds>(
                std::chrono::high_resolution_clock::now().time_since_epoch())).count();
        testQueue.push(scheduledAt);
    }
    usleep(1000000);
    return 0;
}

编译标志:

g++ -std=c++0x -O3 -Wall -c -fmessage-length=0 -march=native -mtune=native -pthread -MMD -MP -MF"src/TestProject.d" -MT"src/TestProject.d" -o "src/TestProject.o" "../src/TestProject.cpp"

g++ -pthread -o "TestProject"  ./src/TestProject.o   -lpthread

在我的机器上:RHEL 7.1,gcc 4.8.3,Xeon E5-2690 v3我收到290-300纳秒。

  • 我的测试应用程序有多好?我是否正确衡量&#34; spsc_queue&#34;延迟?
  • 当前行业最佳时间将数据从一个线程传递到另一个线程?
  • 使用boost spsc_queue将数据从一个线程移动到另一个线程是不错的选择?
  • 你能推荐比spsc_queue更快的东西吗?
  • 你能编写一个能快得多地完成同样工作的代码吗?
需要

upd:队列机制。如果第一个线程每1000纳秒产生一次数据,但第二个线程花费10 000纳秒来处理我需要的单个项目&#34; queue&#34;短时间内的几个项目。但我的排队&#34;从来没有&#34;太大&#34;。固定大小的短环缓冲区必须足够。

upd2 所以简而言之,问题是 - 什么是最快的单一生产者单一消费者队列(最有可能基于固定大小的环形缓冲区)?我使用boost spsc_queue并且达到~300 ns的延迟,你能更快地提出建议吗?

java世界中的 upd3 有一个干扰器可以实现50 ns的延迟https://code.google.com/p/disruptor/wiki/PerformanceResults我们在c ++中有一些具有相同50 ns延迟的东西吗?

3 个答案:

答案 0 :(得分:6)

由于您有int s,所以您(理想情况下)衡量的是调用push()pop()返回true之间的整体延迟。

这没有意义:消费者线程忙于轮询队列,即循环并忙着检查pop是否已获取价值。

  • 这很浪费,
  • 如果你想最大程度地减少延迟,那么轮询当然不是 的方式

如果(IFF)你希望最小化延迟(对于单个项目),我的 guess 将使用信令同步机制spsc_queue,据我所知,没有规定这个。 (您需要一个容器或自定义解决方案,您需要使用condition variable /事件,...)

但是,如果(IFF),您希望最大化吞吐量(每次的项目数),那么测量&#34;唤醒的延迟&#34;一个(单个)项目确实没有意义。在这种情况下,您希望充分利用您拥有的并行性,如is mentioned in a comment

  

传递数据的最快方法通常是为每个数据块使用一个线程。也就是说,只使用数据中存在的并行性。


解决你的要点:

  • 测试应用程序有多好:我觉得它没有多大意义。

    • 当你从一个线程写入并从另一个线程读取时,需要在原子中使用scheduledAt。否则你有UB。
    • 显然任何测量差异都可以。这纯粹是一个测量误差,并没有说明固有延迟。 (您可以尝试将聚合struct {int val; int64_t time; };放入队列,从而避免使用原子围栏。
  • 当前行业最佳时间:没有头绪。不确定是否有人关心这一点。 (也许在某些内核中?)

  • 选择spsc_queue :我认为这不是一个好选择,因为它需要投票。

  • 比spsc_queue更快?:见上文。使用非投票通知。

  • 编写一个代码,可以更快地完成相同的工作吗?:不。或者更确切地说,我不会。 =&GT;

引用"man"s answer

  
      
  1. 您定义问题并选择适当的同步机制
  2.   

您的问题是没有问题定义

到目前为止,我担心在常规操作系统上的用户登陆过程中,跨线程通知延迟似乎完全无关紧要。 您的用例是什么?

答案 1 :(得分:2)

首先,编写这样的测试程序是完全没用的。您不对数据进行任何处理,因此结果会有所偏差。其次,您的测试是在推送之间使用usleep() - 以此速率,您可以使用任何类型的同步原语。 你的消费者()似乎也永远不会退出...

您实现此类事情的方式如下:

  1. 定义问题并选择适当的同步机制
  2. 您实施该软件
  3. 您分析软件以识别潜在的热点
  4. 根据上一步的结果进行优化并重复。

您在第一步需要一些以前的经验,或者您可以尝试实施不同的方法,看看什么效果最好。

答案 2 :(得分:0)

它取决于应用程序的语义以及涉及的线程数。到目前为止,您正在研究原始延迟。使用更多线程,扩展也可能开始成为一个有趣的指标。

对于双线程情况,如果您对检索到的数据执行的操作,对单个位置(最好是在任何其他操作未触及的缓存行中)的原子更新可能会更快允许它。