Практична з обрахунку інтегралу хаданої функції з використанням SSE розширень для x86 Варіант функції: 2 Автор: Денис Дацко ############# Встановлення та запуск ################# УВАГА!!! Код буде компілюватися і працювати тільки якщо процесором і компілятором підтримуються інтринсики з SSE УВАГА 2 !!! Все працюватиме швидше, якщо у вас встановлений Intell C++ Compiler (icpc), бо в нього є інтринсики для функцій exp() і cos() Для встанослення виконайте: $ ./install.sh Або $ mkdir build $ cd build $ cmake -DCMAKE_BUILD_TYPE=Release -G"Unix Makefiles" .. $ make У директорії bin буде створено виконуваний файл lab_integral_sse Для запуску потрібно вказати ім'я файлу конфігурації ############# Файл конфігурації ###################### В цій директорії є файл "config". Це приклад файлу конфігурації. Всі поля присутні там мають обов'язково бути присутніми. Детальніше - див. файл "config" ############# Запуск Python скрипта ################### Мінімальна версія Python - 3.6 (використовував f-strings) Для запуску скрипта слід вказати такі аргументи: binary file name, number of threads (or range), number of runs each time !! Якщо потрібно змінити конфігурацію, просто змініть 5-11 рідки файлу "main.py" Наприклад, $ python3 bin/lab_integrl 1-8 10 $ python3 bin/lab_integrl 4 1 Кількість потоків можна вказувати, як відрізок. В такому разі код запустить програму вказану кількість разів для різної кількості потоків Якщо результати різних запусків не збігатимуться, на екран виведеться повідомлення про це ############ Коментарі щодо реалізації ################# - Основу коду я взяв з першой практичної з обрахунку інтегралу. Архів, який ми здавали (я нічого не змінвав) я включив сюди для змоги порівняти реалізації, продуктивність і т.д. - В C++ компіляторі для Intel присутні інтринсики _mm_exp_pd() та _mm_cos_pd(), що значною мірою прискорює та спрощує код. Тому, якщо icpc знайдений будуть використані вони. Якщо ж ні, то для того, щоб зробити exp(__m128d x) числа завантажаться в пам'ять, над ними виконається функція exp() (послідовно) і вони запишуться назад - Я хотів, щоб об'єкт типу Function() передавався з мейну, бо це б зробило код зручнішим та менш зв'язним, проте через проблеми з копіюванням __m128d я відмовився від цієї ідеї. - Для чого фугкція "double *two_aligned_two_doubles()"? Виявляється, щоб робити _mm_load_pd(addr) addr має бути вирівняний на 16 байт. Тому я зробив цю функцію, щоб гаранувати це, адже мені іноді прилітав "SIGSEGV segmentation fault". Проблема тоді полягає в тому, що вказівник потрібно самостійно видаляти. Але проблем з витоками пам'яті немає (принаймні, Valgrind не знайшов) - Я хотів ще зробити таке саме з AVX, адже в моєму процесорі є це розширення, але після побачених результатів відмовився від цього ############ Результати запусків ######################## - Міряти продуктивність було трози складно, до з різною кфлькфстю потоків результати на різних ітераціях трішки відрізнялися, що інколи призводило до меншої кількості ітерацій або більшої (наприклад, код на 3 потоках завершувався в 5 разів швидше, ніж код на 4-х, бо умови похибок задовільнилися швидше). Тому, я ставив дуже малі похибки і обмеження на 6 ітерацій. Тобто всі були в рівних умовах - Тепер до сумного. Виявилося, що мій кож з використанням SSE працює приблизно так само, як в минулій практичній... Результати можна побачити тут: https://docs.google.com/spreadsheets/d/1mUOSjEOt9lNZwKX5B63ErIOtHD-zF3ZCL9TIgPaBdq0/edit?usp=sharing - Я не знаю, чому так стається. Спочатку, думав - через std::align, але побачив, що вона викликається тільки на початку обрахунку. Я пробував трохи міняти місцями команди (щоб між _mm_load_pd і використанням змінної пройшов певний час), але це не дуже допомагало.