C++ 数据访问基准测试

3

我正在编写一个简单的 C++ 基准测试,以比较不同平台上数据访问的执行时间。但是我得到了奇怪的结果。我测量了顺序访问和间接访问的时间。为此,我只是以两种不同的方式将一个数组的数据复制到另一个数组中。下面是代码和结果。

我得到的时间是模棱两可的。对于 int 数据类型的评估显示,顺序访问更快(这很好)。但是对于 float 和 double 类型,情况恰恰相反(请参见下面的时间结果)。也许我在做基准测试时出了问题,或者有一些陷阱我没有考虑到吗?还是您可以建议一些基准测试工具来比较不同数据类型的数据访问或简单操作性能呢?

template<typename T>
std::chrono::nanoseconds::rep PerformanceMeter<T>::testDataAccessArr()
{
    std::chrono::nanoseconds::rep totalSequential = 0;

    T* arrDataIn = new T[k_SIZE];
    T* arrDataOut = new T[k_SIZE];

    std::generate_n(arrDataIn, k_SIZE, DataProcess<T>::valueGenerator);
    DataProcess<T>::clearCache();

    std::chrono::nanoseconds::rep timeSequential = measure::ns(copySequentialArr, arrDataIn, arrDataOut, k_SIZE);

    std::cout << "Sequential order access:\t" << timePrint(timeSequential) << "\t";
    std::cout.flush();

    std::chrono::nanoseconds::rep totalIndirection = 0;
    T** pointers = new T*[k_SIZE];
    T** pointersOut = new T*[k_SIZE];
    for (size_t i = 0; i < k_SIZE; ++i)
    {
        pointers[i] = &arrDataIn[i];
        pointersOut[i] = &arrDataOut[i];
    }

    std::generate_n(arrDataIn, k_SIZE, DataProcess<T>::valueGenerator);
    std::generate_n(arrDataOut, k_SIZE, DataProcess<T>::valueGenerator);

    DataProcess<T>::clearCache();

    totalIndirection = measure::ns(copyIndirectionArr, pointers, pointersOut, k_SIZE);

    std::cout << std::endl << "Indirection order access:\t" << timePrint(totalIndirection) << std::endl;
    std::cout.flush();

    delete[] arrDataIn;
    delete[] arrDataOut;
    delete[] pointers;
    delete[] pointersOut;

    return timeSequential;
}

template <typename T>
void PerformanceMeter<T>::copySequentialArr(const T* dataIn, T* dataOut, size_t dataSize)
{
    for (int i = 0; i < dataSize; i++)
        dataOut[i] = dataIn[i];
}

template <typename T>
void PerformanceMeter<T>::copyIndirectionArr(T** dataIn, T** dataOut, size_t dataSize)
{
    for (int i = 0; i < dataSize; i++)
        *dataOut[i] = *dataIn[i];
}

结果:

-------------------测量int---------------

数据:10 MB;迭代次数:1

顺序访问:8.50454毫秒

间接访问:11.6925毫秒

-------------------测量float------------

数据:10 MB;迭代次数:1

顺序访问:8.84023毫秒

间接访问:8.53148毫秒

-------------------测量double-----------

数据:10 MB;迭代次数:1

顺序访问:5.57747毫秒

间接访问:3.72843毫秒


2
迭代次数:1。进行更多的迭代并计算平均时间。 - πάντα ῥεῖ
2
微基准测试并不简单。您应该增加更多迭代,包括优化,甚至最好查看汇编代码。 - Jonas
@Jonas 谢谢,但是你所说的“包括优化”是什么意思? - Alona
1
以“发布模式”编译它,或者只需在编译器选项中添加“-O3”(适用于clang和gcc)。 - Jonas
3
进行 10-20 次迭代是不够的。你应该进行数万次或数十万次的迭代。 - NathanOliver
显示剩余6条评论
1个回答

0

以下是使用T = int的示例,展示了GCC 6.3使用-O2的汇编输出:
copySequentialArrcopyIndirectionArr

从汇编中可以清楚地看出它们非常相似,但是copyIndirectionArrcopySequentialArr多了两个mov指令。因此我们可以得出结论,copySequentialArr是最快的。

当使用T = double时,情况也是如此:copySequentialArrcopyIndirectionArr

向量化

使用-O3时,情况变得有趣起来:copySequentialArrcopyIndirectionArrcopyIndirectionArr没有任何变化,但是copySequentialArr现在被编译器向量化了。这种向量化将使其在正常情况下比以前更快。

免责声明

这些生成的汇编代码检查是“不考虑上下文的”,因为如果编译器知道上下文,它会进一步优化它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接