Введение
Автовекторизация позволяет автоматически генерировать SIMD-инструкции, но её эффективность зависит от компилятора и структуры алгоритма. Цель работы — экспериментально сравнить GCC, Clang и ICC на алгоритмах БПФ, умножения матриц и свёртки. Разработаны тестовые реализации на C/C++, выполнены замеры с включённой и отключённой векторизацией.
Методология экспериментальных исследований
Стенд: Intel Core i7-10700K (3,8 ГГц, 16 потоков), Ubuntu 22.04 LTS, GCC 12.3, Clang 16.0, ICC 2022.2. Профилирование — perf, 10 прогонов, исключение выбросов.
Алгоритмы: БПФ (комплексные числа), умножение матриц (наивное и блочное), свёртка (простая и оптимизированная).
Компиляция: -O3 -march=native; отключение векторизации: -fno-tree-vectorize (GCC), -fno-vectorize (Clang), -no-vec (ICC). Код оптимизирован под выравнивание и контроль алиасинга.
Анализ производительности алгоритмов
Ускорение от автовекторизации зависит от блочной структуры, выравнивания данных и кэш-памяти. Наивная реализация даёт меньший прирост, чем оптимизированная (библиотечная может быть вдвое быстрее). Автовекторизация не заменяет алгоритмическую оптимизацию, а усиливает её при правильной организации данных. Невыровненные обращения требуют дополнительных операций, снижая пропускную способность. Для матриц 512×512 и более ускорение на -O3 значительно. Оптимальный размер блока под кэш L2 улучшает баланс между доступом к памяти и вычислениями [2, с. 16].
Для FFT прирост производительности заметнее на больших массивах: при 2^20 элементов ускорение достигает 2,5–3 раз, при 2^10 — лишь 30–40 %. Это связано с тем, что операции «бабочки» при больших размерах работают в кэше, а при малых накладные расходы на векторизацию снижают выигрыш. Сравнительные результаты для всех трёх компиляторов представлены в таблице 1, а динамика ускорения для GCC визуализирована на рисунке 1.
Таблица 1.
Коэффициент ускорения при автовекторизации для БПФ в зависимости от компилятора и размера массива
|
Размер массива |
GCC 12.3 |
Clang 16.0 |
ICC 2022.2 |
|
2^10 |
1,32 |
1,38 |
1,45 |
|
2^12 |
1,58 |
1,67 |
1,82 |
|
2^14 |
2,01 |
2,12 |
2,31 |
|
2^16 |
2,28 |
2,46 |
2,68 |
|
2^18 |
2,55 |
2,78 |
2,94 |
|
2^20 |
2,71 |
2,95 |
3,12 |

Рисунок 1. Зависимость коэффициента ускорения при автовекторизации от размера массива для алгоритма БПФ
Особенности работы алгоритмов свёртки
Нерегулярные шаблоны доступа и условные ветвления мешают формированию SIMD-паттернов. Нерегулярная индексация нарушает выровненные загрузки, снижая выгоду от векторизации. Как отмечается в [1, с. 440], особенности кода могут препятствовать межмодульным преобразованиям, вызывая скалярные откаты и маскированные операции вместо полноширинных инструкций. В свёртках прирост производительности меньше, чем в регулярных ядрах.
Сравнение с включённой и отключённой векторизацией показывает, что при нерегулярном доступе выигрыш ограничен и нестабилен — компилятор не может устранить структурные препятствия. Для устойчивого прироста требуется сочетание векторизации с алгоритмическими преобразованиями (перестановка данных, блокирование) и обеспечение межмодульной интеграции кода.
Интерпретация результатов автовекторизации по компиляторам
Результаты (таблица 1) показывают, что ICC обеспечивает максимальное ускорение на всех размерах массивов благодаря глубокому знанию микроархитектуры Intel и агрессивной оптимизации прологов/эпилогов циклов. Для массива 2^20 ICC достигает 3,12× против 2,95× у Clang и 2,71× у GCC. Однако эффективность ICC снижается при сложных условиях и непредсказуемом доступе — более 25% времени уходит на проверки границ. ICC эффективен для регулярных задач; сложные алгоритмы требуют ручной адаптации.
Clang занимает промежуточное положение, стабильно опережая GCC на 5–8% для больших массивов. Это объясняется более эффективной эвристикой при развёртывании циклов и выборе порядка векторных операций. GCC 12.2, хотя и уступает лидерам, показывает предсказуемую производительность и добавляет проверки выравнивания (потеря 12–15% против Clang), что делает его поведение более консервативным, но надёжным.
Выбор компилятора: ICC — для плотных вычислений на Intel [3, c. 23]; GCC — для баланса производительности и переносимости (-O3 -march=native -ffast-math); Clang — для FFT с нерегулярным доступом [3, c. 39].
Заключение
Эксперимент подтвердил зависимость эффективности автовекторизации от компилятора и типа задачи. ICC показал максимальное ускорение для БПФ (до 3,12× при 2^20), опережая Clang на ~6% и GCC на ~15%. GCC и Clang обеспечивают конкурентоспособную производительность при использовании restrict и AVX2. Рекомендации: ICC — для максимальной производительности на Intel, GCC — для баланса и переносимости, Clang — для задач с нерегулярной структурой данных.
Список литературы
- Вьюкова Н.И., Галатенко В.А., Самборский С.В. Директивная и автоматическая векторизации циклов // Программная инженерия. — 2016. — № 10. — С. 435–445
- Габрельян Б.В. Технологии высокопроизводительных вычислений. — Ростов-на-Дону: Донской государственный технический университет, 2018. — 26 с.
- Штейнберг Б.Я., Штейнберг О.Б. Преобразования программ — фундаментальная основа создания оптимизирующих распараллеливающих компиляторов // Программные системы: теория и приложения. — 2021. — №1. — С. 21–113


