【问题标题】:Implementation of a simple unit test for lock-free queue实现无锁队列的简单单元测试
【发布时间】:2014-08-29 07:00:23
【问题描述】:

我正在尝试比较不同无锁队列的性能,因此,我想创建一个单元测试 - 其中包括将用户定义的预构建对象推入和弹出队列。因此,我想问你几个问题:- 1)如何以简单的方式创建预建对象。像我一样创建一个数组是否可以达到目的。 2) 我收到一个错误“在抛出 'std::system_error' what() 的实例后调用终止:无效参数已中止(核心转储)”。

提前感谢。

#include <cstdlib>
#include <stdio.h>
#include <string>
#include <chrono>
#include <iostream>
#include <ctime>
#include <atomic>
#include <thread>
#include <boost/lockfree/queue.hpp>

using namespace std;

const long NUM_DATA = 10;
const int NUM_PROD_THREAD = 2;
const int NUM_CONSUM_THREAD = 2;
const long NUM_ITEM = 1000000;


class Data
{
public:
    Data(){}
    void dataPrint() {cout << "Hello";}
private:
    long i;
    double j;
};


Data *DataArray = new Data[NUM_DATA];
boost::lockfree::queue<Data*> BoostQueue(1000);

struct Producer
{
    void operator()()
    {
        for(long i=0; i<1000000; i++)
            BoostQueue.push( DataArray );
    }
};


struct Consumer
{
    Data *pData;
    void operator()()
    {
        while (  BoostQueue.pop( pData ) ) ;
    }
};


int main(int argc, char** argv)
{
    std::thread thrd [NUM_PROD_THREAD + NUM_CONSUM_THREAD];

    std::chrono::duration<double> elapsed_seconds;

    auto start = std::chrono::high_resolution_clock::now();
    for ( int i = 0; i < NUM_PROD_THREAD;  i++ )
    {
        thrd[i] = std::thread{ Producer() };
    }

    for ( int i = 0; i < NUM_CONSUM_THREAD; i++ )
    {
        thrd[NUM_PROD_THREAD+i] = std::thread{Consumer()};
    }

    for ( int i = 0; i < NUM_CONSUM_THREAD; i++ )
    {
        thrd[i].join();
    }

    auto end = std::chrono::high_resolution_clock::now();
    elapsed_seconds = end - start;
    std::cout << "Enqueue and Dequeue 1 million item in:" << elapsed_seconds.count() << std::endl;

    for ( int i = 0; i < NUM_PROD_THREAD; i++ )
    {
        thrd[i].join();
    }

    return 0;
}

【问题讨论】:

  • 我不明白您为什么会崩溃 - 尝试添加一些跟踪或使用调试器。您根本没有使用 Data 数组......只是推送和弹出指向它的指针。对于基准测试,也许只需推送连续的数字并让消费者将它们相加,当您join 时获得每个线程的总数。您也可以从argc 开始数字,这样编译器就不会有任何期望。我喜欢在我的基准测试中看到工作的证据,这样我就知道代码做了预期的事情,并且不能在编译时被优化掉或完成。
  • 我得到的输出如下:- 在抛出 'std::system_error' what() 的实例后调用入队和出队 100 万个项目:0.145872 终止调用:无效参数中止(核心转储)。但这个错误让我担心。另外,在队列中推送数字对性能的影响是否与自定义对象一样?
  • 哦 - 崩溃一定是因为您已经加入了相同的线程 id...您在加入消费者线程时忘记在数组索引上使用 NUM_PROD_THREAD+。您的用户定义对象数组当前未在生产/消费分析期间使用,即使数组元素构造在main() 的第一行执行之前静态发生。推送int 与推送任何类型的指针将花费相似的时间……与无锁队列操作相比,做一点添加不会增加太多。您可以单独分析添加 1 个线程所需的时间。
  • 谢谢!我花了一整天的时间试图消除这个错误:D。最后一个问题。我想我明白你关于推动 int 与指针的说法。但是如果我想将预先构建的对象推送到队列中。我怎样才能做到这一点?我想在不依赖容器的情况下做到这一点。更简单的方法会有所帮助。
  • 不客气。好吧,如果生产者推送一个指向对象的指针,那么消费者可以像你一样获取指针......你只是没有碰巧使用它并在定时代码之外进行所有创建,这就是我说你的原因还不如使用ints 进行基准测试。在您的真实应用程序中,只需在pushing 指向它们之前将对象一一对应地new,然后当消费者弹出指向该对象的指针时,它可以在调用delete 之前处理该对象。是这个意思吗?

标签: c++ unit-testing


【解决方案1】:

只是说明了如何在基准测试中使用Data 元素,尽管这确实在测量的时间内添加了cout,这并不理想但可能也不重要。

class Data
{
public:
    Data(long i) : i_(i) {}
    void dataPrint() {cout << "Hello";}
private:
    long i_;
    double j;
};


Data* dataArray[1000000];
for (int i = 0; i < NUM_DATA; ++i) dataArray[i] = new Data(i);

boost::lockfree::queue<Data*> BoostQueue(1000);

struct Producer
{
    void operator()()
    {
        for(long i=0; i<1000000; i++)
            BoostQueue.push( dataArray[i] );
    }
};


struct Consumer
{
    Data *pData;
    long result_;
    void operator()()
    {
        result_ = 0;
        while (  BoostQueue.pop( pData ) )
            result_ += pData->i_;
        std::cout << result_ << '\n';
    }
};


int main(int argc, char** argv)
{
    std::thread thrd [NUM_PROD_THREAD + NUM_CONSUM_THREAD];

    std::chrono::duration<double> elapsed_seconds;

    auto start = std::chrono::high_resolution_clock::now();
    for ( int i = 0; i < NUM_PROD_THREAD;  i++ )
        thrd[i] = std::thread{ Producer() };

    for ( int i = 0; i < NUM_CONSUM_THREAD; i++ )
        thrd[NUM_PROD_THREAD+i] = std::thread{Consumer()};

    for ( int i = 0; i < NUM_CONSUM_THREAD; i++ )
        thrd[NUM_PROD_THREAD+i].join();

    auto end = std::chrono::high_resolution_clock::now();
    elapsed_seconds = end - start;
    std::cout << "Enqueue and Dequeue 1 million item in:"
        << elapsed_seconds.count() << std::endl;

    for ( int i = 0; i < NUM_PROD_THREAD; i++ )
        thrd[i].join();
    for (int i = 0; i < 1000000; ++i)
        delete dataArray[i];
}

【讨论】:

  • 感谢您的插图。但是,我注意到两件事,一是它们在两个实现(前一个和这个)的执行时间上差别不大。其次,如果我使用 result_ += pData->i_ changed i_ (to public),我会遇到分段错误
  • @user225008:这个想法是做一些非常琐碎的工作,所以它不会改变执行时间,但至少说明了访问队列已传递指针的对象,因此它表示最小的“真实”工作。对于段错误 - 您可以将循环和数据从 100 万减少到 10,并打印 (cout) 被推送和弹出的 pData 值,以确保它成功弹出有效指针。
猜你喜欢
  • 1970-01-01
  • 2011-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-23
相关资源
最近更新 更多