2013-04-02 35 views
1

我正在为iPhone实施基于切线距离的OCR解决方案,该解决方案严重依赖于大小为253x7的浮点矩阵的快速乘法。对于概念验证,我已经实现了我自己的幼稚矩阵程序是这样的:iOS BLAS - 加速框架差矩阵乘法性能

Matrix operator*(const Matrix& matrix) const { 
    if(cols != matrix.rows) throw "cant multiply!"; 

    Matrix result(rows, matrix.cols); 
    for(int i = 0; i < result.rows; i++){ 
     for(int j = 0; j < result.cols; j++){ 
      T tmp = 0; 
      for(int k = 0; k < cols; k++){ 
       tmp += at(i,k) * matrix.at(k,j); 
      } 
      result.at(i,j) = tmp; 
     } 
    } 

    return result; 
} 

正如你所看到的,这是很基本的。 在PoC表现良好之后,我决定通过结合Accelerate Framework的矩阵乘法(推测使用SIMD和其他花哨的东西来完成繁重的工作......)来进一步提高性能极限:

Matrix operator*(const Matrix& m) const { 
    if(cols != m.rows) throw "cant multiply!"; 

    Matrix result(rows,m.cols); 

    cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, rows, m.cols, cols, 1, matrix, cols, m.matrix, m.cols, 1, result.matrix, result.cols); 

    return result; 
} 

令人震惊的是(至少对我而言),上面的代码花了两倍的时间来乘以矩阵!我试过使用单精度而不是双精度,因为我怀疑它与CPU的字大小有关(32位浮点数与32位ARM上的64位双精度浮点数),但没有性能增益......

我在做什么错?我的253x7矩阵是否太小,以致于在天真实施方面显着提升性能?

+0

看看[MGMatrix](https://github.com/ghenania/MGMatrix),它正是你需要的。它基于vDSP并提出了非常简单的界面。 –

回答

0

基本上,是的。 “x7”部分可能太小而不能使CBLAS的开销值得。做一个函数调用的代价,加上CBLAS函数给你的所有灵活性,需要一段时间才能恢复。每当您通过CblasNoTrans等选项时,请记住在那里有一个if()来管理该选项。尤其是cblas_dgemm会累加到C中,所以它必须读取前一个结果元素,应用一个乘法,然后在存储之前添加。这是很多额外的工作。

您可能想尝试vDSP功能而不是CBLAS。 vDSP_mmul稍微简单一些,不会累积到结果中。我在vDSP_*的小数据集(几千个元素)上运气不错。这就是说,我的经验是,幼稚的C实现在小数据集上通常可以相当快。避免函数调用是一个巨大的好处。说到这一点,请确保你的at()呼叫是内联的。否则,你在循环中浪费了很多时间。您可以通过使用指针添加在您的矩阵中串行移动而不是乘法(通过[]随机访问所需的)来加速C实现。在这个小的矩阵上,它可能或不值得;你必须简单介绍一下。看看汇编器的输出是非常有益的。

请记住,你绝对必须在设备上分析这些东西。模拟器的性能无关紧要。这不仅仅是模拟器速度更快,它完全不同。设备上模拟器上速度非常快的东西可能会慢很多。

+0

有趣的是,尽管我已经用指针算法(水平方向为++,垂直方向为+ = colSize)实现了它,但性能保持不变...编译器非常聪明,或者我错过了某些东西完全:D –

+0

编译器可能会识别“遍历数组”并将其转换为加法。 (现在回想起来,这似乎是一件很可能的事情,因为它很常见。)请看看生成的汇编程序。 –

1

几个问题:

  1. 253×7乘以什么尺寸矩阵?如果你正在做253x7 * 7x1的话,那么通用乘法器程序大部分时间都会花费在边缘代码上,而且很少有一个调整过的库可以做到这一点,这会使得它比一个天真的实现更快。

  2. 您计划使用哪些硬件以及iOS版本?特别是对于双精度,较旧的硬件和较旧的iOS版本在性能方面更为有限。例如,在Cortex-A8上,双精度算法完全没有流水线,所以图书馆几乎没有办法打败一个天真的实现。

如果其他矩阵不是小得可笑,而在硬件上,还请提交一个bug(出乎意料的低性能绝对一个bug)。具有高纵横比的小矩阵在通用矩阵乘法中很难快速实现,但它仍然是一个很好的缺陷。

如果硬件/ iOS版本较旧,则可能需要使用加速,因为它应该在较新的硬件/软件上显着更好。

如果另一个矩阵很小,那么可能没有太多要做。 ARM上没有双精度SIMD,这些矩阵太小而无法从缓存阻塞中受益,并且矩阵的维数也太小而无法从循环展开中获益。


如果你知道先验你的矩阵将是完全253x7 * 7X ???,你应该能够做的比这两个天真的执行情况和任何通用库好得多通过完全展开的矩阵乘法的内部维数。

+0

要乘以的矩阵是253x7乘以7x253。随着一些进一步的分析,我发现运行时间的主要部分是浪费与semaphore_destroy和semaphore_create由libBLAS调用(见图片:[http://i48.tinypic.com/33u4s3b.png](http://此外,我正在使用最新的iOS 6.1.2 –

+0

@TamásZahola在iPhone 5上测试此代码:请提交错误报告,并确保包含配置文件;加速不应该完全通过这个呼叫。 –

+0

您知道在这种情况下我应该在哪里提交错误报告? –