From 71f4651d08f0525050812a681be2acc756ef549f Mon Sep 17 00:00:00 2001 From: Ullrich Koethe Date: Tue, 5 Aug 2014 00:39:03 +0200 Subject: [PATCH] OpenMP refactoring --- include/vigra/openmp_vigra.h | 162 +++++++++++------- test/openmp/test.cxx | 315 +++++++++++++++++++++++------------ 2 files changed, 311 insertions(+), 166 deletions(-) diff --git a/include/vigra/openmp_vigra.h b/include/vigra/openmp_vigra.h index 69b23cc58..20b7e29c6 100644 --- a/include/vigra/openmp_vigra.h +++ b/include/vigra/openmp_vigra.h @@ -50,6 +50,12 @@ #include "openmp_def.h" +#ifdef _MSC_VER +# define VIGRA_RESTRICT __restrict +#else +# define VIGRA_RESTRICT __restrict__ +#endif + namespace vigra { @@ -60,14 +66,16 @@ namespace vigra class SrcImageIterator2, class SrcAccessor2, class DestImageIterator, class DestAccessor, class Functor> - inline void + inline int combineTwoImages(SrcImageIterator1 src1_upperleft, SrcImageIterator1 src1_lowerright, SrcAccessor1 src1_acc, SrcImageIterator2 src2_upperleft, SrcAccessor2 src2_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor& functor) { + int num_threads = 1; #pragma omp parallel { + num_threads = omp_get_num_threads(); const vigra::Size2D size(src1_lowerright - src1_upperleft); Functor f(functor); @@ -83,6 +91,7 @@ namespace vigra f); } } // omp parallel + return num_threads; } @@ -91,15 +100,17 @@ namespace vigra class MaskImageIterator, class MaskAccessor, class DestImageIterator, class DestAccessor, class Functor> - inline void + inline int combineTwoImagesIf(SrcImageIterator1 src1_upperleft, SrcImageIterator1 src1_lowerright, SrcAccessor1 src1_acc, SrcImageIterator2 src2_upperleft, SrcAccessor2 src2_acc, MaskImageIterator mask_upperleft, MaskAccessor mask_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor& functor) { + int num_threads = 1; #pragma omp parallel { + num_threads = omp_get_num_threads(); const vigra::Size2D size(src1_lowerright - src1_upperleft); Functor f(functor); @@ -116,6 +127,7 @@ namespace vigra f); } } // omp parallel + return num_threads; } @@ -124,15 +136,17 @@ namespace vigra class SrcImageIterator3, class SrcAccessor3, class DestImageIterator, class DestAccessor, class Functor> - inline void + inline int combineThreeImages(SrcImageIterator1 src1_upperleft, SrcImageIterator1 src1_lowerright, SrcAccessor1 src1_acc, SrcImageIterator2 src2_upperleft, SrcAccessor2 src2_acc, SrcImageIterator3 src3_upperleft, SrcAccessor3 src3_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor& functor) { + int num_threads = 1; #pragma omp parallel { + num_threads = omp_get_num_threads(); const vigra::Size2D size(src1_lowerright - src1_upperleft); Functor f(functor); @@ -149,17 +163,20 @@ namespace vigra f); } } // omp parallel + return num_threads; } template - inline void + inline int copyImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc) { + int num_threads = 1; #pragma omp parallel { + num_threads = omp_get_num_threads(); const vigra::Size2D size(src_lowerright - src_upperleft); #pragma omp for schedule(guided) nowait @@ -172,19 +189,22 @@ namespace vigra dest_upperleft + begin, dest_acc); } } // omp parallel + return num_threads; } template - inline void + inline int transformImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor& functor) { + int num_threads = 1; #pragma omp parallel { + num_threads = omp_get_num_threads(); const vigra::Size2D size(src_lowerright - src_upperleft); Functor f(functor); @@ -199,6 +219,7 @@ namespace vigra f); } } // omp parallel + return num_threads; } @@ -206,14 +227,16 @@ namespace vigra class MaskImageIterator, class MaskAccessor, class DestImageIterator, class DestAccessor, class Functor> - inline void + inline int transformImageIf(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, MaskImageIterator mask_upperleft, MaskAccessor mask_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor& functor) { + int num_threads = 1; #pragma omp parallel { + num_threads = omp_get_num_threads(); const vigra::Size2D size(src_lowerright - src_upperleft); Functor f(functor); @@ -229,6 +252,7 @@ namespace vigra f); } } // omp parallel + return num_threads; } @@ -255,7 +279,7 @@ namespace vigra int id() const {return 0;} - void operator()(ValueType* __restrict__ d, const ValueType* __restrict__ f, int n) const + void operator()(ValueType* VIGRA_RESTRICT d, const ValueType* VIGRA_RESTRICT f, int n) const { vigra_fail("fh::detail::ChessboardTransform1D: not implemented"); } @@ -269,7 +293,7 @@ namespace vigra int id() const {return 1;} - void operator()(ValueType* __restrict__ d, const ValueType* __restrict__ f, int n) const + void operator()(ValueType* VIGRA_RESTRICT d, const ValueType* VIGRA_RESTRICT f, int n) const { const ValueType one = static_cast(1); @@ -293,7 +317,7 @@ namespace vigra int id() const {return 2;} - void operator()(ValueType* __restrict__ d, const ValueType* __restrict__ f, int n) const + void operator()(ValueType* VIGRA_RESTRICT d, const ValueType* VIGRA_RESTRICT f, int n) const { typedef float math_t; @@ -347,7 +371,7 @@ namespace vigra template - void + int fhDistanceTransform(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor sa, DestImageIterator dest_upperleft, DestAccessor da, ValueType background, Transform1dFunctor transform1d) @@ -360,8 +384,10 @@ namespace vigra const int greatest_length = std::max(size.x, size.y); DistanceImageType intermediate(size, vigra::SkipInitialization); + int num_threads = 1; #pragma omp parallel { + num_threads = omp_get_num_threads(); DistanceType* const f = new DistanceType[greatest_length]; DistanceType* const d = new DistanceType[greatest_length]; @@ -420,6 +446,7 @@ namespace vigra delete [] d; delete [] f; } // omp parallel + return num_threads; } } // namespace detail } // namespace fh @@ -428,7 +455,7 @@ namespace vigra template - void + int distanceTransform(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor sa, DestImageIterator dest_upperleft, DestAccessor da, ValueType background, int norm) @@ -436,25 +463,25 @@ namespace vigra switch (norm) { case 0: - fh::detail::fhDistanceTransform(src_upperleft, src_lowerright, sa, - dest_upperleft, da, - background, - fh::detail::ChessboardTransform1D()); + return fh::detail::fhDistanceTransform(src_upperleft, src_lowerright, sa, + dest_upperleft, da, + background, + fh::detail::ChessboardTransform1D()); break; case 1: - fh::detail::fhDistanceTransform(src_upperleft, src_lowerright, sa, - dest_upperleft, da, - background, - fh::detail::ManhattanTransform1D()); + return fh::detail::fhDistanceTransform(src_upperleft, src_lowerright, sa, + dest_upperleft, da, + background, + fh::detail::ManhattanTransform1D()); break; case 2: // FALLTHROUGH default: - fh::detail::fhDistanceTransform(src_upperleft, src_lowerright, sa, - dest_upperleft, da, - background, - fh::detail::EuclideanTransform1D()); + return fh::detail::fhDistanceTransform(src_upperleft, src_lowerright, sa, + dest_upperleft, da, + background, + fh::detail::EuclideanTransform1D()); } } @@ -466,7 +493,7 @@ namespace vigra class SrcImageIterator2, class SrcAccessor2, class DestImageIterator, class DestAccessor, class Functor> - inline void + inline int combineTwoImages(SrcImageIterator1 src1_upperleft, SrcImageIterator1 src1_lowerright, SrcAccessor1 src1_acc, SrcImageIterator2 src2_upperleft, SrcAccessor2 src2_acc, @@ -477,6 +504,7 @@ namespace vigra src2_upperleft, src2_acc, dest_upperleft, dest_acc, func); + return 1; } @@ -485,7 +513,7 @@ namespace vigra class MaskImageIterator, class MaskAccessor, class DestImageIterator, class DestAccessor, class Functor> - inline void + inline int combineTwoImagesIf(SrcImageIterator1 src1_upperleft, SrcImageIterator1 src1_lowerright, SrcAccessor1 src1_acc, SrcImageIterator2 src2_upperleft, SrcAccessor2 src2_acc, MaskImageIterator mask_upperleft, MaskAccessor mask_acc, @@ -497,6 +525,7 @@ namespace vigra mask_upperleft, mask_acc, dest_upperleft, dest_acc, func); + return 1; } @@ -505,7 +534,7 @@ namespace vigra class SrcImageIterator3, class SrcAccessor3, class DestImageIterator, class DestAccessor, class Functor> - inline void + inline int combineThreeImages(SrcImageIterator1 src1_upperleft, SrcImageIterator1 src1_lowerright, SrcAccessor1 src1_acc, SrcImageIterator2 src2_upperleft, SrcAccessor2 src2_acc, SrcImageIterator3 src3_upperleft, SrcAccessor3 src3_acc, @@ -517,23 +546,25 @@ namespace vigra src3_upperleft, src3_acc, dest_upperleft, dest_acc, func); + return 1; } template - inline void + inline int copyImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc) { vigra::copyImage(src_upperleft, src_lowerright, src_acc, dest_upperleft, dest_acc); + return 1; } template - inline void + inline int transformImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor& func) @@ -541,6 +572,7 @@ namespace vigra vigra::transformImage(src_upperleft, src_lowerright, src_acc, dest_upperleft, dest_acc, func); + return 1; } @@ -548,7 +580,7 @@ namespace vigra class MaskImageIterator, class MaskAccessor, class DestImageIterator, class DestAccessor, class Functor> - inline void + inline int transformImageIf(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, MaskImageIterator mask_upperleft, MaskAccessor mask_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, @@ -558,13 +590,14 @@ namespace vigra mask_upperleft, mask_acc, dest_upperleft, dest_acc, func); + return 1; } template - void + int distanceTransform(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, ValueType background, int norm) @@ -572,6 +605,7 @@ namespace vigra vigra::distanceTransform(src_upperleft, src_lowerright, src_acc, dest_upperleft, dest_acc, background, norm); + return 1; } #endif // OPENMP @@ -585,16 +619,16 @@ namespace vigra class SrcImageIterator2, class SrcAccessor2, class DestImageIterator, class DestAccessor, class Functor> - inline void + inline int combineTwoImages(vigra::triple src1, vigra::pair src2, vigra::pair dest, const Functor& functor) { - vigra::omp::combineTwoImages(src1.first, src1.second, src1.third, - src2.first, src2.second, - dest.first, dest.second, - functor); + return vigra::omp::combineTwoImages(src1.first, src1.second, src1.third, + src2.first, src2.second, + dest.first, dest.second, + functor); } @@ -603,19 +637,19 @@ namespace vigra class MaskImageIterator, class MaskAccessor, class DestImageIterator, class DestAccessor, class Functor> - inline void + inline int combineTwoImagesIf(vigra::triple src1, vigra::pair src2, vigra::pair mask, vigra::pair dest, const Functor& functor) { - vigra::omp::combineTwoImagesIf(src1.first, src1.second, src1.third, - src2.first, src2.second, - mask.first, mask.second, - dest.first, dest.second, - functor); - } + return vigra::omp::combineTwoImagesIf(src1.first, src1.second, src1.third, + src2.first, src2.second, + mask.first, mask.second, + dest.first, dest.second, + functor); + } template - inline void + inline int combineThreeImages(vigra::triple src1, vigra::pair src2, vigra::pair src3, vigra::pair dest, const Functor& functor) { - vigra::omp::combineThreeImages(src1.first, src1.second, src1.third, - src2.first, src2.second, - src3.first, src3.second, - dest.first, dest.second, - functor); + return vigra::omp::combineThreeImages(src1.first, src1.second, src1.third, + src2.first, src2.second, + src3.first, src3.second, + dest.first, dest.second, + functor); } template - inline void + inline int transformImage(vigra::triple src, vigra::pair dest, const Functor& functor) { - vigra::omp::transformImage(src.first, src.second, src.third, - dest.first, dest.second, - functor); + return vigra::omp::transformImage(src.first, src.second, src.third, + dest.first, dest.second, + functor); } template - inline void + inline int copyImage(vigra::triple src, vigra::pair dest) { - vigra::omp::copyImage(src.first, src.second, src.third, - dest.first, dest.second); + return vigra::omp::copyImage(src.first, src.second, src.third, + dest.first, dest.second); } @@ -667,30 +701,30 @@ namespace vigra class MaskImageIterator, class MaskAccessor, class DestImageIterator, class DestAccessor, class Functor> - inline void + inline int transformImageIf(vigra::triple src, vigra::pair mask, vigra::pair dest, const Functor& functor) { - vigra::omp::transformImageIf(src.first, src.second, src.third, - mask.first, mask.second, - dest.first, dest.second, - functor); + return vigra::omp::transformImageIf(src.first, src.second, src.third, + mask.first, mask.second, + dest.first, dest.second, + functor); } template - inline void + inline int distanceTransform(vigra::triple src, vigra::pair dest, ValueType background, int norm) { - vigra::omp::distanceTransform(src.first, src.second, src.third, - dest.first, dest.second, - background, norm); + return vigra::omp::distanceTransform(src.first, src.second, src.third, + dest.first, dest.second, + background, norm); } } // namespace omp } // namespace vigra diff --git a/test/openmp/test.cxx b/test/openmp/test.cxx index 0e0d79c98..61d4cae69 100644 --- a/test/openmp/test.cxx +++ b/test/openmp/test.cxx @@ -39,59 +39,68 @@ #include "vigra/openmp_vigra.h" #include "vigra/multi_array.hxx" #include "vigra/error.hxx" +#include "vigra/timing.hxx" using namespace std; using namespace vigra; #define IMAGE_WIDTH 1000 #define IMAGE_HEIGHT 1000 -#define DEFAULT_PIXEL_VALUE 27.0 +#define DEFAULT_PIXEL_VALUE -27.0 -class ParallelTestFunctor { - int num_threads; + +class ParallelTestFunctor +{ + int num_threads, thread_id; public: - ParallelTestFunctor() : - num_threads(1) { + ParallelTestFunctor() + : num_threads(1) + , thread_id(0) + { } +#ifdef OPENMP //This constructor is initialized per thread - ParallelTestFunctor(ParallelTestFunctor const&) : - num_threads(omp_get_num_threads()) { - } + ParallelTestFunctor(ParallelTestFunctor const&) + : num_threads(omp_get_num_threads()) + , thread_id(omp_get_thread_num()) + {} +#endif template - T operator()(T const& arg1) const { -#ifdef OPENMP - vigra_precondition(num_threads > 1, - "The current team should contain at least two threads"); -#endif - return std::cos(arg1) + num_threads; + TinyVector operator()(T const& arg1) const + { + return TinyVector(std::cos(arg1), num_threads, thread_id); } template - T operator()(T const& arg1, T const& arg2) const { -#ifdef OPENMP - vigra_precondition(num_threads > 1, - "The current team should contain at least two threads"); -#endif - return arg1 + arg2 + num_threads; + TinyVector operator()(T const& arg1, T const& arg2) const + { + return TinyVector(arg1 + arg2, num_threads, thread_id); } template - T operator()(T const& arg1, T const& arg2, T const& arg3) const { -#ifdef OPENMP - vigra_precondition(num_threads > 1, - "The current team should contain at least two threads"); -#endif - return arg1 + arg2 + arg3 + num_threads; + TinyVector operator()(T const& arg1, T const& arg2, T const& arg3) const + { + return TinyVector(arg1 + arg2 + arg3, num_threads, thread_id); } }; struct OpenMPWrapperTest { typedef vigra::DImage Image; - OpenMPWrapperTest() : - img(IMAGE_WIDTH, IMAGE_HEIGHT), mask(Shape2(IMAGE_WIDTH, IMAGE_HEIGHT)) { + OpenMPWrapperTest() + : img(IMAGE_WIDTH, IMAGE_HEIGHT) + , mask(Shape2(IMAGE_WIDTH, IMAGE_HEIGHT)) +#ifdef OPENMP + , max_threads(omp_get_num_procs() / 2) +#else + , max_threads(1) +#endif + { +#ifdef OPENMP + omp_set_num_threads(max_threads); +#endif Image::Accessor acc = img.accessor(); Image::ScanOrderIterator i = img.begin(); @@ -105,10 +114,16 @@ struct OpenMPWrapperTest { mask.begin()[IMAGE_WIDTH * IMAGE_HEIGHT - 1] = 0; } - void copyImageTest() { + void copyImageTest() + { Image img1(IMAGE_WIDTH, IMAGE_HEIGHT); - vigra::omp::copyImage(srcImageRange(img), destImage(img1)); + int num_threads = vigra::omp::copyImage(srcImageRange(img), destImage(img1)); + + should( num_threads <= max_threads); +#ifdef OPENMP + should( num_threads > 1); +#endif Image::ScanOrderIterator i = img.begin(); Image::ScanOrderIterator i1 = img1.begin(); @@ -121,159 +136,246 @@ struct OpenMPWrapperTest { void combineTwoImagesTest() { - Image img1(IMAGE_WIDTH, IMAGE_HEIGHT); + typedef MultiArray<2, TinyVector > Image2; + Image2 img1(Shape2(IMAGE_WIDTH, IMAGE_HEIGHT)); + + int num_threads = vigra::omp::combineTwoImages(srcImageRange(img), srcImage(img), destImage(img1), ParallelTestFunctor()); - vigra::omp::combineTwoImages(srcImageRange(img), srcImage(img), destImage(img1), ParallelTestFunctor()); + should( num_threads <= max_threads); +#ifdef OPENMP + should( num_threads > 1); +#endif Image::ScanOrderIterator i = img.begin(); - Image::ScanOrderIterator i1 = img1.begin(); - Image::Accessor acc = img.accessor(); + Image2::iterator i1 = img1.begin(); + Image2::iterator i1end = img1.end(); - int num_threads = acc(i1) - 2*acc(i); + std::vector count_per_thread(num_threads, 0); - for(; i != img.end(); ++i, ++i1) + for(; i1 != i1end; ++i, ++i1) { - should( 2.0*acc(i) + num_threads == acc(i1) ); + shouldEqual( (*i)*2.0, (*i1)[0]); + shouldEqual( (double)num_threads, (*i1)[1]); + ++count_per_thread[int((*i1)[2])]; } -#ifdef OPENMP - should( num_threads > 1 ); -#endif + int active_threads = 0, count = 0, + m = NumericTraits::max(), M = NumericTraits::min(); + for(int k=0; k 0) + ++active_threads; + count += count_per_thread[k]; + m = std::min(m, count_per_thread[k]); + M = std::max(M, count_per_thread[k]); + } + + shouldEqual(count, img1.size()); + std::cerr << "combineTwoImages() active threads: " << active_threads << " of " << num_threads << "\n"; + std::cerr << " load range: " << m << " to " << M << "\n"; } void combineTwoImagesIfTest() { - Image img1(IMAGE_WIDTH, IMAGE_HEIGHT); - img1 = DEFAULT_PIXEL_VALUE; + typedef MultiArray<2, TinyVector > Image2; + Image2 img1(Shape2(IMAGE_WIDTH, IMAGE_HEIGHT), TinyVector(DEFAULT_PIXEL_VALUE)); - vigra::omp::combineTwoImagesIf(srcImageRange(img), srcImage(img), maskImage(mask), destImage(img1), ParallelTestFunctor()); + int num_threads = vigra::omp::combineTwoImagesIf(srcImageRange(img), srcImage(img), maskImage(mask), destImage(img1), ParallelTestFunctor()); + + should( num_threads <= max_threads); +#ifdef OPENMP + should( num_threads > 1); +#endif Image::ScanOrderIterator i = img.begin(); - Image::ScanOrderIterator i1 = img1.begin(); - Image::ScanOrderIterator i1end = img1.end(); - Image::Accessor acc = img.accessor(); + Image2::iterator i1 = img1.begin(); + Image2::iterator i1end = img1.end(); //First pixel and last pixel are preserved - should(acc(i1) == DEFAULT_PIXEL_VALUE); + shouldEqual((*i1)[0], DEFAULT_PIXEL_VALUE); i++; i1++; i1end--; - should(acc(i1end) == DEFAULT_PIXEL_VALUE); + shouldEqual((*i1end)[0], DEFAULT_PIXEL_VALUE); - int num_threads = acc(i1) - 2*acc(i); + std::vector count_per_thread(num_threads, 0); + count_per_thread[0] += 2; for(; i1 != i1end; ++i, ++i1) { - should( 2.0*acc(i) + num_threads == acc(i1) ); + shouldEqual( (*i)*2.0, (*i1)[0]); + shouldEqual( (double)num_threads, (*i1)[1]); + ++count_per_thread[int((*i1)[2])]; } -#ifdef OPENMP - should( num_threads > 1 ); -#endif + int active_threads = 0, count = 0, + m = NumericTraits::max(), M = NumericTraits::min(); + for(int k=0; k 0) + ++active_threads; + count += count_per_thread[k]; + m = std::min(m, count_per_thread[k]); + M = std::max(M, count_per_thread[k]); + } + + shouldEqual(count, img1.size()); + std::cerr << "combineTwoImagesIf() active threads: " << active_threads << " of " << num_threads << "\n"; + std::cerr << " load range: " << m << " to " << M << "\n"; } void combineThreeImagesTest() { - Image img1(IMAGE_WIDTH, IMAGE_HEIGHT); - Image img2(IMAGE_WIDTH, IMAGE_HEIGHT); + typedef MultiArray<2, TinyVector > Image2; + Image2 img1(Shape2(IMAGE_WIDTH, IMAGE_HEIGHT), TinyVector(DEFAULT_PIXEL_VALUE)); - std::plus add; - - vigra::omp::combineTwoImages(srcImageRange(img), srcImage(img), destImage(img1), add); + int num_threads = vigra::omp::combineThreeImages(srcImageRange(img), srcImage(img), srcImage(img), destImage(img1), ParallelTestFunctor()); + should( num_threads <= max_threads); +#ifdef OPENMP + should( num_threads > 1); +#endif Image::ScanOrderIterator i = img.begin(); - Image::ScanOrderIterator i1 = img1.begin(); - Image::Accessor acc = img.accessor(); + Image2::iterator i1 = img1.begin(); + + std::vector count_per_thread(num_threads, 0); for(; i != img.end(); ++i, ++i1) { - should(2.0*acc(i) == acc(i1)); + shouldEqual( (*i)*3.0, (*i1)[0]); + shouldEqual( (double)num_threads, (*i1)[1]); + ++count_per_thread[int((*i1)[2])]; } - vigra::omp::combineThreeImages(srcImageRange(img), srcImage(img), srcImage(img1), destImage(img2), ParallelTestFunctor()); - - i = img.begin(); - Image::ScanOrderIterator i2 = img2.begin(); - - int num_threads = acc(i2) - 4*acc(i); - - for(; i != img.end(); ++i, ++i2) + int active_threads = 0, count = 0, + m = NumericTraits::max(), M = NumericTraits::min(); + for(int k=0; k 0) + ++active_threads; + count += count_per_thread[k]; + m = std::min(m, count_per_thread[k]); + M = std::max(M, count_per_thread[k]); } -#ifdef OPENMP - should( num_threads > 1 ); -#endif + shouldEqual(count, img1.size()); + std::cerr << "combineThreeImages() active threads: " << active_threads << " of " << num_threads << "\n"; + std::cerr << " load range: " << m << " to " << M << "\n"; } void transformImageTest() { - Image img1(IMAGE_WIDTH, IMAGE_HEIGHT); + typedef MultiArray<2, TinyVector > Image2; + Image2 img1(Shape2(IMAGE_WIDTH, IMAGE_HEIGHT)); - vigra::omp::transformImage(srcImageRange(img), destImage(img1), - ParallelTestFunctor()); + int num_threads = vigra::omp::transformImage(srcImageRange(img), destImage(img1), ParallelTestFunctor()); + + should( num_threads <= max_threads); +#ifdef OPENMP + should( num_threads > 1); +#endif Image::ScanOrderIterator i = img.begin(); - Image::ScanOrderIterator i1 = img1.begin(); + Image2::iterator i1 = img1.begin(); Image::Accessor acc = img.accessor(); - int num_threads = acc(i1) - cos(acc(i)); + std::vector count_per_thread(num_threads, 0); for(; i != img.end(); ++i, ++i1) { - should(cos(acc(i)) + num_threads == acc(i1)); + shouldEqual( cos(*i), (*i1)[0]); + shouldEqual( (double)num_threads, (*i1)[1]); + ++count_per_thread[int((*i1)[2])]; } -#ifdef OPENMP - should( num_threads > 1 ); -#endif + int active_threads = 0, count = 0, + m = NumericTraits::max(), M = NumericTraits::min(); + for(int k=0; k 0) + ++active_threads; + count += count_per_thread[k]; + m = std::min(m, count_per_thread[k]); + M = std::max(M, count_per_thread[k]); + } + + shouldEqual(count, img1.size()); + std::cerr << "transformImage() active threads: " << active_threads << " of " << num_threads << "\n"; + std::cerr << " load range: " << m << " to " << M << "\n"; } void transformImageIfTest() { - Image img1(IMAGE_WIDTH, IMAGE_HEIGHT); - img1 = DEFAULT_PIXEL_VALUE; + typedef MultiArray<2, TinyVector > Image2; + Image2 img1(Shape2(IMAGE_WIDTH, IMAGE_HEIGHT), TinyVector(DEFAULT_PIXEL_VALUE)); - vigra::omp::transformImageIf(srcImageRange(img), maskImage(mask), destImage(img1), ParallelTestFunctor()); + int num_threads = vigra::omp::transformImageIf(srcImageRange(img), maskImage(mask), destImage(img1), ParallelTestFunctor()); + + should( num_threads <= max_threads); +#ifdef OPENMP + should( num_threads > 1); +#endif Image::ScanOrderIterator i = img.begin(); - Image::ScanOrderIterator i1 = img1.begin(); - Image::ScanOrderIterator i1end = img1.end(); - Image::Accessor acc = img.accessor(); + Image2::iterator i1 = img1.begin(); + Image2::iterator i1end = img1.end(); //First pixel and last pixel are preserved - should(acc(i1) == DEFAULT_PIXEL_VALUE); + shouldEqual((*i1)[0], DEFAULT_PIXEL_VALUE); i++; i1++; i1end--; - should(acc(i1end) == DEFAULT_PIXEL_VALUE); + shouldEqual((*i1end)[0], DEFAULT_PIXEL_VALUE); - int num_threads = acc(i1) - cos(acc(i)); + std::vector count_per_thread(num_threads, 0); + count_per_thread[0] += 2; for(; i1 != i1end; ++i, ++i1) { - should( cos(acc(i)) + num_threads == acc(i1)); + shouldEqual( cos(*i), (*i1)[0]); + shouldEqual( (double)num_threads, (*i1)[1]); + ++count_per_thread[int((*i1)[2])]; } -#ifdef OPENMP - should( num_threads > 1 ); -#endif + int active_threads = 0, count = 0, + m = NumericTraits::max(), M = NumericTraits::min(); + for(int k=0; k 0) + ++active_threads; + count += count_per_thread[k]; + m = std::min(m, count_per_thread[k]); + M = std::max(M, count_per_thread[k]); + } + + shouldEqual(count, img1.size()); + std::cerr << "transformImageIf() active threads: " << active_threads << " of " << num_threads << "\n"; + std::cerr << " load range: " << m << " to " << M << "\n"; } Image img; vigra::MultiArray<2, unsigned char> mask; + int max_threads; }; struct DistanceTransformTest { typedef vigra::DImage Image; - DistanceTransformTest() : - img(7, 7) { + DistanceTransformTest() + : img(7, 7) +#ifdef OPENMP + , max_threads(omp_get_num_procs() / 2) +#else + , max_threads(1) +#endif + { +#ifdef OPENMP + omp_set_num_threads(max_threads); +#endif static const double in[] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, @@ -297,8 +399,12 @@ struct DistanceTransformTest { Image res(img); Image res1(img); - vigra::omp::distanceTransform(srcImageRange(img), destImage(res), 0.0, 1); + int num_threads = vigra::omp::distanceTransform(srcImageRange(img), destImage(res), 0.0, 1); + should( num_threads <= max_threads); +#ifdef OPENMP + should( num_threads > 1); +#endif Image::Iterator i = res.upperLeft(); Image::Accessor acc = res.accessor(); int x, y; @@ -319,8 +425,12 @@ struct DistanceTransformTest { Image res(img); - vigra::omp::distanceTransform(srcImageRange(img), destImage(res), 0.0, 2); + int num_threads = vigra::omp::distanceTransform(srcImageRange(img), destImage(res), 0.0, 2); + should( num_threads <= max_threads); +#ifdef OPENMP + should( num_threads > 1); +#endif Image::Iterator i = res.upperLeft(); Image::Accessor acc = res.accessor(); int x, y; @@ -340,21 +450,22 @@ struct DistanceTransformTest { } Image img; + int max_threads; }; struct OpenMPWrapperTestSuite: public vigra::test_suite { OpenMPWrapperTestSuite() : vigra::test_suite("OpenMPWrapperTestSuite") { + add( testCase( &OpenMPWrapperTest::copyImageTest)); add( testCase( &OpenMPWrapperTest::combineTwoImagesTest)); add( testCase( &OpenMPWrapperTest::combineTwoImagesIfTest)); add( testCase( &OpenMPWrapperTest::combineThreeImagesTest)); add( testCase( &OpenMPWrapperTest::transformImageTest)); add( testCase( &OpenMPWrapperTest::transformImageIfTest)); -// add( testCase( &OpenMPWrapperTest::copyImageTest)); -// add( testCase( &DistanceTransformTest::distanceTransformL1Test)); -// add( testCase( &DistanceTransformTest::distanceTransformL2Test)); + add( testCase( &DistanceTransformTest::distanceTransformL1Test)); + add( testCase( &DistanceTransformTest::distanceTransformL2Test)); } };