diff --git a/tasks/mpi/koshkin_m_sobel/func_tests/main.cpp b/tasks/mpi/koshkin_m_sobel/func_tests/main.cpp new file mode 100644 index 00000000000..a6d78330eca --- /dev/null +++ b/tasks/mpi/koshkin_m_sobel/func_tests/main.cpp @@ -0,0 +1,82 @@ +#include + +#include +#include +#include +#include +#include + +#include "../include/ops_mpi.hpp" +#include "boost/mpi/communicator.hpp" +#include "core/task/include/task.hpp" + +static std::vector make_img(size_t width, size_t height) { + std::random_device dev; + std::mt19937 gen(dev()); + std::uniform_int_distribution<> distrib(0, 255); + std::vector vec(width * height); + for (size_t i = 0; i < width * height; i++) { + vec[i] = distrib(gen); + } + return vec; +} + +static std::shared_ptr test_sobel_mk_taskdata(std::vector &in, std::vector &out, + uint32_t width, uint32_t height) { + auto taskData = std::make_shared(); + + if (boost::mpi::communicator{}.rank() == 0) { + taskData->inputs = {reinterpret_cast(in.data())}; + taskData->inputs_count = {width, height}; + + taskData->outputs = {reinterpret_cast(out.data())}; + taskData->outputs_count = {width, height}; + } + + return taskData; +} + +static void test_sobel_io(std::vector &&in, uint32_t width, uint32_t height, std::vector &out) { + ASSERT_EQ(in.size(), width * height); + + auto taskData = test_sobel_mk_taskdata(in, out, width, height); + + koshkin_m_sobel_mpi::TestTaskParallel testTaskParallel(taskData); + ASSERT_EQ(testTaskParallel.validation(), true); + testTaskParallel.pre_processing(); + testTaskParallel.run(); + testTaskParallel.post_processing(); +} + +static void test_sobel(uint32_t width, uint32_t height) { + std::vector in = make_img(width, height); + std::vector out; + if (boost::mpi::communicator{}.rank() == 0) { + out.resize(in.size()); + } + test_sobel_io(std::move(in), width, height, out); + + if (boost::mpi::communicator{}.rank() == 0) { + std::vector ref(in.size()); + + koshkin_m_sobel_mpi::TestTaskSequential testTaskSequential(test_sobel_mk_taskdata(in, ref, width, height)); + ASSERT_EQ(testTaskSequential.validation(), true); + testTaskSequential.pre_processing(); + testTaskSequential.run(); + testTaskSequential.post_processing(); + + ASSERT_EQ(out, ref); + } +} + +TEST(koshkin_m_sobel_mpi, Image_Random_1x1) { test_sobel(1, 1); } +TEST(koshkin_m_sobel_mpi, Image_Random_2x2) { test_sobel(2, 2); } +TEST(koshkin_m_sobel_mpi, Image_Random_2x3) { test_sobel(2, 3); } +TEST(koshkin_m_sobel_mpi, Image_Random_3x3) { test_sobel(3, 3); } +TEST(koshkin_m_sobel_mpi, Image_Random_3x7) { test_sobel(3, 7); } +TEST(koshkin_m_sobel_mpi, Image_Random_7x3) { test_sobel(7, 3); } +TEST(koshkin_m_sobel_mpi, Image_Random_7x13) { test_sobel(7, 13); } +TEST(koshkin_m_sobel_mpi, Image_Random_13x7) { test_sobel(13, 7); } +TEST(koshkin_m_sobel_mpi, Image_Random_17x13) { test_sobel(17, 13); } +TEST(koshkin_m_sobel_mpi, Image_Random_1x13) { test_sobel(1, 13); } +TEST(koshkin_m_sobel_mpi, Image_Random_17x17) { test_sobel(17, 17); } \ No newline at end of file diff --git a/tasks/mpi/koshkin_m_sobel/include/ops_mpi.hpp b/tasks/mpi/koshkin_m_sobel/include/ops_mpi.hpp new file mode 100644 index 00000000000..30f0327a3b2 --- /dev/null +++ b/tasks/mpi/koshkin_m_sobel/include/ops_mpi.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include + +#include "core/task/include/task.hpp" + +namespace koshkin_m_sobel_mpi { + +// clang-format off +static inline const std::array, 3> SOBEL_KERNEL_X = {{ + {{-1, 0, 1}}, + {{-2, 0, 2}}, + {{-1, 0, 1}} +}}; +static inline const std::array, 3> SOBEL_KERNEL_Y = {{ + {{-1, -2, -1}}, + {{ 0, 0, 0}}, + {{ 1, 2, 1}} +}}; +// clang-format on + +class TestTaskSequential : public ppc::core::Task { + public: + explicit TestTaskSequential(std::shared_ptr taskData_) : Task(std::move(taskData_)) {} + bool pre_processing() override; + bool validation() override; + bool run() override; + bool post_processing() override; + + private: + std::pair imgsize; + std::vector image; + std::vector resimg; +}; + +class TestTaskParallel : public ppc::core::Task { + public: + explicit TestTaskParallel(std::shared_ptr taskData_) : Task(std::move(taskData_)) {} + bool pre_processing() override; + bool validation() override; + bool run() override; + bool post_processing() override; + + private: + std::pair imgsize; + std::vector image; + std::vector resimg; + + boost::mpi::communicator world; +}; + +} // namespace koshkin_m_sobel_mpi \ No newline at end of file diff --git a/tasks/mpi/koshkin_m_sobel/perf_tests/main.cpp b/tasks/mpi/koshkin_m_sobel/perf_tests/main.cpp new file mode 100644 index 00000000000..45e4f85b73d --- /dev/null +++ b/tasks/mpi/koshkin_m_sobel/perf_tests/main.cpp @@ -0,0 +1,94 @@ +#include + +#include +#include +#include + +#include "../include/ops_mpi.hpp" +#include "boost/mpi/communicator.hpp" +#include "core/perf/include/perf.hpp" + +static std::vector make_img(size_t width, size_t height) { + std::random_device dev; + std::mt19937 gen(dev()); + std::uniform_int_distribution<> distrib(0, 255); + std::vector vec(width * height); + for (size_t i = 0; i < width * height; i++) { + vec[i] = distrib(gen); + } + return vec; +} + +TEST(koshkin_m_sobel_mpi_perf_test, test_pipeline_run) { + boost::mpi::communicator world; + + const auto width = 1200; + const auto height = 1200; + std::vector in = make_img(width, height); + std::vector out(in.size()); + + // Create TaskData + std::shared_ptr taskDataPar = std::make_shared(); + if (world.rank() == 0) { + taskDataPar->inputs = {reinterpret_cast(in.data())}; + taskDataPar->inputs_count = {width, height}; + taskDataPar->outputs = {reinterpret_cast(out.data())}; + taskDataPar->outputs_count = {width, height}; + } + + // Create Task + auto testTaskParallel = std::make_shared(taskDataPar); + + // Create Perf attributes + auto perfAttr = std::make_shared(); + perfAttr->num_running = 10; + const boost::mpi::timer current_timer; + perfAttr->current_timer = [&] { return current_timer.elapsed(); }; + + // Create and init perf results + auto perfResults = std::make_shared(); + + // Create Perf analyzer + auto perfAnalyzer = std::make_shared(testTaskParallel); + perfAnalyzer->pipeline_run(perfAttr, perfResults); + if (world.rank() == 0) { + ppc::core::Perf::print_perf_statistic(perfResults); + } +} + +TEST(koshkin_m_sobel_mpi_perf_test, test_task_run) { + boost::mpi::communicator world; + + const auto width = 1200; + const auto height = 1200; + std::vector in = make_img(width, height); + std::vector out(in.size()); + + // Create TaskData + std::shared_ptr taskDataPar = std::make_shared(); + if (world.rank() == 0) { + taskDataPar->inputs = {reinterpret_cast(in.data())}; + taskDataPar->inputs_count = {width, height}; + taskDataPar->outputs = {reinterpret_cast(out.data())}; + taskDataPar->outputs_count = {width, height}; + } + + // Create Task + auto testTaskParallel = std::make_shared(taskDataPar); + + // Create Perf attributes + auto perfAttr = std::make_shared(); + perfAttr->num_running = 10; + const boost::mpi::timer current_timer; + perfAttr->current_timer = [&] { return current_timer.elapsed(); }; + + // Create and init perf results + auto perfResults = std::make_shared(); + + // Create Perf analyzer + auto perfAnalyzer = std::make_shared(testTaskParallel); + perfAnalyzer->task_run(perfAttr, perfResults); + if (world.rank() == 0) { + ppc::core::Perf::print_perf_statistic(perfResults); + } +} diff --git a/tasks/mpi/koshkin_m_sobel/src/ops_mpi.cpp b/tasks/mpi/koshkin_m_sobel/src/ops_mpi.cpp new file mode 100644 index 00000000000..522ca71dae0 --- /dev/null +++ b/tasks/mpi/koshkin_m_sobel/src/ops_mpi.cpp @@ -0,0 +1,149 @@ +#include "../include/ops_mpi.hpp" + +#include +#include +#include +#include +#include +#include +#include + +int16_t conv_kernel3(const std::array, 3>& kernel, const std::vector& img, size_t i, + size_t j, size_t height) { + // clang-format off + return kernel[0][0] * img[(i - 1) * height + (j - 1)] + + kernel[0][1] * img[(i - 1) * height + j] + + kernel[0][2] * img[(i - 1) * height + (j + 1)] + + kernel[1][0] * img[i * height + (j - 1)] + + kernel[1][1] * img[i * height + j] + + kernel[1][2] * img[i * height + (j + 1)] + + kernel[2][0] * img[(i + 1) * height + (j - 1)] + + kernel[2][1] * img[(i + 1) * height + j] + + kernel[2][2] * img[(i + 1) * height + (j + 1)]; + // clang-format on +} + +bool koshkin_m_sobel_mpi::TestTaskSequential::pre_processing() { + internal_order_test(); + + imgsize = {taskData->inputs_count[0], taskData->inputs_count[1]}; + auto& [width, height] = imgsize; + const int padding = 2; + image.resize((width + padding) * (height + padding)); + resimg.resize(width * height, 0); + + const auto* in = reinterpret_cast(taskData->inputs[0]); + for (size_t row = 0; row < height; row++) { + std::copy(in + (row * width), in + ((row + 1) * width), image.begin() + ((row + 1) * (width + padding) + 1)); + } + + return true; +} + +bool koshkin_m_sobel_mpi::TestTaskSequential::validation() { + internal_order_test(); + return taskData->inputs_count[0] > 0 && taskData->inputs_count[1] > 0 && + taskData->outputs_count[0] == taskData->inputs_count[0] && + taskData->outputs_count[1] == taskData->inputs_count[1]; +} + +bool koshkin_m_sobel_mpi::TestTaskSequential::run() { + internal_order_test(); + + const auto [width, height] = imgsize; + + for (size_t i = 1; i < (width + 2) - 1; i++) { + for (size_t j = 1; j < (height + 2) - 1; j++) { + const auto accX = conv_kernel3(SOBEL_KERNEL_X, image, i, j, (height + 2)); + const auto accY = conv_kernel3(SOBEL_KERNEL_Y, image, i, j, (height + 2)); + resimg[(i - 1) * height + (j - 1)] = std::clamp(std::sqrt(std::pow(accX, 2) + std::pow(accY, 2)), 0., 255.); + } + } + + return true; +} + +bool koshkin_m_sobel_mpi::TestTaskSequential::post_processing() { + internal_order_test(); + std::copy(resimg.begin(), resimg.end(), reinterpret_cast(taskData->outputs[0])); + return true; +} + +bool koshkin_m_sobel_mpi::TestTaskParallel::pre_processing() { + internal_order_test(); + + if (world.rank() == 0) { + imgsize = {taskData->inputs_count[0], taskData->inputs_count[1]}; + auto& [width, height] = imgsize; + + image.resize((width + 2) * (height + 2)); + resimg.resize(width * height, 0); + + const auto* in = reinterpret_cast(taskData->inputs[0]); + for (size_t row = 0; row < height; row++) { + std::copy(in + (row * width), in + ((row + 1) * width), image.begin() + ((row + 1) * (width + 2) + 1)); + } + } + + return true; +} + +bool koshkin_m_sobel_mpi::TestTaskParallel::validation() { + internal_order_test(); + return world.rank() != 0 || (taskData->inputs_count[0] > 0 && taskData->inputs_count[1] > 0 && + taskData->outputs_count[0] == taskData->inputs_count[0] && + taskData->outputs_count[1] == taskData->inputs_count[1]); +} + +bool koshkin_m_sobel_mpi::TestTaskParallel::run() { + internal_order_test(); + + const int pad = 2; + + boost::mpi::broadcast(world, imgsize, 0); + const auto [width, height] = imgsize; + const auto [pwidth, pheight] = std::make_pair(width + pad, height + pad); + + const int perproc = width / world.size(); + const int leftover = width % world.size(); + + std::vector sendcounts(world.size(), 0); + std::vector senddispls(world.size(), 0); + std::vector recvcounts(world.size(), 0); + std::vector recvdispls(world.size(), 0); + + for (int i = 0; i < world.size(); i++) { + const int extra = i < leftover ? 1 : 0; + sendcounts[i] = (perproc + extra + pad) * pheight; + recvcounts[i] = (perproc + extra) * height; + } + for (int i = 1; i < world.size(); i++) { + senddispls[i] = senddispls[i - 1] + sendcounts[i - 1] - pheight * pad; + recvdispls[i] = recvdispls[i - 1] + recvcounts[i - 1]; + } + + std::vector locimg(sendcounts[world.rank()]); + boost::mpi::scatterv(world, image, sendcounts, senddispls, locimg.data(), locimg.size(), 0); + + const int actw = sendcounts[world.rank()] / pheight; + std::vector locres(recvcounts[world.rank()]); + for (int i = 1; i < actw - 1; i++) { + for (size_t j = 1; j < (height + 2) - 1; j++) { + const auto accX = conv_kernel3(SOBEL_KERNEL_X, locimg, i, j, (height + 2)); + const auto accY = conv_kernel3(SOBEL_KERNEL_Y, locimg, i, j, (height + 2)); + locres[(i - 1) * height + (j - 1)] = std::clamp(std::sqrt(std::pow(accX, 2) + std::pow(accY, 2)), 0., 255.); + } + } + + boost::mpi::gatherv(world, locres, resimg.data(), recvcounts, recvdispls, 0); + + return true; +} + +bool koshkin_m_sobel_mpi::TestTaskParallel::post_processing() { + internal_order_test(); + if (world.rank() == 0) { + std::copy(resimg.begin(), resimg.end(), reinterpret_cast(taskData->outputs[0])); + } + return true; +} \ No newline at end of file diff --git a/tasks/seq/koshkin_m_sobel/func_tests/main.cpp b/tasks/seq/koshkin_m_sobel/func_tests/main.cpp new file mode 100644 index 00000000000..1bc310a826d --- /dev/null +++ b/tasks/seq/koshkin_m_sobel/func_tests/main.cpp @@ -0,0 +1,49 @@ +#include + +#include +#include +#include +#include +#include + +#include "../include/ops_seq.hpp" +#include "core/task/include/task.hpp" + +static std::shared_ptr test_sobel_mk_taskdata(std::vector &in, std::vector &out, + uint32_t width, uint32_t height) { + auto taskData = std::make_shared(); + + taskData->inputs = {reinterpret_cast(in.data())}; + taskData->inputs_count = {width, height}; + + taskData->outputs = {reinterpret_cast(out.data())}; + taskData->outputs_count = {width, height}; + + return taskData; +} + +static void test_sobel_io(std::vector &&in, uint32_t width, uint32_t height, std::vector &out) { + ASSERT_EQ(in.size(), width * height); + + auto taskData = test_sobel_mk_taskdata(in, out, width, height); + + koshkin_m_sobel_seq::TestTaskSequential testTaskSequential(taskData); + ASSERT_EQ(testTaskSequential.validation(), true); + testTaskSequential.pre_processing(); + testTaskSequential.run(); + testTaskSequential.post_processing(); +} + +static void test_sobel(std::vector &&in, const std::vector &ref, uint32_t width, uint32_t height) { + std::vector out(in.size()); + test_sobel_io(std::move(in), width, height, out); + + ASSERT_EQ(out, ref); +} + +TEST(koshkin_m_sobel_seq, Image_1x1) { test_sobel({128}, {0}, 1, 1); } +TEST(koshkin_m_sobel_seq, Image_2x2) { test_sobel({32, 21, 132, 111}, {255, 255, 255, 255}, 2, 2); } +TEST(koshkin_m_sobel_seq, Image_2x3) { test_sobel({32, 21, 201, 231, 132, 111}, {255, 159, 255, 255, 243, 255}, 2, 3); } +TEST(koshkin_m_sobel_seq, Image_3x3) { + test_sobel({32, 21, 61, 201, 231, 61, 132, 61, 111}, {255, 255, 255, 255, 255, 255, 255, 255, 255}, 3, 3); +} \ No newline at end of file diff --git a/tasks/seq/koshkin_m_sobel/include/ops_seq.hpp b/tasks/seq/koshkin_m_sobel/include/ops_seq.hpp new file mode 100644 index 00000000000..20a15ba30e4 --- /dev/null +++ b/tasks/seq/koshkin_m_sobel/include/ops_seq.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#include "core/task/include/task.hpp" + +namespace koshkin_m_sobel_seq { + +class TestTaskSequential : public ppc::core::Task { + public: + explicit TestTaskSequential(std::shared_ptr taskData_) : Task(std::move(taskData_)) {} + bool pre_processing() override; + bool validation() override; + bool run() override; + bool post_processing() override; + + private: + std::pair imgsize; + std::vector image; + std::vector resimg; + + // clang-format off + static inline const std::array, 3> SOBEL_KERNEL_X = {{ + {{-1, 0, 1}}, + {{-2, 0, 2}}, + {{-1, 0, 1}} + }}; + static inline const std::array, 3> SOBEL_KERNEL_Y = {{ + {{-1, -2, -1}}, + {{ 0, 0, 0}}, + {{ 1, 2, 1}} + }}; + // clang-format on +}; + +} // namespace koshkin_m_sobel_seq \ No newline at end of file diff --git a/tasks/seq/koshkin_m_sobel/perf_tests/main.cpp b/tasks/seq/koshkin_m_sobel/perf_tests/main.cpp new file mode 100644 index 00000000000..ae0b23c0c9c --- /dev/null +++ b/tasks/seq/koshkin_m_sobel/perf_tests/main.cpp @@ -0,0 +1,90 @@ +#include + +#include +#include + +#include "../include/ops_seq.hpp" +#include "core/perf/include/perf.hpp" + +static std::vector make_img(size_t width, size_t height) { + std::random_device dev; + std::mt19937 gen(dev()); + std::uniform_int_distribution<> distrib(0, 255); + std::vector vec(width * height); + for (size_t i = 0; i < width * height; i++) { + vec[i] = distrib(gen); + } + return vec; +} + +TEST(koshkin_m_sobel_seq_perf_test, test_pipeline_run) { + const auto width = 1200; + const auto height = 1200; + std::vector in = make_img(width, height); + std::vector out(in.size()); + + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs = {reinterpret_cast(in.data())}; + taskDataSeq->inputs_count = {width, height}; + taskDataSeq->outputs = {reinterpret_cast(out.data())}; + taskDataSeq->outputs_count = {width, height}; + + // Create Task + auto testTaskSequential = std::make_shared(taskDataSeq); + + // Create Perf attributes + auto perfAttr = std::make_shared(); + perfAttr->num_running = 10; + const auto t0 = std::chrono::high_resolution_clock::now(); + perfAttr->current_timer = [&] { + auto current_time_point = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(current_time_point - t0).count(); + return static_cast(duration) * 1e-9; + }; + + // Create and init perf results + auto perfResults = std::make_shared(); + + // Create Perf analyzer + auto perfAnalyzer = std::make_shared(testTaskSequential); + perfAnalyzer->pipeline_run(perfAttr, perfResults); + + ppc::core::Perf::print_perf_statistic(perfResults); +} + +TEST(koshkin_m_sobel_seq_perf_test, test_task_run) { + const auto width = 1200; + const auto height = 1200; + std::vector in = make_img(width, height); + std::vector out(in.size()); + + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs = {reinterpret_cast(in.data())}; + taskDataSeq->inputs_count = {width, height}; + taskDataSeq->outputs = {reinterpret_cast(out.data())}; + taskDataSeq->outputs_count = {width, height}; + + // Create Task + auto testTaskSequential = std::make_shared(taskDataSeq); + + // Create Perf attributes + auto perfAttr = std::make_shared(); + perfAttr->num_running = 10; + const auto t0 = std::chrono::high_resolution_clock::now(); + perfAttr->current_timer = [&] { + auto current_time_point = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(current_time_point - t0).count(); + return static_cast(duration) * 1e-9; + }; + + // Create and init perf results + auto perfResults = std::make_shared(); + + // Create Perf analyzer + auto perfAnalyzer = std::make_shared(testTaskSequential); + perfAnalyzer->task_run(perfAttr, perfResults); + + ppc::core::Perf::print_perf_statistic(perfResults); +} diff --git a/tasks/seq/koshkin_m_sobel/src/ops_seq.cpp b/tasks/seq/koshkin_m_sobel/src/ops_seq.cpp new file mode 100644 index 00000000000..b78530f3832 --- /dev/null +++ b/tasks/seq/koshkin_m_sobel/src/ops_seq.cpp @@ -0,0 +1,67 @@ +#include "../include/ops_seq.hpp" + +#include +#include +#include +#include + +int16_t conv_kernel3(const std::array, 3>& kernel, const std::vector& img, size_t i, + size_t j, size_t height) { + // clang-format off + return kernel[0][0] * img[(i - 1) * height + (j - 1)] + + kernel[0][1] * img[(i - 1) * height + j] + + kernel[0][2] * img[(i - 1) * height + (j + 1)] + + kernel[1][0] * img[i * height + (j - 1)] + + kernel[1][1] * img[i * height + j] + + kernel[1][2] * img[i * height + (j + 1)] + + kernel[2][0] * img[(i + 1) * height + (j - 1)] + + kernel[2][1] * img[(i + 1) * height + j] + + kernel[2][2] * img[(i + 1) * height + (j + 1)]; + // clang-format on +} + +bool koshkin_m_sobel_seq::TestTaskSequential::pre_processing() { + internal_order_test(); + + imgsize = {taskData->inputs_count[0], taskData->inputs_count[1]}; + auto& [width, height] = imgsize; + const int padding = 2; + image.resize((width + padding) * (height + padding)); + resimg.resize(width * height, 0); + + const auto* in = reinterpret_cast(taskData->inputs[0]); + for (size_t row = 0; row < height; row++) { + std::copy(in + (row * width), in + ((row + 1) * width), image.begin() + ((row + 1) * (width + padding) + 1)); + } + + return true; +} + +bool koshkin_m_sobel_seq::TestTaskSequential::validation() { + internal_order_test(); + return taskData->inputs_count[0] > 0 && taskData->inputs_count[1] > 0 && + taskData->outputs_count[0] == taskData->inputs_count[0] && + taskData->outputs_count[1] == taskData->inputs_count[1]; +} + +bool koshkin_m_sobel_seq::TestTaskSequential::run() { + internal_order_test(); + + const auto [width, height] = imgsize; + + for (size_t i = 1; i < (width + 2) - 1; i++) { + for (size_t j = 1; j < (height + 2) - 1; j++) { + const auto accX = conv_kernel3(SOBEL_KERNEL_X, image, i, j, (height + 2)); + const auto accY = conv_kernel3(SOBEL_KERNEL_Y, image, i, j, (height + 2)); + resimg[(i - 1) * height + (j - 1)] = std::clamp(std::sqrt(std::pow(accX, 2) + std::pow(accY, 2)), 0., 255.); + } + } + + return true; +} + +bool koshkin_m_sobel_seq::TestTaskSequential::post_processing() { + internal_order_test(); + std::copy(resimg.begin(), resimg.end(), reinterpret_cast(taskData->outputs[0])); + return true; +}