Toliak

LAB 8

Apr 12th, 2019
396
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.23 KB | None | 0 0
  1. #include <iostream>
  2. #include <vector>
  3. #include <random>
  4. #include <chrono>
  5. #include <thread>
  6. #include <functional>
  7. #include <iomanip>
  8. #include <atomic>
  9. #include <algorithm>
  10.  
  11. /**
  12.  * @brief Базовое исключение, содержащее строку
  13.  */
  14. class Exception: public std::exception
  15. {
  16. private:
  17.     std::string text;                       ///< Текст исключения
  18.  
  19. public:
  20.     /**
  21.      * @brief Основной конструктор
  22.      * @param string Текст исключения
  23.      */
  24.     explicit Exception(std::string string)
  25.         : text(std::move(string))
  26.     {}
  27.  
  28.     const char *what() const noexcept override
  29.     {
  30.         return text.c_str();
  31.     }
  32. };
  33.  
  34. /**
  35.  * @brief Информация о потоках
  36.  * Свойства public, так как их защите не ожидается
  37.  */
  38. struct ThreadInfo
  39. {
  40.     size_t threadMax;                                   ///< Максимальное количество потоков
  41.     std::atomic<size_t> threadAmount{0};                ///< Текущее количество потоков
  42.  
  43.     /**
  44.      * @brief Основной конструктор
  45.      * @param maxThreads Максимальное количество потоков
  46.      */
  47.     explicit ThreadInfo(size_t maxThreads)
  48.         : threadMax(maxThreads)
  49.     {}
  50. };
  51.  
  52. /**
  53.  * @brief Обертка для типа, позволяющая создать пустой экземпляр
  54.  * @tparam T Оборачиваемый тип
  55.  */
  56. template<class T>
  57. struct TypeWrapper
  58. {
  59.     // Не ожидается защита полей
  60.  
  61.     T value;                    ///< Значение
  62.     bool isEmpty = false;       ///< Пустое ли значение
  63.  
  64.     explicit TypeWrapper(T value)
  65.         : value(value)
  66.     {}
  67.  
  68.     TypeWrapper(T value, bool isEmpty)
  69.         : value(value), isEmpty(isEmpty)
  70.     {}
  71.  
  72.     // Не ожидается приведение типов => операторы перегружены внутри структуры\
  73.  
  74.     bool operator==(const TypeWrapper<T> &right) const noexcept
  75.     {
  76.         return TypeWrapper::value == right.value;
  77.     }
  78.  
  79.     bool operator>=(const TypeWrapper<T> &right) const noexcept
  80.     {
  81.         return TypeWrapper::value >= right.value;
  82.     }
  83.  
  84.     bool operator<=(const TypeWrapper<T> &right) const noexcept
  85.     {
  86.         return TypeWrapper::value <= right.value;
  87.     }
  88.  
  89.     bool operator<(const TypeWrapper<T> &right) const noexcept
  90.     {
  91.         return TypeWrapper::value < right.value;
  92.     }
  93.  
  94.     bool operator>(const TypeWrapper<T> &right) const noexcept
  95.     {
  96.         return TypeWrapper::value > right.value;
  97.     }
  98. };
  99.  
  100. /**
  101.  * @brief Отображение вектора в std::cout
  102.  * @tparam T Тип вектора
  103.  * @param vector Вектор для отображения
  104.  */
  105. template<typename T>
  106. void printVector(const std::vector<TypeWrapper<T>> &vector)
  107. {
  108.     const size_t ROW_SIZE = 20;                 ///< Максимальное количество элементов в векторе
  109.  
  110.     size_t index = 0;                           ///< Индекс текущего элемента в векторе
  111.     for (const auto &it : vector) {
  112.         std::cout << std::setw(5) << it.value << " ";
  113.  
  114.         if (index % ROW_SIZE == 0 && index != 0) {
  115.             std::cout << std::endl;
  116.         }
  117.         index++;
  118.     }
  119.  
  120.     std::cout << std::endl;
  121. }
  122.  
  123. /**
  124.  * @brief Определяет, является ли число степеньню 2
  125.  * @param number Число
  126.  * @return Пара (является ли степень 2, log_2(number))
  127.  */
  128. std::pair<bool, size_t> isTwoPower(uint64_t number)
  129. {
  130.     size_t result = 0;                  ///< Количество единиц в двоичной записи
  131.     size_t index = 0;                   ///< Индекс последней единицы
  132.  
  133.     // Пока число существует
  134.     while (number != 0) {
  135.         // Определяем крайний бит
  136.         result += number & 0x1u;
  137.  
  138.         index++;
  139.         number >>= 1u;
  140.     }
  141.  
  142.     return {result == 1, index - 1};
  143. }
  144.  
  145. /**
  146.  * @brief Применение алгоритма к блоку
  147.  * @tparam T Тип вектора
  148.  * @tparam Compare Функция сравнения
  149.  * @param start Начальный итератор
  150.  * @param end Конечный итератор
  151.  * @param isOdd Четный ли блок
  152.  * @param threadInfo Информация о доступных потоках
  153.  */
  154. template<typename T, typename Compare>
  155. void bitonicSortStep(typename std::vector<T>::iterator start,
  156.                      typename std::vector<T>::iterator end,
  157.                      bool isOdd,
  158.                      ThreadInfo &threadInfo)
  159. {
  160.     Compare comparator;                 ///< Функция сравнения
  161.     size_t size = end - start;          ///< Размер блока
  162.     auto sizeInfo = isTwoPower(size);   ///< Получение информации о размере
  163.     if (!sizeInfo.first) {
  164.         // Выход, если размер не степень 2
  165.         throw Exception("log_2(size) is bad");
  166.     }
  167.  
  168.     size_t step = size >> 1u;           ///< Расстояние от начального до конечного элемента сравнения
  169.     // (Длина стрелки в алгоритме сравенения) size / 2
  170.  
  171.     // Обход первой половины блока
  172.     for (auto it = start; it != start + (size >> 1u); it++) {
  173.         auto secondIterator = it + step;            ///< Сравниваемый элемент (из второй половины)
  174.         if (isOdd) {
  175.             // Если четное, то максимум во второй половине в случае сортировки по возрастанию
  176.             if (comparator(*it, *secondIterator)) {
  177.                 // Если максимум в первой половине - делаем swap
  178.                 std::swap(*it, *secondIterator);
  179.             }
  180.         } else {
  181.             // Если нечетное, то минимум во второй половине в случае сортировки по возрастанию
  182.             if (!comparator(*it, *secondIterator)) {
  183.                 // Если минимум в первой половине - делаем swap
  184.                 std::swap(*it, *secondIterator);
  185.             }
  186.         }
  187.     }
  188.  
  189.     // Если блок делится на подблоки
  190.     if (step > 1) {
  191.         auto executable = [start, size, isOdd, &threadInfo]() {
  192.             bitonicSortStep<T, Compare>(start, start + (size >> 1u), isOdd, std::ref(threadInfo));
  193.         };
  194.         ///< Анонимная функция void (*)(), вызывающая функцию сортировки подблока
  195.  
  196.         // ветвление бинарное => поток единственный
  197.         std::thread *thread = nullptr;                      ///< Указатель на подпоток
  198.         if (threadInfo.threadAmount < threadInfo.threadMax) {
  199.             // Есть доступные подпотоки
  200.             threadInfo.threadAmount++;
  201.             thread = new std::thread(executable);
  202.         } else {
  203.             // Нет доступных потоков. Выполнение параллельное
  204.             executable();
  205.         }
  206.  
  207.         // Вторая ветвь продолжается в текущем потоке
  208.         bitonicSortStep<T, Compare>(start + (size >> 1u), end, isOdd, std::ref(threadInfo));
  209.  
  210.         if (thread) {
  211.             // Если существует подпоток - присоединить
  212.             thread->join();
  213.             threadInfo.threadAmount--;
  214.             delete thread;
  215.         }
  216.     }
  217. }
  218.  
  219. /**
  220.  * @brief Функция битонической сортировки
  221.  * @tparam T Тип вектора
  222.  * @tparam Compare Функция сравнения
  223.  * @param vector Сортируемый вектор
  224.  * @param threadAmount Количество доступных потоков
  225.  */
  226. template<typename T, typename Compare = std::greater_equal<T>>
  227. void bitonicSort(std::vector<T> &vector, size_t threadAmount)
  228. {
  229.     auto sizeInfo = isTwoPower(vector.size());              ///< Информация о размере
  230.     if (!sizeInfo.first) {
  231.         throw Exception("Bad vector size (expected power of 2)");
  232.     }
  233.  
  234.     const size_t MIN_BLOCK_SIZE = 8;        ///< Минимальный размер блока, для обработки которого создается новый поток
  235.  
  236.     for (size_t i = 0; i < sizeInfo.second; i++) {
  237.         const size_t step = 0b10u << i;                     ///< Размер блока 2**i
  238.  
  239.         size_t subThreadAmount = threadAmount;                             ///< Количество подпотоков
  240.         if (step >= MIN_BLOCK_SIZE) {
  241.             size_t exceptedThreadAmount = vector.size() / step;            ///< Количество основных потоков
  242.             if (exceptedThreadAmount >= threadAmount) {
  243.                 subThreadAmount = 0;
  244.             } else {
  245.                 subThreadAmount = threadAmount - exceptedThreadAmount;
  246.             }
  247.         }
  248.  
  249.         std::vector<std::thread *> threads;                ///< Вектор, содержащий указатели на основные потоки
  250.         ThreadInfo threadInfo(subThreadAmount);                            ///< Информация о подпотоках
  251.  
  252.         for (size_t j = 0; j < vector.size(); j += step) {
  253.             auto executable = [&vector, j, step, &threadInfo]() {
  254.                 bitonicSortStep<T, Compare>(
  255.                     vector.begin() + j,
  256.                     vector.begin() + j + step,
  257.                     (j / step) % 2 == 0,
  258.                     std::ref(threadInfo)
  259.                 );
  260.             };
  261.             ///< Анонимная функция void (*)(), вызывающая сортировку блока
  262.  
  263.             if (step >= MIN_BLOCK_SIZE && j / step <= threadAmount && j + step < vector.size()) {
  264.                 // Размер блока не менее MIN_BLOCK_SIZE, достаточно потоков и блок не последний
  265.                 // => Параллельный вызов
  266.                 threads.push_back(new std::thread(executable));
  267.             } else {
  268.                 // В другом случае вызываем функцию последовательно
  269.                 executable();
  270.             }
  271.         }
  272.  
  273.         // Перед переходом к следуюшему шагу текущий должен быть полностью выполнен
  274.         if (!threads.empty()) {
  275.             for (std::thread *thread: threads) {
  276.                 thread->join();
  277.                 delete thread;
  278.             }
  279.         }
  280.     }
  281. }
  282.  
  283. /**
  284.  * @brief Случайная генерация вектора указанной длины
  285.  * @param size Размер вектора
  286.  * @tparam T Тип результирующего вектора
  287.  * @return Сгенерированный вектор
  288.  */
  289. template<typename T>
  290. std::vector<T> generateVector(size_t size)
  291. {
  292.     std::vector<T> result(size);
  293.  
  294.     // Инициализация рандома
  295.     std::random_device randomDevice;
  296.     std::mt19937 mt(randomDevice());
  297.     std::uniform_int_distribution<T> rand(-1000, 1000);
  298.  
  299.     for (size_t i = 0; i < size; i++) {
  300.         result[i] = rand(mt);
  301.     }
  302.     return result;
  303. }
  304.  
  305. /**
  306.  * @brief Удобная функция для ввода
  307.  * @tparam T Приводимый тип
  308.  * @param string Вывод перед вводом
  309.  * @return Значение из потока, приведенное к типу T
  310.  */
  311. template<typename T>
  312. T input(const std::string &string)
  313. {
  314.     T value;
  315.     std::cout << string;
  316.     std::cin >> value;
  317.     return value;
  318. }
  319.  
  320. /**
  321.  * @brief Проверка на отсортированный вектор
  322.  * @tparam T Тип вектора
  323.  * @tparam Compare Функция сравнения
  324.  * @param vector Исследуемый вектор
  325.  * @return Отсортирован ли вектор
  326.  */
  327. template<typename T, typename Compare = std::greater_equal<TypeWrapper<T>>>
  328. bool isSorted(const std::vector<TypeWrapper<T>> &vector)
  329. {
  330.     Compare comparator;
  331.     for (auto it = vector.cbegin() + 1; it != vector.cend(); it++) {
  332.         if (!comparator(*it, *(it - 1))) {
  333.             return false;
  334.         }
  335.     }
  336.  
  337.     return true;
  338. }
  339.  
  340. int main()
  341. {
  342.     std::cout << "Works for 10 seconds with 1048576 elements" << std::endl;
  343.  
  344.     auto vectorSize = input<size_t>("Enter vector size: ");    ///< Размер вектора
  345.     auto threadAmount = input<size_t>("Enter thread amount: ");     ///< Количество потоков
  346.  
  347.     using Type = TypeWrapper<int>;
  348.     std::vector<int> originalVector = generateVector<int>(vectorSize);      ///< Вектор из случайных значений
  349.     std::vector<Type> vector(originalVector.cbegin(), originalVector.cend());                ///< Вектор длины 2**K
  350.  
  351.     // Вывод вектора
  352.     std::cout << "Source vector: " << std::endl;
  353.     if (vectorSize <= 1024) {
  354.         printVector(vector);
  355.     } else {
  356.         std::cout << "\tIs large..." << std::endl;
  357.     }
  358.  
  359.     size_t additionalElementAmount = (0x1u << (isTwoPower(vectorSize).second + 1)) - vectorSize;
  360.     ///< Количество элементов, которые необходимо добавить, для приведения вектора к длине 2**K
  361.  
  362.     // Добавление заглушек
  363.     std::cout << "Additional empty elements amount: " << additionalElementAmount << std::endl;
  364.     for (size_t i = 0; i < additionalElementAmount; i++) {
  365.         vector.emplace_back(TypeWrapper<int>(0, true));
  366.     }
  367.  
  368.     std::chrono::time_point<std::chrono::high_resolution_clock> startTime, endTime;     ///< Тайминг
  369.  
  370.     // Замер времени и выполнение сортировки
  371.     startTime = std::chrono::high_resolution_clock::now();
  372.     bitonicSort<Type>(vector, threadAmount);                    // Сортировка
  373.     endTime = std::chrono::high_resolution_clock::now();
  374.  
  375.     // После сортировки необходимо удалить "заглушки"
  376.     vector.erase(
  377.         std::remove_if(
  378.             vector.begin(),
  379.             vector.end(),
  380.             [](Type value) {
  381.                 return value.isEmpty;
  382.             }
  383.         ),
  384.         vector.end()
  385.     );
  386.  
  387.     // Вывод тайминга
  388.     std::cout << std::endl
  389.               << "Time elapsed: "
  390.               << std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count()
  391.               << " ms"
  392.               << std::endl;
  393.  
  394.     // Вывод вектора
  395.     std::cout << "Result vector: " << std::endl;
  396.     if (vectorSize <= 1024) {
  397.         printVector(vector);
  398.     } else {
  399.         std::cout << "\tIs large..." << std::endl;
  400.     }
  401.  
  402.     // Проверка сортировки
  403.     std::cout << std::endl << "Is sorted: " << (isSorted(vector) ? "True" : "False") << std::endl;
  404.  
  405.     return 0;
  406. }
Advertisement
Add Comment
Please, Sign In to add comment