- Введение
- Параметры и среда тестирования
- Этап 1: Анализ производительности реализации с фиксированной очередью
- Этап 2: Реализация с настраиваемой очередью
- Общие выводы и заключение
- Рекомендации по использованию
Анализ охватывает две опции реализации: с жестко заданным малым размером очереди и с настраиваемыми параметрами очереди. Используются данные из графиков времени выполнения/блокировок и детальные метрики производительности, собранные с помощью perf stat и perf report.
- Reader: Читает BMP-файлы, помещает во входную очередь (
input_q). - Worker: Извлекает изображения из
input_q, выполняет свертку, помещает результат в выходную очередь (output_q). - Writer: Извлекает результаты из
output_q, записывает в файл. - Синхронизация: Потокобезопасные очереди (
input_q,output_q), барьер для Reader'ов, атомарные счетчики для файлов.
- Размер массива очереди (
capacity) и лимит памяти (max_mem_usage_mbв МБ) задаются через аргументы командной строки (--queue-size,--queue-mem). Дефолтные значения -DEFAULT_QUEUE_CAP=20иDEFAULT_QUEUE_MEM_LIMIT=500. - Логика
queue_push: блокировка происходит, если (1) достигнутаcapacityИЛИ (2) добавление нового элемента превыситmax_mem_usage_mb(и очередь не пуста). Приоритет отдается лимиту памяти при принятии решения о блокировке, еслиcapacityдостаточно велико. - Расчет используемой памяти ведется в мегабайтах (
estimate_image_memory_mb) с округлением вверх.
- Изображения:
- Этап 1 (Графики, ранний
perf):image4.bmp(1242x1227). - Этап 2 (
perf stat,perf report):image6.bmp(4480x6720).
- Этап 1 (Графики, ранний
- Количество изображений: Варьировалось (6 для раннего
perf, 17 для позднегоperf, 20 для графиков). - Размер очереди: Варьировалось (2 для большого тестирования производительности с графиками - сделано для более детального отслеживания зависимостей и узких мест, 1-8 при тестировании с помощью
perf) - Повторы: 25.
- Фильтр:
gg(15x15). - Параметры Worker'а:
COMPUTE_MODE=by_row,BLOCK_SIZE=5. - Метрики: Графики Avg Thread Time/Avg Queue Ops Time,
perf stat -d,perf report -g graph,0.5. - Среда: Intel Core i7 4790 (x86-64, 8 ядер, 3.6 ГГц), GCC 14.2.1, режим CPU
performance.
Соотношение R-W-W является ключевым фактором, определяющим производительность и характер узких мест.
-
Mix с малым числом Worker'ов (e.g.,
1-1-1,2-1-1): Worker является узким местом. Один Worker не успевает обрабатывать данные с той скоростью, с которой их может поставлять Reader и потреблять Writer (особенно если их >1). Reader быстро заполняетinput_q, а Writer часто простаивает, ожидая данные вoutput_q. -
Mix с большим числом Worker'ов (e.g.,
1-4-4,1-6-6): Узкое место смещается на стадии ввода/вывода. Один Reader не успевает загружать данные для множества Worker'ов, или один Writer не успевает записывать результаты их работы. Worker'ы часто простаивают в ожидании данных (QPOPизinput_q) или в ожидании освобождения места в выходной очереди (QPUSHвoutput_q). -
Сбалансированные mix'ы (e.g.,
2-2-2,3-3-3,5-5-5): При малом количестве потоков (2-2-2,3-3-3) система может быть относительно сбалансирована, но ограничена абсолютной производительностью CPU и IO. При большом количестве потоков (5-5-5) узким местом становится сама инфраструктура очередей (конкуренция за мьютексы) или достижение пределов пропускной способности аппаратного обеспечения (дисковая подсистема, контроллер памяти, количество ядер). Малый размер очередей усугубляет эту проблему, вызывая частые блокировки даже при теоретически сбалансированных стадиях, обязывая потоки ожидать. -
Потенциально несбалансированные mix'ы (e.g.,
1-2-1,1-1-2,2-1-1,1-2-2,3-2-2,2-3-3,1-3-3): Например,3-2-2может быть выгоднее2-2-2, если чтение медленнее обработки и записи (что и происходит в нашем случае).2-3-3может быть лучше, если обработка самая медленная. Графики показывают вариативность результатов для этих смесей.
Графики представлены в данной директории.
График "Comparison of Avg Thread Execution Times" показывает среднее время, затраченное потоком одного типа на выполнение одной итерации своей основной задачи (чтение файла, обработка изображения, запись файла). Важно: Это время включает саму работу (I/O, вычисления) и возможные внутренние ожидания, но не включает время явной блокировки на queue_push/queue_pop, которое измеряется отдельно.
- Worker Time: Зависит от сложности фильтра (
gg15x15 - ресурсоемкий), размера изображения и эффективностиcompute_mode. Среднее время одного Worker'а закономерно уменьшается при увеличении их общего числа (работа распределяется), как видно при переходе от1-1-1к1-6-6. Однако, это не означает пропорционального ускорения всей системы, если другие стадии отстают. - Reader Time: В основном определяется скоростью чтения фала и накладными расходами на парсинг BMP. Если Reader один (
1-X-X), его время выполнения становится потенциальным пределом скорости для всего конвейера. - Writer Time: Зависит от скорости записи в файл и формирования BMP. Аналогично Reader'у, если Writer один (
X-X-1), его производительность ограничивает общую пропускную способность.
Графики представлены в данной директории.
График "Comparison of Avg Queue Ops Times" – ключевой для понимания дисбаланса конвейера. Он показывает среднее время, которое потоки проводили в состоянии ожидания (pthread_cond_wait или pthread_cond_timedwait) при попытке доступа к очереди.
-
QPUSHBlock Time (Светло-голубой): Очередь полна.- Reader блокируется на
QPUSHвinput_q: Worker'ы не успевают забирать изображения. Это происходит, когда Worker'ов мало или они медленные (2-1-1,1-1-1). - Worker блокируется на
QPUSHвoutput_q: Writer'ы не успевают записывать результаты. Это происходит, когда Writer'ов мало или они медленные (1-6-1- гипотетически,1-1-1если запись медленная).
- Reader блокируется на
-
QPOPBlock Time (Темно-синий): Очередь пуста.- Worker блокируется на
QPOPизinput_q: Reader'ы не успевают читать файлы. Характерно для смесей с малым числом Reader'ов и быстрыми Worker'ами (1-6-6,1-4-4). - Writer блокируется на
QPOPизoutput_q: Worker'ы не успевают обрабатывать изображения. Характерно для смесей с малым числом Worker'ов (2-1-1,1-1-1).
- Worker блокируется на
Вывод по очередям: Экстремально малый размер очереди (DEFAULT_QUEUE_CAP=2) является основной причиной высоких времен блокировок и низкой утилизации потоков во многих конфигурациях. Время QPOP в целом ниже QPUSH, но все же заметно во многих конфигурациях (1-6-6, 1-1-1, 2-1-1), подтверждая, что потребители часто ожидают данные. Также графиках видны очень высокие значения QPUSH во многих mix'ах (2-1-1, 2-2-2, 2-2-3, 3-2-2, 5-5-5). Даже небольшой временный дисбаланс приводит к заполнению очереди и блокировке производителя. Система работает в режиме "тугой связи", где производительность сильно ограничена необходимостью частой синхронизации.
Графики представлены в данной директории.
Исходная реализация страдала от серьезного ограничения производительности из-за DEFAULT_QUEUE_CAP=2. Это приводило к частым блокировкам, низкой утилизации CPU и маскировало влияние других факторов, таких как compute_mode. Требовалось увеличить гибкость управления очередью.
Введены параметры --queue-size (физическая емкость массива) и --queue-mem (лимит памяти в МБ). Очередь блокируется при достижении capacity ИЛИ превышении лимита памяти (если не пуста). Расчет памяти ведется в МБ с округлением вверх в функции estimate_image_memory.
Тесты проводились на image6.bmp и image4.bmp. --queue-mem=500, чтобы не быть основным лимитом в тестах с queue-size 1-8.
(На основе данных perf stat для image6.bmp)
| RWW Mix | queue-size | Время (с) | CPU Util | IPC | Анализ |
|---|---|---|---|---|---|
| 6,6,1 | 1 | 129.5 | 5.120 | 2.10 | База для q=1 |
| 6,6,1 | 2 | 130.8 | 5.097 | 2.10 | Нет изменений |
| 6,6,1 | 4 | 124.2 | 5.387 | 2.09 | Небольшое улучшение (выброс или оптимум?) |
| 6,6,1 | 8 | 127.6 | 5.252 | 2.10 | Нет значимых изменений |
| 6,6,6 | 1 | 126.0 | 5.241 | 2.10 | Сравнение с 6,6,1 - Writer'ы не влияют |
| 6,6,6 | 8 | 128.8 | 5.178 | 2.09 | queue-size 1 vs 8 - нет разницы |
| 4,6,4 | 1 | 128.4 | 5.197 | 2.10 | Сравнение с 6,6,6 - Reader'ы не влияют (при 6W) |
| 4,6,4 | 8 | 129.1 | 5.168 | 2.08 | queue-size 1 vs 8 - нет разницы |
| 2,6,4 | 1 | 134.1 | 4.979 | 2.07 | Чуть медленнее (2 Reader'а - предел?) |
| 2,6,4 | 8 | 130.5 | 5.125 | 2.08 | queue-size 1 vs 8 - нет разницы |
Вывод: Для большого изображения и 6 Worker'ов, queue-size (1-8) почти не влияет. Производительность стабильна ~125-130с, утилизация CPU ~5.1-5.4. Число Reader/Writer'ов (при >=2R, >=1W) также слабо влияет. Узкое место не в размере очереди и не в I/O (в этих пределах).
| queue-size | Время (с) | CPU Util | IPC | Анализ |
|---|---|---|---|---|
| 1 | 465.6 | 1.002 | 2.82 | База |
| 2 | 465.4 | 1.002 | 2.82 | Нет изменений |
| 4 | 465.7 | 1.003 | 2.82 | Нет изменений |
| 8 | 465.7 | 1.003 | 2.82 | Нет изменений |
Вывод: Подтверждает, что при 1 Worker'е размер очереди не имеет значения.
Сравнение профилей perf report для RWW 2,4,2 ("Оптимум"), 6,6,6 ("Плато") и 1,8,1 ("Перегрузка").
apply_filter: ~53% CPU времени.intel_idle: ~45.6%, система все еще много простаивает.- Нет явных пиков на
pthread_mutex_lock. - Вывод: Производительность ограничена не только вычислениями, но и простоями (ожидание в очередях или I/O).
apply_filter: ~71% CPU времени (больше ядер занято вычислениями).intel_idle: Снизился до ~27%.- Общее время выполнения не улучшилось. IPC упал (~2.1).
- Нет явных пиков на
pthread_mutex_lock. - Вывод: Эффективность снизилась. Больше ядер работают, но медленнее (на цикл). Узкое место - пропускная способность памяти или скрытая конкуренция за мьютексы.
apply_filter: ~95% CPU времени!intel_idle: Минимальный (~3.3%).- Общее время не лучше, чем у
6,6,6. IPC самый низкий (~1.55). - Вывод: Пропускная способность памяти является явным ограничителем. 8 Worker'ов перегружают подсистему памяти. I/O (1R/1W) также может быть узким местом.
Анализ данных perf stat и perf report для реализации с настраиваемой очередью, особенно на большом изображении (image6.bmp), позволяет сделать следующие ключевые выводы:
- Новая реализация с параметрами
--queue-sizeи--queue-memпозволила преодолеть ограничениеMAX_QUEUE_SIZE=2и дала системе возможность масштабироваться по числу Worker'ов, выявив при этом другие узкие места. - Увеличение числа Worker'ов эффективно до ~4-6 на тестовой системе (8 ядер). Дальнейшее увеличение (до 8) не дает прироста скорости и приводит к значительному падению IPC (с ~2.7 до ~1.55), указывая на достижение предела масштабируемости.
- Плато производительности и падение IPC при 6+ Worker'ах, несмотря на рост утилизации CPU и отсутствие явных долгих блокировок мьютексов в
perf report, сильно указывают на достижение предела пропускной способности подсистемы памяти или на высокую конкуренцию за разделяемые ресурсы (частые, но короткие конфликты за мьютексы очередей, борьба за кэш-линии). - При достижении плато производительности (6+ Worker'ов), изменение
queue-size(>=2) и числа Reader'ов/Writer'ов (в пределах 2-6 R/W) практически не влияет на результат. Это подтверждает, что узкое место уже не в размере буфера очереди или дисковом I/O.
Проведенный анализ двух этапов реализации многопоточной свертки на основе очередей показал:
- Критичность Размера Буфера: Исходная реализация с
MAX_QUEUE_SIZE=2была неэффективна из-за постоянных блокировок. Переход к настраиваемой очереди с лимитом по памяти (--queue-mem) и достаточной емкостью (--queue-size) является обязательным для производительности. - Оптимальное Распределение Потоков: Существует оптимальное число Worker'ов (~4-6 для данной системы/задачи), превышение которого ведет к падению эффективности из-за системных ограничений.
- Лимит Пропускной Способности Памяти/Конкуренции: При оптимальном/высоком числе Worker'ов главным ограничителем становится доступ к памяти и/или конкуренция за мьютексы очередей, а не I/O или размер буфера очереди (если он достаточен).
- Необходимость Оптимизации Worker'а: Несмотря на хороший IPC "в моменте", общее время выполнения и простои CPU указывают на необходимость дальнейшей оптимизации, особенно в части локальности данных для снижения нагрузки на память (см. Рекомендации).
- Использовать Оптимальное Число Worker'ов: Настроить R-W-W Mix так, чтобы число Worker'ов было в районе 4-6 для данной системы.
- Настроить Лимит Памяти (
--queue-mem): Установить значение, достаточное для буферизации нескольких больших изображений (например,4 * <размер_image6_в_МБ>), чтобы избежать блокировок по памяти, но не исчерпать RAM. - Установить достаточный
queue-size: Значение>= Число Worker'ов(например, 8 или 16) будет достаточным, позволяя лимиту памяти управлять буфером.