opencv 矩阵数据Mat元素获取有四种方式: 直接获取、模板方式、行指针方式、元素地址方式。 测试发现在O3优化方式下直接获取和元素地址访问这两中方式效率最高, 其效率为模板和行指针方式的两倍。 在没有优化的方式下符合一般常识,元素地址访问和行指针方式效率最高。
测试代码如下:
#include <string>
#include <iostream>
#include <time.h>
using namespace cv;
using namespace std;
#include "CwTimer.hpp"
int main( int argc, const char** argv )
{
//矩阵总共获取次数
uint32_t num = 10000;
Mat m(100, 100, CV_64F, Scalar(0));
//定义矩阵模板
Mat_<double> m_ = m;
uint32_t h = m.rows, w = m.cols;
//直接获取方式
CwTimer tm0("direct");
for(int i = 0; i < num; i++)
{
tm0.Start();
for(int p = 0; p < h; p++)
{
for(int q = 0; q < w; q++)
{
m.at<float>(p, q) = 13;
double tm = m.at<double>(p, q);
}
}
tm0.Stop();
}
tm0.Report();
//模板获取方式
CwTimer tm3("template");
for(int i = 0; i < num; i++)
{
tm3.Start();
for(int p = 0; p < h; p++)
{
for(int q = 0; q < w; q++)
{
m_(p, q) = 13;
double tm = m_(p, q);
}
}
tm3.Stop();
}
tm3.Report();
//行指针获取方式
CwTimer tm1("row_ptr");
for(int i = 0; i < num; i++)
{
tm1.Start();
for(int p = 0; p < h; p++)
{
double * pm = m.ptr<double>(p);
for(int q = 0; q < w; q++)
{
pm[q] = 13;
double tm = pm[q];
}
}
tm1.Stop();
}
tm1.Report();
//元素地址获取方式
CwTimer tm2("address");
for(int i = 0; i < num; i++)
{
tm2.Start();
double * pdata = (double*)m.data;
for(int p = 0; p < h * w; p++)
{
pdata[p] = 13;
double tm = pdata[p];
}
tm2.Stop();
}
tm2.Report();
return 1;
}
代码中CwTimer 是一个时间类,可以方便测试程序运行时间, 该类是从网络中获取, 作者为 程明明。 其实现如下。
#pragma once
#pragma warning(disable:4512)
class CwTimer
{
public:
CwTimer(string t = "Timer"):title(t) { is_started = false; start_clock = 0; cumulative_clock = 0; n_starts = 0; }
~CwTimer(){ if (is_started) printf("CwTimer '%s' is started and is being destroyed.\n", title.c_str()); }
inline void Start();
inline void Stop();
inline void Reset();
inline bool Report();
inline bool StopAndReport() { Stop(); return Report(); }
inline float TimeInSeconds();
inline float AvgTime(){assert(is_started == false); return TimeInSeconds()/n_starts;}
private:
string title;
bool is_started;
clock_t start_clock;
clock_t cumulative_clock;
unsigned int n_starts;
};
/************************************************************************/
/* Implementations */
/************************************************************************/
void CwTimer::Start()
{
if (is_started){
printf("CwTimer '%s' is already started. Nothing done.\n", title.c_str());
start_clock = clock();
return;
}
is_started = true;
n_starts++;
start_clock = clock();
}
void CwTimer::Stop()
{
if (!is_started){
printf("CwTimer '%s' is started. Nothing done\n", title.c_str());
return;
}
cumulative_clock += clock() - start_clock;
is_started = false;
}
void CwTimer::Reset()
{
if (is_started) {
printf("CwTimer '%s'is started during reset request.\n Only reset cumulative time.\n", title.c_str());
return;
}
cumulative_clock = 0;
}
bool CwTimer::Report()
{
if (is_started){
printf("CwTimer '%s' is started.\n Cannot provide a time report.", title.c_str());
return false;
}
float timeUsed = TimeInSeconds();
printf("[%s] CumuTime: %4gs, #run: %2d, AvgTime: %4gs\n", title.c_str(), timeUsed, n_starts, timeUsed/n_starts);
return true;
}
float CwTimer::TimeInSeconds()
{
if (is_started){
printf("CwTimer '%s' is started. Nothing done\n", title.c_str());
return 0;
}
return float(cumulative_clock) / CLOCKS_PER_SEC;
}
利用O3优化:
g++ test.cpp -O3 -o test `pkg-config --cflags --libs opencv`
程序运行结果如下, 发现直接获取元素和地址访问速度基本一样,但是模板获取和行指针的方式效率只有其他两种的一般。这个和一般性常识不符,一般认为按照行指针获取的效率会和元素地址获取方式持平。
不利用优化:
g++ test.cpp -g -o test `pkg-config --cflags --libs opencv`
得出结果如下:
可以看出此时的性能符合一般常识。