【问题标题】:(opencv rc1) What causes Mat multiplication to be 20x slower than per-pixel multiplication?(opencv rc1) 是什么导致 Mat 乘法比每像素乘法慢 20 倍?
【发布时间】:2015-07-21 22:05:32
【问题描述】:
// 700 ms
cv::Mat in(height,width,CV_8UC1);
in /= 4;

替换为

//40 ms
cv::Mat in(height,width,CV_8UC1);
for (int y=0; y < in.rows; ++y)
{
    unsigned char* ptr = in.data + y*in.step1();
    for (int x=0; x < in.cols; ++x)
    {
        ptr[x] /= 4;
    }
}

什么会导致这种行为?是由于opencv将带有标量乘法的Mat“提升”为带有Mat乘法的Mat,还是针对arm的特定失败优化? (NEON 已启用)。

【问题讨论】:

  • 你可以试试*= 1.0f/4.0; ?你没有初始化元素顺便说一句
  • 浮点乘法的结果与整数除法相同,在我对每像素乘法和整个垫子乘法的测试中,不确定性/差异约为 20%。
  • 你能运行 perf 吗? perf.wiki.kernel.org/index.php/Main_Page
  • 也许你可以确认它,但看起来 cv::Mat 只有operator / 用于双精度标量值。那么你机器的双精度除法计算可能比 int 除法慢 20 倍左右?
  • 您使用的是 openCV 调试库还是发布库?您是在调试模式还是发布模式下编译代码?有/没有优化,强制单精度等?我们在谈论什么样的矩阵大小?

标签: c++ opencv java-native-interface arm neon


【解决方案1】:

通过测量 CPU 时间来尝试相同的方法。

int main()
{
    clock_t startTime;
    clock_t endTime;

    int height =1024;
    int width =1024;

    // 700 ms
    cv::Mat in(height,width,CV_8UC1, cv::Scalar(255));
    std::cout << "value: " << (int)in.at<unsigned char>(0,0) << std::endl;

    cv::Mat out(height,width,CV_8UC1);

    startTime = clock();
    out = in/4;
    endTime = clock();
    std::cout << "1: " << (float)(endTime-startTime)/(float)CLOCKS_PER_SEC << std::endl;
    std::cout << "value: " << (int)out.at<unsigned char>(0,0) << std::endl;


    startTime = clock();
    in /= 4;
    endTime = clock();
    std::cout << "2: " <<  (float)(endTime-startTime)/(float)CLOCKS_PER_SEC << std::endl;
    std::cout << "value: " << (int)in.at<unsigned char>(0,0) << std::endl;

    //40 ms
    cv::Mat in2(height,width,CV_8UC1, cv::Scalar(255));

    startTime = clock();
    for (int y=0; y < in2.rows; ++y)
    {
        //unsigned char* ptr = in2.data + y*in2.step1();
        unsigned char* ptr = in2.ptr(y);
        for (int x=0; x < in2.cols; ++x)
        {
            ptr[x] /= 4;
        }
    }
    std::cout << "value: " << (int)in2.at<unsigned char>(0,0) << std::endl;

    endTime = clock();
    std::cout << "3: " <<  (float)(endTime-startTime)/(float)CLOCKS_PER_SEC << std::endl;


    cv::namedWindow("...");
    cv::waitKey(0);
}

结果:

value: 255
1: 0.016
value: 64
2: 0.016
value: 64
3: 0.003
value: 63

您看到结果不同,可能是因为mat.divide() 确实执行了浮点除法和舍入到下一个。虽然您在更快的版本中使用整数除法,但速度更快但结果不同。

另外,openCV计算中有一个saturate_cast,但我猜计算负载差异较大的将是双精度除法。

【讨论】:

  • 你能加 4:每个元素乘以 0.25 吗? IIRC 对我来说也“很快”,这表明除了 flop/intop 计算性能之外还有其他事情发生。
  • 在我的机器上,乘法/除以浮点数大约是无符号字符除以 4 时间的 2 倍(顺便说一句,这是一个位移位)。双倍除法与浮点数相同,我不相信 atm :)
【解决方案2】:

这是一个非常古老的问题(我在几年前报告过),许多基本操作都需要额外的时间。不仅是除法,还有加法、腹肌等……我不知道这种行为的真正原因。更奇怪的是,本来应该花费更多时间的操作,如 addWeighted,实际上非常有效。试试这个:

addWeighted(in, 1.0/4, in, 0, 0, in);

它对每个像素执行多个操作,但它的运行速度比添加函数和循环实现快几倍。

这是我在错误跟踪器上的report

【讨论】:

  • 对于我的 setup 和 opencv 版本,addWeighted 相当慢,这是另一个问题。
  • 这更奇怪,因为我刚刚检查了一下,发现 addWeighted 快得多。您使用的是什么版本的 OpenCV?
  • 另外你能用其他功能做同样的实验吗?以 abs() 为例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-10
  • 1970-01-01
  • 2023-04-01
  • 2014-11-04
  • 2020-03-25
  • 2012-07-14
  • 1970-01-01
相关资源
最近更新 更多