diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a93d0d..6f63cd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,8 @@ enable_cxx_compiler_flag_if_supported("-Wpedantic") enable_cxx_compiler_flag_if_supported("-pedantic") if(CMAKE_BUILD_TYPE STREQUAL "Debug") enable_cxx_compiler_flag_if_supported("-g") + enable_cxx_compiler_flag_if_supported("-pg") + enable_cxx_compiler_flag_if_supported("-finstrument-functions") else() enable_cxx_compiler_flag_if_supported("-O3") enable_cxx_compiler_flag_if_supported("-flto") diff --git a/benchmarks.fish b/benchmarks.fish index 6443200..e003aec 100755 --- a/benchmarks.fish +++ b/benchmarks.fish @@ -1,20 +1,29 @@ #!/usr/bin/env fish - # This script was written to work with the fish shell. If you do not have the # fish shell installed on your system, please install it before executing this # script. # The results will be stored in the output file `results.txt` - set nb_amelioration 10 50 100 200 500 1000 set nb_execution 200 100 50 20 10 5 -set available_methods 1 2 3 +set available_methods 1 2 3 4 set output results.txt - rm -f $output - for method in $available_methods - for i in (seq (count $nb_execution)) - perf stat -r $nb_execution[$i] -B ./build/bin/genetic-image -i ./img/mahakala-monochrome.jpg -n $nb_amelioration[$i] -m $method ^| grep -iE "counter|elapsed" >> $output - echo "" >> $output + set nb_options (count $nb_execution) + echo "Method $method:" >> $output + echo "| / | < | < |" >> $output + echo "| Nb d’améliorations | Nb d’exécutions | Temps d’exécution (s) |" >> $output + echo "|--------------------+-----------------+-----------------------|" >> $output + for i in (seq $nb_options) + set total_exec_time 0 + set startexec (date +%s.%N) + for j in $nb_execution[$i] + ./build/bin/genetic-image -i ./img/mahakala-monochrome.jpg \ + -n $nb_amelioration[$i] -m $method + end + set endexec (date +%s.%N) + set total_exec_time (math "$total_exec_time+($endexec-$startexec)/$i") + echo "|$nb_amelioration[$i]|$nb_execution[$i]|$total_exec_time|" >> $output end + echo "" >> $output end diff --git a/include/genimg/methods.hh b/include/genimg/methods.hh index 762a63d..1cf9f56 100644 --- a/include/genimg/methods.hh +++ b/include/genimg/methods.hh @@ -43,6 +43,8 @@ void method2(cv::Mat const&, cv::Mat&, int); void method3(cv::Mat const&, cv::Mat&, int); -void method4(cv::Mat const&, cv::Mat&, int); +void method4(cv::Mat const&, cv::Mat&, int, bool); + +void method5(cv::Mat const&, cv::Mat&, int, int, bool); #endif /* GENETIC_IMAGE_INCLUDE_GENIMG_METHODS_HH_ */ diff --git a/include/genimg/parseargs.hh b/include/genimg/parseargs.hh index dfba3fa..484ac2c 100644 --- a/include/genimg/parseargs.hh +++ b/include/genimg/parseargs.hh @@ -4,7 +4,7 @@ #include #include -[[nodiscard]] auto parse_args(int, char**) - -> std::tuple; +[[nodiscard]] auto parse_args(int, char**) -> std:: +tuple; #endif /* GENETIC_IMAGE_INCLUDE_GENIMG_PARSEARGS_HH_ */ diff --git a/report/output1.png b/report/output1.png index f6a5db0..41eb533 100644 Binary files a/report/output1.png and b/report/output1.png differ diff --git a/report/output2.png b/report/output2.png index 921188a..25f9a7a 100644 Binary files a/report/output2.png and b/report/output2.png differ diff --git a/report/output3.png b/report/output3.png index e5577f8..723c186 100644 Binary files a/report/output3.png and b/report/output3.png differ diff --git a/report/report.org b/report/report.org index 89b1ba6..19afef5 100644 --- a/report/report.org +++ b/report/report.org @@ -39,10 +39,10 @@ $$\sqrt{\sum_{i=0}^n(v_i - w_i)^2}$$ ~V~ étant le vecteur de pixels de l’image de référence, ~W~ étant le vecteur de pixels de l’image générée, et ~n~ la taille de ces deux vecteurs. -Les tests de temps sont réalisés sur un Thinkpad x220, disposant d’un processeur -Intel® Core™ i5-2540M à 2.6GHz, composé de deux cœurs supportant chacun deux -threads, et de 4Go de RAM. Le programme est compilé avec les options -d’optimisation ~-O3~ et ~-flto~. +Les tests de temps sont réalisés sur un Lenovo Ideapad Y700, disposant d’un +processeur Intel® Core™ i7-6700HQ à 2.6GHz et un turbo à 3.5GHz, composé de +quatre cœurs supportant chacun deux threads, et de 16Go de RAM. Le programme est +compilé avec les options d’optimisation ~-O3~ et ~-flto~. Voici également ci-dessous la liste des options et arguments possibles concernant l’exécution du logiciel. @@ -57,21 +57,12 @@ concernant l’exécution du logiciel. -v [ --verbose ] Enables verbosity #+end_src -Voici la ligne de commande utilisée depuis le répertoire ~build~ afin de pouvoir -obtenir un temps d’exécution : -#+begin_src shell - perf stat -r {nombreExécutions} -B ./bin/genetic-image \ - -i ../img/mahakala-monochrome.jpg -o output.png \ - -n {nombreIterations} -m 1 -#+end_src +Voici le script grâce auquel les valeurs de temps d’exécution ont été obtenues : -Les deux éléments entre accolades sont à remplacer par leur valeur, par exemple -afin d’exécuter dix fois le programme avec vingt améliorations, il faudrait -exécuter ceci : -#+begin_src shell - perf stat -r 1 -B ./bin/genetic-image \ - -i ../img/mahakala-monochrome.jpg -o output.png -n 20 -m 1 -#+end_src +#+INCLUDE: ../benchmarks.fish src shell -n + +Quelques-unes de ces lignes commençasont là uniquement pour de la mise en forme des +données afin que je puisse ** Méthode naïve @@ -86,15 +77,15 @@ de référence. Voici les moyennes de temps d’exécution selon le nombre d’itérations réussies sur le nombre d’exécutions indiqué. -| / | < | < | < | -| Nb d’améliorations | Temps d’exécution (s) | Variation (s) | Nb d’exécutions | -|--------------------+-----------------------+---------------+-----------------| -| 10 | 0.060847 | 0.000498 | 200 | -| 50 | 0.29823 | 0.00453 | 100 | -| 100 | 0.7093 | 0.0135 | 50 | -| 200 | 1.9584 | 0.0559 | 20 | -| 500 | 8.739 | 0.291 | 10 | -| 1000 | 27.930 | 0.582 | 5 | +| / | < | < | +| Nb d’améliorations | Nb d’exécutions | Temps d’exécution (s) | +|--------------------+-----------------+-----------------------| +| 10 | 200 | 0.065881 | +| 50 | 100 | 0.130041 | +| 100 | 50 | 0.186012 | +| 200 | 20 | 0.385982 | +| 500 | 10 | 1.437486 | +| 1000 | 5 | 3.608983 | Naturellement, la variation en temps d’exécution croît en même temps que le nombre d’améliorations nécessaires à apporter à l’image à améliorer, dû à la @@ -120,17 +111,17 @@ de rapidité à l’exécution. Cette méthode est celle implémentée dans la f Voici les moyennes de temps d’exécution selon le nombre d’itérations réussies sur le nombre d’exécutions indiqué. -| / | < | < | < | -| Nb d’améliorations | Temps d’exécution (s) | Variation (s) | Nb d’exécutions | -|--------------------+-----------------------+---------------+-----------------| -| 10 | 0.074951 | 0.000533 | 200 | -| 50 | 0.26385 | 0.00401 | 100 | -| 100 | 0.6385 | 0.0148 | 50 | -| 200 | 1.6145 | 0.0348 | 20 | -| 500 | 6.747 | 0.235 | 10 | -| 1000 | 19.608 | 0.327 | 5 | +| / | < | < | +| Nb d’améliorations | Nb d’exécutions | Temps d’exécution (s) | +|--------------------+-----------------+-----------------------| +| 10 | 200 | 0.072979 | +| 50 | 100 | 0.114426 | +| 100 | 50 | 0.157965 | +| 200 | 20 | 0.290475 | +| 500 | 10 | 0.785426 | +| 1000 | 5 | 2.664046 | -On peut remarquer une amélioration dans la rapidité d’exécution du logiciel. +On peut remarquer une amélioration quant à la rapidité d’exécution du logiciel. Cependant, le résultat n’est pas aussi important qu’escompté. Je suppose que cela est dû au fait que l’algorithme précédent peut considérer un rapprochement d’une zone déjà colorée vers la couleur d’origine comme une amélioration, avec @@ -164,15 +155,15 @@ $$taille=Rand([\![tailleMinimale;tailleMaximale[\![)$$ Voici les moyennes de temps d’exécution selon le nombre d’itérations réussies sur le nombre d’exécutions indiqué. -| / | < | < | < | -| Nb d’améliorations | Temps d’exécution (s) | Variation (s) | Nb d’exécutions | -|--------------------+-----------------------+---------------+-----------------| -| 10 | | | 200 | -| 50 | | | 100 | -| 100 | | | 50 | -| 200 | | | 20 | -| 500 | | | 10 | -| 1000 | | | 5 | +| / | < | < | +| Nb d’améliorations | Nb d’exécutions | Temps d’exécution (s) | +|--------------------+-----------------+-----------------------| +| 10 | 200 | 0.082068 | +| 50 | 100 | 0.244236 | +| 100 | 50 | 0.418075 | +| 200 | 20 | 1.453703 | +| 500 | 10 | 4.777205 | +| 1000 | 5 | 20.33209 | Cette version du logiciel est nettement plus lente que ses versions précédentes du fait de la contrainte de taille pour les formes pouvant potentiellement @@ -196,14 +187,70 @@ résultats sont récupérés et évalués, et parmi les résultats améliorant l générée, celle avec le meilleur score est conservée. Cela permet ainsi de multiplier les chances d’avoir une amélioration de l’image par tentative. +Voici les benchmarks d’exécution de cette méthode sans contrôle de la taille des +formes aléatoires : +| / | < | < | +| Nb d’améliorations | Nb d’exécutions | Temps d’exécution (s) | +|--------------------+-----------------+-----------------------| +| 10 | 200 | 0.080525 | +| 50 | 100 | 0.139892 | +| 100 | 50 | 0.169113 | +| 200 | 20 | 0.273342 | +| 500 | 10 | 0.610812 | +| 1000 | 5 | 1.403816 | + +Et voici les benchmarks d’exécution de cette même méthode avec contrôle de la +taille des formes aléatoires : +| / | < | < | +| Nb d’améliorations | Nb d’exécutions | Temps d’exécution (s) | +|--------------------+-----------------+-----------------------| +| 10 | 200 | 0.085981 | +| 50 | 100 | 0.156099 | +| 100 | 50 | 0.29183 | +| 200 | 20 | 0.59844 | +| 500 | 10 | 2.513782 | +| 1000 | 5 | 6.457168 | + +Pour résumer, ces deux tableaux montrent la parallélisation de la seconde +méthode et de la troisième méthode respectivement via des threads +concurrentiels. On peut remarquer que le temps d’exécution s’est nettement +amélioré, avec un temps d’exécution à peu près deux fois plus rapide pour +l’exécution sans contrôle de taille des formes que la seconde méthode, et +pouvant être jusqu’à trois fois plus rapide que la troisième méthode avec le +contrôle de la taille des formes activée. On a donc une véritable amélioration +significative avec cette nouvelle version parallèle. + +** Collaboration entre threads + +Une différente approche au parallélisme peut être réalisée : plutôt que +d’essayer de mettre en concurrence plusieurs threads, il serait possible +d’essayer de plutôt les mettre en collaboration. Cela implique par exemple de +diviser l’image d’entrée en plusieurs zones sur laquelle chacun des threads +lancés travailleraient, appliquant chacun le nombre d’améliorations demandé sur +sa zone dédiée. Puis, une fois que chacun des threads a terminé son travail, les +différentes zones sont unifiées en une seule image. + * Annexes ** Images +*** Image de référence +#+CAPTION: Image de référence utilisée pour les tests du logiciel +[[../img/mahakala-monochrome.jpg]] *** Méthode 1 #+CAPTION: Image générée à partir de ~img/mahakala-monochrome.png~ avec 2000 améliorations avec la première méthode [[./output1.png]] + *** Méthode 2 #+CAPTION: Image générée à partir de ~img/mahakala-monochrome.png~ avec 2000 améliorations avec la seconde méthode [[./output2.png]] + *** Méthode 3 #+CAPTION: Image générée à partir de ~img/mahakala-monochrome.png~ avec 2000 améliorations avec la troisième méthode [[./output3.png]] + +*** Méthode 4 +**** Taille des formes non contrôlée +#+CAPTION: Image générée à partir de ~img/mahakala-monochrome.png~ avec 2000 améliorations avec la quatrième méthode sans l’option ~-s~ +[[./output4-1.png]] +**** Taille des formes contrôlée +#+CAPTION: Image générée à partir de ~img/mahakala-monochrome.png~ avec 2000 améliorations avec la quatrième méthode avec l’option ~-s~ +[[./output4-2.png]] diff --git a/report/report.pdf b/report/report.pdf index ee86ced..ec14f03 100644 Binary files a/report/report.pdf and b/report/report.pdf differ diff --git a/src/main.cc b/src/main.cc index f45c933..938b3f9 100644 --- a/src/main.cc +++ b/src/main.cc @@ -8,8 +8,10 @@ int main(int ac, char** av) { std::srand(std::time(nullptr)); - auto const [input_file, output_file, iterations, method, verbose] - = parse_args(ac, av); + auto const [input_file, output_file, iterations, method, division, + controlled_size, verbose + + ] = parse_args(ac, av); spdlog::set_level(verbose ? spdlog::level::debug : spdlog::level::info); spdlog::debug("Input file:\t{}", input_file.native()); spdlog::debug("Output file:\t{}", output_file.native()); @@ -29,12 +31,12 @@ int main(int ac, char** av) method3(input_image, process_image, iterations); break; } - case 4: { - method4(input_image, process_image, iterations); - break; - } + case 4: { + method4(input_image, process_image, iterations, controlled_size); + break; + } default: - spdlog::error("Requested method {} is not implemented."); + spdlog::error("Requested method {} is not implemented.", method); std::exit(-1); } diff --git a/src/methods.cc b/src/methods.cc index f0fb5aa..9a1dc2b 100644 --- a/src/methods.cc +++ b/src/methods.cc @@ -204,7 +204,10 @@ void method3(cv::Mat const& t_reference, cv::Mat& t_output, int t_iterations) } } -void method4(cv::Mat const& t_reference, cv::Mat& t_output, int t_iterations) +void method4(cv::Mat const& t_reference, + cv::Mat& t_output, + int t_iterations, + bool controlled_size) { auto const init_iter = t_iterations; auto diff = euclidian_distance(t_reference, t_output); @@ -218,27 +221,38 @@ void method4(cv::Mat const& t_reference, cv::Mat& t_output, int t_iterations) results{}; std::vector> values{}; for (unsigned i = 0; i < thread_nbr; ++i) { - results.push_back( - std::async(std::launch::async, methods_private::createCandidate, - std::ref(t_output), std::ref(t_reference), - std::ref(colors), diff, true, init_iter, t_iterations)); + results.push_back(std::async( + std::launch::async, methods_private::createCandidate, + std::ref(t_output), std::ref(t_reference), std::ref(colors), diff, + controlled_size, init_iter, t_iterations)); } for (auto& elem : results) { if (auto res = elem.get(); res.has_value() && res->second < diff) { - values.push_back(*res); + values.push_back(*res); } } - if(values.size() > 0) { - unsigned best = 0; - for(unsigned i = 0; i < values.size(); ++i) { - if(values[i].second < values[best].second) { - best = i; - } - } - diff = values[best].second; - values[best].first.copyTo(t_output); - --t_iterations; + if (values.size() > 0) { + unsigned best = 0; + for (unsigned i = 0; i < values.size(); ++i) { + if (values[i].second < values[best].second) { + best = i; + } + } + diff = values[best].second; + values[best].first.copyTo(t_output); + --t_iterations; spdlog::debug("iteration:{} diff:{}", t_iterations, diff); - } + } } } + +void method5(cv::Mat const& t_reference, + cv::Mat& t_output, + int t_iterations, + int t_nb_tiles, + bool t_controlled_size = false) +{ + std::vector tiles{static_cast(t_nb_tiles * t_nb_tiles)}; + int col_width = t_reference.cols / t_nb_tiles; + spdlog::info("collumns of {}px", col_width); +} diff --git a/src/parseargs.cc b/src/parseargs.cc index dec4df8..c60f611 100644 --- a/src/parseargs.cc +++ b/src/parseargs.cc @@ -3,7 +3,7 @@ #include #include -constexpr int DEFAULT_ITERATIONS = 5000; +constexpr int DEFAULT_ITERATIONS = 2000; using path = std::filesystem::path; namespace po = boost::program_options; @@ -22,16 +22,22 @@ void processFilenames(po::variables_map const& vm, } [[nodiscard]] auto parse_args(int t_ac, char** t_av) - -> std::tuple +-> std::tuple { po::options_description desc("Allowed options"); - desc.add_options()("help,h", "Display this help message")( - "input,i", po::value(), "Input image")( - "output,o", po::value(), - "Image output path (default: input path + \"_output\")")( - "method,m", po::value(), "Method number to be used (default: 1)")( - "iterations,n", po::value(), "Number of iterations (default: 5000)")( - "verbose,v", "Enables verbosity"); + desc.add_options() + ("help,h", "Display this help message") + ("input,i", po::value(), "Input image") + ("output,o", po::value(), + "Image output path (default: \"output_\" + input path)") + ("method,m", po::value(), "Method number to be used (default: 1)") + ("iterations,n", po::value(), "Number of iterations (default: 2000)") + ("size,s", "Controlled size of the random shapes (default: false)") + ("division,d", po::value(), + "For method 5, number of regions the reference image should be divided " + "into. For instance, -d4 will divide the reference image into a 4*4 " + "matrice of smaller images. (default: 1)") + ("verbose,v", "Enables verbosity"); po::variables_map vm; po::store(po::parse_command_line(t_ac, t_av, desc), vm); po::notify(vm); @@ -49,5 +55,7 @@ void processFilenames(po::variables_map const& vm, input_path, output_path, vm.count("iterations") ? vm["iterations"].as() : DEFAULT_ITERATIONS, vm.count("method") ? vm["method"].as() : 1, + vm.count("division") ? vm["division"].as() : 1, + vm.count("size") ? true : false, vm.count("verbose") ? true : false); }