【问题标题】:C++ optimize array conversion from int to floatC ++优化从int到float的数组转换
【发布时间】:2018-05-19 08:39:08
【问题描述】:

我正在尝试通过将一些 Matlab 代码移植到 C++ 来加速它,我发现在 C++ 中非常简单的操作比在 Matlab 中慢得多。即,将整数数组转换为浮点数。我正在使用带有 MKL 和 TBB 的 Intel Parallel Studio 2018。

Matlab 中的这一行在我的笔记本电脑(Mac OS 10.13)上平均需要 6 毫秒:

spec = single(spec_int); % spec_int is 1000x4096 uint16

naive 方法(单循环)大约需要 9 毫秒(不包括内存分配和初始化):

uint16_t *spec_int = (uint16_t *) MKL_malloc(4096 * 1000 * sizeof(uint16_t), 64);
float *spec = (float *) MKL_malloc(sizeof(float) * FRAME_SIZE, 64);

// Initialize spec_int

for(MKL_INT i = 0; i<FRAME_SIZE; i++)
    spec[i] = spec_int[i];

在 TBB 中使用 parallel_for 大约需要 13 毫秒:

tbb::parallel_for( size_t(0), size_t(FRAME_SIZE), [&]( size_t i ) {
    spec[i] = spec_int[i];
} );

我很困惑。我究竟做错了什么?如何在 C++ 中匹配 Matlab 速度?

【问题讨论】:

  • 编译器很可能正在对您的幼稚方法进行矢量化。您在编译时使用什么优化级别?
  • 我无法想象并行版本会更快,除非向量很大
  • 您当然应该为循环变量和索引使用int,而不是任何其他类型,例如MKL_INT,不管它是什么。
  • @NathanOliver -O3。是的,可能有自动矢量化。这是完整的编译行:icc -DMKL_ILP64 -mkl=parallel -O3 -I/opt/intel/compilers_and_libraries_2019.0.041/mac/mkl/include -I/opt/intel/compilers_and_libraries_2019.0.041/mac/tbb/include -std =c++11 -o gdocm_cpu main.cpp -L/opt/intel/compilers_and_libraries_2019.0.041/mac/mkl/lib -L/opt/intel/compilers_and_libraries_2019.0.041/mac/tbb/lib -Wl,-rpath,/ opt/intel/compilers_and_libraries_2019.0.041/mac/mkl/lib -Wl,-rpath,/opt/intel/compilers_and_libraries_2019.0.041/mac/tbb/lib -ltbb -lstdc++ -lpthread -lm -ldl
  • @RichardHodges 英特尔文档指出:“通常循环需要至少一百万个时钟周期才能使其值得使用 parallel_for。例如,在 2 GHz 处理器上至少需要 500 微秒的循环可能会从 parallel_for 中受益。”我有大约 400 万个元素,循环需要 9 毫秒。所以我期待至少有一些改进。

标签: c++ matlab tbb intel-mkl


【解决方案1】:

总结:您可能正在测量噪音:

相当确定您正在处理的只是以前(隐式)matlab 操作的内存缓存,以加快它的速度。我重写了您的幼稚实现以使用查找表(因此这是一个没有类型转换的简单分配)。这并没有加快速度。这是值得注意的。

接下来,我运行了一个类似的(但不同的——以防止它被优化)循环并对其进行计时。它的运行速度快得多

int main()
{
  float index[65537] ;
  for (int i = 0; i < 65537; i++)
    index[i] = i;
  uint16_t *pi = new uint16_t[4096 * 1000];
  memset(pi, 0, sizeof(pi));
  float *pf = new float[4096 * 1000];
  memset(pf, 0, sizeof(pf));
  clock_t begin = clock();
  for (int i = 0; i < 4096 * 1000; ++i)
  {
    pf[i] = index[pi[i]]; //no conversion...simple copy(and not faster)
  }
  clock_t end = clock();
  clock_t begin2 = clock();
  for (int i = 0; i < 4096 * 1000; ++i)
  {
    pf[i] = index[pi[i]+1]; 
  }
  clock_t end2 = clock();
  printf("%d\n", (long)end - begin);
  printf("%d\n", (long)end2 - begin2);
}

输出:

9
2

我通过实际的浮点转换得到了类似的结果。所以我的理论是,Matlab 只是执行一些操作,在执行您正在计时的操作之前缓存重要的内存(顺便说一下,您可能使用不同的机制来计时)。

这全是猜测,但这可能是没有 matlab 专家介入的最好结果。

C# 中的上述输出,顺便说一句:12, 4

【讨论】:

    猜你喜欢
    • 2019-12-04
    • 2021-01-13
    • 1970-01-01
    • 2014-04-30
    • 2012-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-01
    相关资源
    最近更新 更多