Skip to content

Instantly share code, notes, and snippets.

@kazuki-ma
Last active April 29, 2022 06:01
Show Gist options
  • Save kazuki-ma/8285876 to your computer and use it in GitHub Desktop.
Save kazuki-ma/8285876 to your computer and use it in GitHub Desktop.
test code for cv::Mat::forEach
#include <opencv2/core.hpp>
typedef cv::Point3_<uint8_t> Pixel;
typedef cv::Mat_<Pixel> MatT;
const cv::Size SIZE_FULL_HD(1920, 1020);
void ShowResult(const std::string name, const double time) {
std::cout << cv::format("%40s | %20.10f", name.c_str(), time) << std::endl;
return;
}
void bench(const std::string func_name, void func(MatT&), const int time) {
MatT img(SIZE_FULL_HD);
img *= 0;
const uint64_t start = cv::getCPUTickCount();
for (int count = 0; count < time; ++count) {
func(img);
}
const uint64_t end = cv::getCPUTickCount();
const double time_elapsed = (end - start) / cv::getTickFrequency() / time;
ShowResult(func_name, time_elapsed);
}
inline void iterator_access(MatT& frame) {
auto it = frame.begin();
for (auto &p : frame) {
p.x = 255;
}
}
inline void pointer_access(MatT& frame) {
auto p = reinterpret_cast<Pixel*>(frame.data);
const auto e = p + frame.cols * frame.rows;
while (p < e)
{
p->x = 255;
++p;
}
}
inline void forEach_access(MatT& frame) {
auto mat = cv::Mat_<Pixel>(frame);
mat.forEach([&](Pixel &p, const void*) -> void{
p.x = 255;
});
}
#define BENCH(func_name) \
bench(#func_name, func_name, times);
int main(const int an, const char* const* const as)
{
#ifdef _DEBUG
const int times = 1;
#else
const int times = 1000;
#endif
BENCH(iterator_access);
BENCH(pointer_access);
BENCH(forEach_access);
}
@kazuki-ma
Copy link
Author

my result

                     iterator_access |         7.2292294634
                      pointer_access |         2.0351401337
                   optimized_forEach |         1.2348574069
           optimized_forEachParallel |         0.3258145738

@kazuki-ma
Copy link
Author

・画像は continuous (n行目の終わりと、n+1行目の初めがメモリ上隣にある)前提
・OpenCV 3.0 実装では、 forEach を使っただけで parallel になります。
 が、一方で、最終的なマージ結果では、2.0 → 0.3 のような極端な高速化は無いかも……
 (ループアンローリングをやめてしまったので)

 基本的に、処理が重い Functor であれば、コア数に比例してスケールします。
 (CPU⇔Memory の帯域がボトルネックの場合などを除く)

テンプレートの中で渡された functor を ParallelLoopBody でラップし、
cv::parallel_for_ 経由で実行しています。

functor を ParallelLoopBodyでラップする処理は、
コンパイラでインライン変換されることを期待して書いています。
そのため、コンパイラが、functor の実装を特定出来る場合(一番いいのは、その場で ラムダ式を渡す)、
functor はインライン展開されます。
http://d.hatena.ne.jp/mickey24/20110211/stl_algorithms_and_functors)

最近のコンパイラは、関数ポインタ渡された場合でも、
取り得る値が1種類のみと分かれば、インライン展開してくれるらしいです。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment