繁体   English   中英

复数乘法:使用 -Ofast 编译时,std::vector 比 Eigen::Matrix 快 2 倍

[英]Complex multiply: std::vector 2x fast than Eigen::Matrix when compiled with -Ofast

我在服务器上写了一些测试用例,发现 Eigen::Matrix 比 std::vector 慢得多。 我不知道为什么?

服务器的配置列表如下:

猫 /proc/cpuinfo

Intel(R) Xeon(R) Platinum 8124M CPU @ 3.00GHz

flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq monitor ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 ida arat

编译命令:

g++ -DEIGEN_FFTW_DEFAULT -isystem /toolchain/library/gtest/1.10.0/include -isystem /toolchain/library/glog/0.4.0/include -isystem /toolchain/library/eigen/3.3.7/include/eigen3 -isystem / toolchain/library/eigen/3.3.7/include/eigen3/unsupported -isystem /toolchain/library/boost/1.72.0/include -isystem /toolchain/library/fftw/3.3.8/include -isystem /toolchain/library/ opencv/2.4.13.6/include -isystem /toolchain/library/nlopt/2.6.2/include -Wno-unused-local-typedefs -Werror -Wall -std=c++0x -fPIC -march=native -Ofast -DNDEBUG -std=gnu++11……

测试用例列表如下:

TEST(ComplexMul, MatrixFloat2) {  // test for Matrix * complex_value
    using test_type = float;
    const complex<test_type> kC1(3.4, 4.3);
    Matrix<complex<test_type>, Dynamic, Dynamic, RowMajor> m1, m3(kMatRowNum, kMatColNum);
    m1 = Matrix<complex<test_type>, Dynamic, Dynamic, RowMajor>::Random(kMatRowNum, kMatColNum);

    bpt::ptime tm_begin1 = bpt::microsec_clock::local_time();

    m3 = m1 * kC1;

    bpt::ptime tm_end1 = boost::posix_time::microsec_clock::local_time();
    bpt::time_duration dur1 = tm_end1 - tm_begin1;

    ostream_color::Modifier_C red(ostream_color::FG_GREEN);
    ostream_color::Modifier_C def(ostream_color::FG_DEFAULT);
    cout << red << "ComplexMul.MatrixFloat2 duration: " << dur1.total_milliseconds() << " ms";
    cout << def << endl;

    cout << m3.block(0, 0, 3, 3) << endl;
}

TEST(ComplexMul, VectorFloat) {  // test for std::vector * complex_value
    using test_type = float;
    const complex<test_type> kC1(3.4, 4.3);
    Matrix<complex<test_type>, Dynamic, Dynamic, RowMajor> m1 = Matrix<complex<test_type>, Dynamic, Dynamic, RowMajor>::Random(kMatRowNum, kMatColNum);
    std::vector<std::complex<test_type>> vec1(m1.data(), m1.data() + m1.rows() * m1.cols()), vec3(m1.rows() * m1.cols());

    bpt::ptime tm_begin1 = bpt::microsec_clock::local_time();

    for (size_t i = 0; i < vec1.size(); i++) {
        vec3[i] = vec1[i] * kC1;
    }

    bpt::ptime tm_end1 = boost::posix_time::microsec_clock::local_time();
    bpt::time_duration dur1 = tm_end1 - tm_begin1;

    ostream_color::Modifier_C red(ostream_color::FG_GREEN);
    ostream_color::Modifier_C def(ostream_color::FG_DEFAULT);
    cout << red << "ComplexMul.VectorFloat duration: " << dur1.total_milliseconds() << " ms";
    cout << def << endl;

    cout << vec3[0] << endl;
}

下面的测试结果列表:在此处输入图像描述

我已经多次编写和重写(和重写)线性代数库,并使用 Blaze、Eigen 和 ETL 作为示例代码库。 有几件事需要检查:

  1. 您是否有任何外部库链接到您的测试并正确配置? 例如,几乎每个智能表达式模板库都能够在适当的情况下使用英特尔的 MKL。 艾根也不例外。 https://eigen.tuxfamily.org/dox/TopicUsingIntelMKL.html

  2. 您是否启用了 SIMD? 在 GCC/clang 上,您可以检查是否使用__AVX__和公司宏启用了 SSE/AVX/etc。 您可能还需要使用特殊的编译器标志进行编译以启用它们。 如果您使用 msvc,则需要特别狡猾,因为它们在这里没有提供很多所需的功能。 msvc 确实支持 SIMD 内在函数,但它们不提供编译标志。 您可以使用 Intel 和 amd 上的 cpuid 指令查看您的 cpu 支持什么。 除了 cpu 支持之外,还有一些额外的注意事项需要记住,但我会让你在这里阅读 rest: 如何检查 CPU 是否支持 SSE3 指令集? . 输入此内容后,我注意到您的编译器标志,并看到您可以访问 avx512 并且正在使用该标志进行编译。 这应该意味着它已启用,但我想我会留下它,以便您可以验证它。

  3. 这个结果可以用静态大小的矩阵重复吗? 我问的原因是,特征库中可能存在缓存未命中问题。 使用静态大小的矩阵可以保证您拥有连续的、对齐的存储空间,并且不会因为矩阵稀疏而仅仅查看一堆缓存未命中。

  4. Eigen 的抽象不是零成本的,老实说,在小矩阵大小时并不令人惊讶。 我的机器上的基准测试显示 blaze 和 etl 要好几光年(etl 甚至获得了很多 gpu 支持)。 也就是说,Eigen 比大多数都好。 尝试比您正在使用的更大的矩阵,但要小到足以放入缓存中。 小矩阵几乎不考虑单个 AVX512 加载指令,由于与它周围的逻辑相关的开销,您可能会为使用 SIMD 支付更多费用。 更大的矩阵尺寸将真正让 SIMD 大放异彩。

  5. 您是否启用了多线程? Eigen 能够使用多线程,但这可能会对您造成伤害,而不是帮助您处理小矩阵。

  6. 这有多可重复? ... Idk,10,000 次迭代的平均值和标准差是多少? 我不熟悉你的测试 API,但伪代码应该看起来更像这样:

     // Make sure the cache lines are hot C = B * A; start_timer(); for (i = 0; i < 10000; ++i) C = B * A; stop_timer(); mean = time / 10000;

如果需要,您可以重新运行上述几次以计算平均值的标准差。

我打算运行一个替代基准,但结果绝对没有意义,因为我的机器与你的机器截然不同。 我没有相同的指令集、操作系统、stl 实现或外部依赖项。 我什至不使用相同的编译器。 通常情况下,我会说可以忽略这一点,但是这些库会利用它们所能提供的一切,因此,您看到的结果绝不会反映我(或大多数用户)必然会看到的内容。

编辑:我用我的设置运行了一个基准测试。 我的设置目前在 Intel i9、MSVC 编译器、c++latest 和 avx512 上使用 16 个线程。 需要指出的一件事是,您的矩阵大小比处理器的缓存大小大得多。 因此,我预期的行为是 Eigen 应该收敛到与其他库大致相同的性能,因为从 RAM 加载成为主要瓶颈,而不是计算速度。

也就是说,对于您的特定测试用例,blaze 在我的设置中表现得更好。 甚至我自己的图书馆在这个地区也做得更好(Eigen 在大多数地区通常都比我的图书馆做得更好,所以这对我来说是令人惊讶的行为)。 与原始 for 循环相比,我的机器性能下降了大约 2 倍。 我做了一些挖掘,发现了这个: https://eigen.tuxfamily.org/bz/show_bug.cgi?id=1765

似乎我的特定设置受到特定于 c++17+ 和 msvc 的错误的困扰。 我不能轻易纠正这个问题,因为我的测试设置需要 c++20 支持。 一旦您确认优化已经到位,并且您的问题仍然存在,您可以尝试联系 Eigen 人员并提交错误报告。 如果我正确理解了这个问题,msvc 没有办法强制内联程序集和内在函数。 您能做的最好的事情就是强烈建议编译器内联代码。 不过 GCC/Clang 不应该有这个问题,而且我看到您正在使用 C++11 进行编译。 也许他们理解错误,或者它与 msvc 错误无关。 无论哪种方式,Eigen 团队都可能会从您的反馈中受益。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM