diff --git a/tasks/spichek_d_dot_product_of_vectors/common/include/common.hpp b/tasks/spichek_d_dot_product_of_vectors/common/include/common.hpp new file mode 100644 index 0000000000..2d20ead067 --- /dev/null +++ b/tasks/spichek_d_dot_product_of_vectors/common/include/common.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include +#include + +#include "task/include/task.hpp" + +namespace spichek_d_dot_product_of_vectors { + +// Пара векторов как входные данные +using InType = std::pair, std::vector>; +using OutType = int; +// Тестовые параметры: (пара_векторов, описание) +using TestType = std::tuple, std::vector>, std::string>; +using BaseTask = ppc::task::Task; + +} // namespace spichek_d_dot_product_of_vectors diff --git a/tasks/spichek_d_dot_product_of_vectors/info.json b/tasks/spichek_d_dot_product_of_vectors/info.json new file mode 100644 index 0000000000..df4f5a0812 --- /dev/null +++ b/tasks/spichek_d_dot_product_of_vectors/info.json @@ -0,0 +1,9 @@ +{ + "student": { + "first_name": "Денис", + "last_name": "Спичек", + "middle_name": "Игоревич", + "group_number": "3823Б1ФИ1", + "task_number": "1" + } +} diff --git a/tasks/spichek_d_dot_product_of_vectors/mpi/include/ops_mpi.hpp b/tasks/spichek_d_dot_product_of_vectors/mpi/include/ops_mpi.hpp new file mode 100644 index 0000000000..f817c55d4c --- /dev/null +++ b/tasks/spichek_d_dot_product_of_vectors/mpi/include/ops_mpi.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "spichek_d_dot_product_of_vectors/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace spichek_d_dot_product_of_vectors { + +class SpichekDDotProductOfVectorsMPI : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + explicit SpichekDDotProductOfVectorsMPI(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace spichek_d_dot_product_of_vectors diff --git a/tasks/spichek_d_dot_product_of_vectors/mpi/src/ops_mpi.cpp b/tasks/spichek_d_dot_product_of_vectors/mpi/src/ops_mpi.cpp new file mode 100644 index 0000000000..16d6ad826c --- /dev/null +++ b/tasks/spichek_d_dot_product_of_vectors/mpi/src/ops_mpi.cpp @@ -0,0 +1,86 @@ +#include "spichek_d_dot_product_of_vectors/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include +#include + +#include "spichek_d_dot_product_of_vectors/common/include/common.hpp" + +namespace spichek_d_dot_product_of_vectors { + +SpichekDDotProductOfVectorsMPI::SpichekDDotProductOfVectorsMPI(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = 0; +} + +bool SpichekDDotProductOfVectorsMPI::ValidationImpl() { + const auto &[vector1, vector2] = GetInput(); + return vector1.size() == vector2.size(); +} + +bool SpichekDDotProductOfVectorsMPI::PreProcessingImpl() { + GetOutput() = 0; + return true; +} + +bool SpichekDDotProductOfVectorsMPI::RunImpl() { + int rank = 0; + int size = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + + const auto &[v1, v2] = GetInput(); + int n = 0; + if (rank == 0) { + n = static_cast(v1.size()); + } + MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD); + + if (n == 0 || (rank == 0 && static_cast(n) != v2.size())) { + GetOutput() = 0; + return true; + } + + std::vector counts(size); + std::vector displs(size); + + int base = n / size; + int rem = n % size; + + for (int i = 0; i < size; ++i) { + counts[i] = base + (i < rem ? 1 : 0); + // [Исправлено] Добавлены скобки для явного приоритета операций + displs[i] = (i * base) + std::min(i, rem); + } + + int local_count = counts[rank]; + + std::vector lv1(local_count); + std::vector lv2(local_count); + + MPI_Scatterv(rank == 0 ? v1.data() : nullptr, counts.data(), displs.data(), MPI_INT, lv1.data(), local_count, MPI_INT, + 0, MPI_COMM_WORLD); + MPI_Scatterv(rank == 0 ? v2.data() : nullptr, counts.data(), displs.data(), MPI_INT, lv2.data(), local_count, MPI_INT, + 0, MPI_COMM_WORLD); + + int64_t local_dot = 0; + for (int i = 0; i < local_count; ++i) { + local_dot += static_cast(lv1[i]) * lv2[i]; + } + + int64_t global_dot = 0; + MPI_Allreduce(&local_dot, &global_dot, 1, MPI_INT64_T, MPI_SUM, MPI_COMM_WORLD); + + GetOutput() = static_cast(global_dot); + return true; +} + +bool SpichekDDotProductOfVectorsMPI::PostProcessingImpl() { + return true; +} + +} // namespace spichek_d_dot_product_of_vectors diff --git a/tasks/spichek_d_dot_product_of_vectors/report.md b/tasks/spichek_d_dot_product_of_vectors/report.md new file mode 100644 index 0000000000..8d2668cc4c --- /dev/null +++ b/tasks/spichek_d_dot_product_of_vectors/report.md @@ -0,0 +1,259 @@ +# Отчёт по лабораторной работе +## «Параллельное вычисление скалярного произведения векторов с использованием MPI» + +**Дисциплина:** Параллельное программирование +**Преподаватель:** Нестеров Александр Юрьевич и Оболенский Арсений Андреевич +**Студент:** Спичек Денис Игоревич 3823Б1ФИ1 +**Вариант:** 9 + +--- + +## Введение + +Современные вычислительные задачи часто требуют обработки больших объёмов данных за ограниченное время. Одним из ключевых методов ускорения вычислений является распараллеливание. В данной работе рассматривается реализация алгоритма вычисления скалярного произведения двух векторов как в последовательной, так и в параллельной формах с использованием технологии MPI (Message Passing Interface). + +Цель работы — продемонстрировать принципы параллельного программирования, а также сравнить эффективность и корректность работы MPI-версии с последовательной реализацией. + +--- + +## Постановка задачи + +Необходимо реализовать вычисление **скалярного произведения двух векторов одинаковой длины**. + +Скалярное произведение двух векторов $\mathbf{a} = (a_1, a_2, \dots, a_n)$ и $\mathbf{b} = (b_1, b_2, \dots, b_n)$ вычисляется по формуле: + +$$ +\mathbf{a} \cdot \mathbf{b} = \sum_{i=1}^{n} a_i b_i +$$ + +### Требуется: +1. Реализовать **последовательную версию** вычисления. +2. Реализовать **параллельную версию** с использованием **MPI**. +3. Провести сравнение времени выполнения и подтвердить корректность вычислений. + +--- + +## Описание алгоритма + +Алгоритм вычисления скалярного произведения можно описать следующим образом: + +1. Проверка корректности входных данных: векторы должны быть одной длины. +2. Инициализация результата `dot_product = 0`. +3. Для всех элементов $i$ от 0 до $n-1$: + - Вычислить частичный результат $a[i] \cdot b[i]$; + - Добавить его к сумме `dot_product`. +4. Вернуть полученное значение как результат скалярного произведения. + +--- + +## Описание схемы параллельного алгоритма + +В параллельной версии с использованием **MPI**: + +1. **Инициализация MPI** — каждый процесс получает свой ранг `rank` и общее число процессов `size`. +2. **Разделение данных:** - Используется `MPI_Scatterv` для распределения векторов между процессами. + - Если размер векторов не делится нацело на количество процессов, остаток равномерно распределяется между первыми процессами (балансировка нагрузки). +3. **Вычисление частичных результатов:** Каждый процесс вычисляет скалярное произведение своей части векторов. Для суммирования используется тип `int64_t` во избежание переполнения. +4. **Сбор результатов:** Используется операция `MPI_Allreduce` с операцией `MPI_SUM` для суммирования частичных произведений всех процессов и получения итогового значения на каждом узле. +5. **Вывод результата и завершение работы MPI.** + +Схематично процесс можно изобразить так: + +``` +[Rank 0] --(Scatterv)--> [Rank 1..N] + | | +(Calc) (Calc) + \ / + -------> MPI_Allreduce (Sum) --> [Общий результат] +``` + +--- + +## Описание программной реализации (MPI-версия) + +Реализация выполнена на языке **C++17** с использованием **библиотеки MPI**. +Класс `SpichekDDotProductOfVectorsMPI` реализует интерфейс параллельного вычисления. + +**Ключевые особенности реализации:** +- Используется `MPI_Scatterv` для передачи данных только нужным процессам, что оптимизирует работу с памятью. +- Предварительный расчет смещений (`displs`) и размеров (`counts`) гарантирует, что все элементы вектора будут обработаны ровно один раз. +- Код оптимизирован для прохождения строгих проверок статического анализатора `clang-tidy` (использование `int64_t`, явные приведения типов, безопасные заголовки). + +--- + +## Результаты экспериментов + +### Условия экспериментов +- **Размер векторов:** $100,000,000$ ($10^8$) элементов. +- **Среда выполнения:** Windows, MS-MPI. +- **Оборудование:** Локальная рабочая станция. +- **Методика:** Измерение времени проводилось встроенными средствами тестового фреймворка (GoogleTest). Приведены усредненные значения времени выполнения (pipeline). + +### Результаты замеров времени + +| Кол-во процессов (MPI) | Время MPI (сек) | Время SEQ (сек) | Ускорение (отн. MPI-1) | Примечание | +|:---:|---:|---:|---:|:---| +| **1** | 0.2649 | 0.0276 | 1.00x | Базовое время MPI (накладные расходы) | +| **2** | 0.1917 | 0.0349 | 1.38x | Заметное снижение времени | +| **4** | 0.1958 | 0.0556 | 1.35x | Стабильный результат | +| **8** | 0.1608 | 0.0399 | 1.65x | Лучшее время выполнения | + +### Анализ результатов + +1. **Сравнение MPI vs SEQ:** + Последовательная версия (`SEQ`) работает быстрее параллельной (`0.027с` против `0.16с`). Это ожидаемое поведение для простых операций (умножение + сложение) на одной машине с общей памятью. Накладные расходы на создание процессов MPI, выделение буферов и пересылку данных (`MPI_Scatterv`) превышают выигрыш от параллельного счета. + +2. **Масштабируемость MPI:** + Внутри самой MPI-реализации наблюдается **положительная динамика**. При увеличении числа процессов с 1 до 8 время выполнения сократилось с `0.2649 с` до `0.1608 с`, что дает ускорение примерно в **1.65 раза**. Это подтверждает, что алгоритм распараллелен корректно и при увеличении сложности вычислений (или объема данных) эффективность будет расти. + +--- + +## Подтверждение корректности +Результаты тестов GoogleTest показывают статус `[ PASSED ]` для всех режимов запуска (1, 2, 4, 8 процессов). Итоговые суммы, вычисленные параллельно, полностью совпадают с результатами последовательного алгоритма. + +--- + +## Выводы +1. Реализованы две версии алгоритма — последовательная и MPI. +2. Параллельная реализация успешно масштабируется: время выполнения уменьшается при добавлении процессов (с 0.26с до 0.16с). +3. Алгоритм корректно обрабатывает распределение нагрузки (остатки от деления размера вектора). +4. Продемонстрирована работа коллективных операций MPI (`Scatterv`, `Allreduce`). + +--- + +## Приложение. Исходный код + +### Последовательная версия (`ops_seq.cpp`) + +```cpp +#include "spichek_d_dot_product_of_vectors/seq/include/ops_seq.hpp" +#include + +namespace spichek_d_dot_product_of_vectors { + +SpichekDDotProductOfVectorsSEQ::SpichekDDotProductOfVectorsSEQ(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = 0; +} + +bool SpichekDDotProductOfVectorsSEQ::ValidationImpl() { + const auto &[vector1, vector2] = GetInput(); + return vector1.size() == vector2.size(); +} + +bool SpichekDDotProductOfVectorsSEQ::PreProcessingImpl() { + GetOutput() = 0; + return true; +} + +bool SpichekDDotProductOfVectorsSEQ::RunImpl() { + const auto &[vector1, vector2] = GetInput(); + + if (vector1.empty()) { + GetOutput() = 0; + return true; + } + + int dot_product = 0; + for (size_t i = 0; i < vector1.size(); ++i) { + dot_product += vector1[i] * vector2[i]; + } + GetOutput() = dot_product; + return true; +} + +bool SpichekDDotProductOfVectorsSEQ::PostProcessingImpl() { + return true; +} + +} // namespace spichek_d_dot_product_of_vectors +``` + +### MPI-версия (`ops_mpi.cpp`) + +```cpp +#include "spichek_d_dot_product_of_vectors/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include + +#include "spichek_d_dot_product_of_vectors/common/include/common.hpp" + +namespace spichek_d_dot_product_of_vectors { + +SpichekDDotProductOfVectorsMPI::SpichekDDotProductOfVectorsMPI(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = 0; +} + +bool SpichekDDotProductOfVectorsMPI::ValidationImpl() { + const auto &[vector1, vector2] = GetInput(); + return vector1.size() == vector2.size(); +} + +bool SpichekDDotProductOfVectorsMPI::PreProcessingImpl() { + GetOutput() = 0; + return true; +} + +bool SpichekDDotProductOfVectorsMPI::RunImpl() { + int rank = 0; + int size = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + + const auto &[v1, v2] = GetInput(); + int n = 0; + if (rank == 0) { + n = static_cast(v1.size()); + } + MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD); + + if (n == 0 || (rank == 0 && static_cast(v2.size()) != n)) { + GetOutput() = 0; + return true; + } + + std::vector counts(size); + std::vector displs(size); + + int base = n / size; + int rem = n % size; + + for (int i = 0; i < size; ++i) { + counts[i] = base + (i < rem ? 1 : 0); + displs[i] = (i * base) + std::min(i, rem); + } + + int local_count = counts[rank]; + std::vector lv1(local_count); + std::vector lv2(local_count); + + MPI_Scatterv(rank == 0 ? v1.data() : nullptr, counts.data(), displs.data(), MPI_INT, lv1.data(), local_count, MPI_INT, + 0, MPI_COMM_WORLD); + MPI_Scatterv(rank == 0 ? v2.data() : nullptr, counts.data(), displs.data(), MPI_INT, lv2.data(), local_count, MPI_INT, + 0, MPI_COMM_WORLD); + + int64_t local_dot = 0; + for (int i = 0; i < local_count; ++i) { + local_dot += static_cast(lv1[i]) * lv2[i]; + } + + int64_t global_dot = 0; + MPI_Allreduce(&local_dot, &global_dot, 1, MPI_LONG_LONG, MPI_SUM, MPI_COMM_WORLD); + + GetOutput() = static_cast(global_dot); + return true; +} + +bool SpichekDDotProductOfVectorsMPI::PostProcessingImpl() { + return true; +} + +} // namespace spichek_d_dot_product_of_vectors +``` \ No newline at end of file diff --git a/tasks/spichek_d_dot_product_of_vectors/seq/include/ops_seq.hpp b/tasks/spichek_d_dot_product_of_vectors/seq/include/ops_seq.hpp new file mode 100644 index 0000000000..21a186b46a --- /dev/null +++ b/tasks/spichek_d_dot_product_of_vectors/seq/include/ops_seq.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "spichek_d_dot_product_of_vectors/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace spichek_d_dot_product_of_vectors { + +class SpichekDDotProductOfVectorsSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit SpichekDDotProductOfVectorsSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace spichek_d_dot_product_of_vectors diff --git a/tasks/spichek_d_dot_product_of_vectors/seq/src/ops_seq.cpp b/tasks/spichek_d_dot_product_of_vectors/seq/src/ops_seq.cpp new file mode 100644 index 0000000000..bdd786f79d --- /dev/null +++ b/tasks/spichek_d_dot_product_of_vectors/seq/src/ops_seq.cpp @@ -0,0 +1,46 @@ +#include "spichek_d_dot_product_of_vectors/seq/include/ops_seq.hpp" + +#include + +#include "spichek_d_dot_product_of_vectors/common/include/common.hpp" + +namespace spichek_d_dot_product_of_vectors { + +SpichekDDotProductOfVectorsSEQ::SpichekDDotProductOfVectorsSEQ(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = 0; +} + +bool SpichekDDotProductOfVectorsSEQ::ValidationImpl() { + const auto &[vector1, vector2] = GetInput(); + return vector1.size() == vector2.size(); +} + +bool SpichekDDotProductOfVectorsSEQ::PreProcessingImpl() { + GetOutput() = 0; + return true; +} + +bool SpichekDDotProductOfVectorsSEQ::RunImpl() { + const auto &[vector1, vector2] = GetInput(); + + if (vector1.empty() && vector2.empty()) { + GetOutput() = 0; + return true; + } + + int dot_product = 0; + for (size_t i = 0; i < vector1.size(); ++i) { + dot_product += vector1[i] * vector2[i]; + } + + GetOutput() = dot_product; + return true; +} + +bool SpichekDDotProductOfVectorsSEQ::PostProcessingImpl() { + return true; +} + +} // namespace spichek_d_dot_product_of_vectors diff --git a/tasks/spichek_d_dot_product_of_vectors/settings.json b/tasks/spichek_d_dot_product_of_vectors/settings.json new file mode 100644 index 0000000000..b1a0d52574 --- /dev/null +++ b/tasks/spichek_d_dot_product_of_vectors/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "processes", + "tasks": { + "mpi": "enabled", + "seq": "enabled" + } +} diff --git a/tasks/spichek_d_dot_product_of_vectors/tests/.clang-tidy b/tasks/spichek_d_dot_product_of_vectors/tests/.clang-tidy new file mode 100644 index 0000000000..ef43b7aa8a --- /dev/null +++ b/tasks/spichek_d_dot_product_of_vectors/tests/.clang-tidy @@ -0,0 +1,13 @@ +InheritParentConfig: true + +Checks: > + -modernize-loop-convert, + -cppcoreguidelines-avoid-goto, + -cppcoreguidelines-avoid-non-const-global-variables, + -misc-use-anonymous-namespace, + -modernize-use-std-print, + -modernize-type-traits + +CheckOptions: + - key: readability-function-cognitive-complexity.Threshold + value: 50 # Relaxed for tests diff --git a/tasks/spichek_d_dot_product_of_vectors/tests/functional/main.cpp b/tasks/spichek_d_dot_product_of_vectors/tests/functional/main.cpp new file mode 100644 index 0000000000..ffddf6b549 --- /dev/null +++ b/tasks/spichek_d_dot_product_of_vectors/tests/functional/main.cpp @@ -0,0 +1,192 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "spichek_d_dot_product_of_vectors/common/include/common.hpp" +#include "spichek_d_dot_product_of_vectors/mpi/include/ops_mpi.hpp" +#include "spichek_d_dot_product_of_vectors/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace spichek_d_dot_product_of_vectors { + +class SpichekDDotProductOfVectorsRunFuncTestsProcesses : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + const auto &[vectors, description] = test_param; + return description; + } + + protected: + void SetUp() override { + TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + input_data_ = std::get<0>(params); // Получаем пару векторов + } + + bool CheckTestOutputData(OutType &output_data) final { + const auto &[vector1, vector2] = input_data_; + + // Вычисляем ожидаемый результат + int expected_result = 0; + for (size_t i = 0; i < vector1.size(); ++i) { + expected_result += vector1[i] * vector2[i]; + } + + return (output_data == expected_result); + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + InType input_data_; +}; + +namespace { + +TEST_P(SpichekDDotProductOfVectorsRunFuncTestsProcesses, DotProductTest) { + ExecuteTest(GetParam()); +} + +// Тестовые параметры: ((вектор1, вектор2), описание) +const std::array kTestParam = { + // 1. Простые + std::make_tuple(std::make_pair(std::vector{1, 2, 3}, + std::vector{4, 5, 6}), + "basic_vectors_3"), + + // 2. Нули + std::make_tuple(std::make_pair(std::vector{0, 0, 0}, + std::vector{1, 2, 3}), + "all_zero_first"), + + std::make_tuple(std::make_pair(std::vector{1, 2, 3}, + std::vector{0, 0, 0}), + "all_zero_second"), + + // 3. Только отрицательные + std::make_tuple(std::make_pair(std::vector{-1, -2, -3}, + std::vector{-4, -5, -6}), + "all_negative"), + + // 4. Смешанные + std::make_tuple(std::make_pair(std::vector{1, -2, 3, -4}, + std::vector{-1, 2, -3, 4}), + "alternating_signs"), + + // 5. Большие числа (проверка переполнения) + std::make_tuple(std::make_pair(std::vector{10000, 20000, 30000}, + std::vector{30000, 20000, 10000}), + "large_numbers"), + + // 6. Короткий случай + std::make_tuple(std::make_pair(std::vector{7}, + std::vector{9}), + "single_element"), + + // 7. Много одинаковых + std::make_tuple(std::make_pair(std::vector(100, 5), + std::vector(100, 2)), + "constant_100"), + + // 8. Вектор длиной 1000 + std::make_tuple(std::make_pair(std::vector(1000, 1), + std::vector(1000, -1)), + "size_1000"), + + // 9. Рандом-1 (детерминированный) + std::make_tuple(std::make_pair( + []{ + std::vector v(50); + for (int i = 0; i < 50; i++){ v[i] = i - 25; +} + return v; + }(), + []{ + std::vector v(50); + for (int i = 0; i < 50; i++){ v[i] = 25 - i; +} + return v; + }()), + "random_like_case_1"), + + // 10. Рандом-2 (другая закономерность) + std::make_tuple(std::make_pair( + []{ + std::vector v(60); + for (int i = 0; i < 60; i++){ v[i] = ((i * 7) % 13) - 6; +} + return v; + }(), + []{ + std::vector v(60); + for (int i = 0; i < 60; i++){ v[i] = ((i * 3) % 11) - 5; +} + return v; + }()), + "random_like_case_2"), + + // 11. Половина нулей + std::make_tuple(std::make_pair(std::vector{0,1,0,1,0,1}, + std::vector{1,0,1,0,1,0}), + "half_zero"), + + // 12. Восходящая/нисходящая последовательность + std::make_tuple(std::make_pair( + []{ + std::vector v(100); + for (int i = 0; i < 100; i++){ v[i] = i; +} + return v; + }(), + []{ + std::vector v(100); + for (int i = 0; i < 100; i++){ v[i] = 100 - i; +} + return v; + }()), + "ascending_descending"), + + // 13. Большой тест (5000 элементов) + std::make_tuple(std::make_pair(std::vector(5000, 3), + std::vector(5000, 4)), + "size_5000"), + + // 14. Очень большие положительные/отрицательные + std::make_tuple(std::make_pair(std::vector{1000000, -1000000}, + std::vector{-500000, 500000}), + "extreme_values"), + + // 15. Векторы одинаковых случайных чисел + std::make_tuple(std::make_pair(std::vector(200, 123), + std::vector(200, 321)), + "same_random_like"), + + // 16. Пустые векторы + std::make_tuple(std::make_pair(std::vector{}, + std::vector{}), + "empty_vectors") +}; + +const auto kTestTasksList = std::tuple_cat(ppc::util::AddFuncTask( + kTestParam, PPC_SETTINGS_spichek_d_dot_product_of_vectors), + ppc::util::AddFuncTask( + kTestParam, PPC_SETTINGS_spichek_d_dot_product_of_vectors)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); + +const auto kPerfTestName = SpichekDDotProductOfVectorsRunFuncTestsProcesses::PrintFuncTestName< + SpichekDDotProductOfVectorsRunFuncTestsProcesses>; + +INSTANTIATE_TEST_SUITE_P(DotProductTests, SpichekDDotProductOfVectorsRunFuncTestsProcesses, kGtestValues, + kPerfTestName); + +} // namespace + +} // namespace spichek_d_dot_product_of_vectors diff --git a/tasks/spichek_d_dot_product_of_vectors/tests/performance/main.cpp b/tasks/spichek_d_dot_product_of_vectors/tests/performance/main.cpp new file mode 100644 index 0000000000..b50747b578 --- /dev/null +++ b/tasks/spichek_d_dot_product_of_vectors/tests/performance/main.cpp @@ -0,0 +1,54 @@ +#include + +#include +#include +#include + +#include "spichek_d_dot_product_of_vectors/common/include/common.hpp" +#include "spichek_d_dot_product_of_vectors/mpi/include/ops_mpi.hpp" +#include "spichek_d_dot_product_of_vectors/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace spichek_d_dot_product_of_vectors { + +class SpichekDDotProductOfVectorsRunPerfTestProcesses : public ppc::util::BaseRunPerfTests { + InType input_data_; + + void SetUp() override { + const size_t vector_size = 100000000; + + std::vector vector1(vector_size); + std::vector vector2(vector_size); + + for (size_t i = 0; i < vector_size; ++i) { + vector1[i] = static_cast(i % 1000) + 1; + vector2[i] = static_cast((i * 2) % 1000) + 1; + } + + input_data_ = std::make_pair(vector1, vector2); + } + + bool CheckTestOutputData(OutType &output_data) final { + return (output_data != 0); + } + + InType GetTestInputData() final { + return input_data_; + } +}; + +TEST_P(SpichekDDotProductOfVectorsRunPerfTestProcesses, RunPerfModes) { + ExecuteTest(GetParam()); +} + +const auto kAllPerfTasks = + ppc::util::MakeAllPerfTasks( + PPC_SETTINGS_spichek_d_dot_product_of_vectors); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +const auto kPerfTestName = SpichekDDotProductOfVectorsRunPerfTestProcesses::CustomPerfTestName; + +INSTANTIATE_TEST_SUITE_P(RunModeTests, SpichekDDotProductOfVectorsRunPerfTestProcesses, kGtestValues, kPerfTestName); + +} // namespace spichek_d_dot_product_of_vectors