Почему так сильно различается время, необходимое для выполнения программы?

Что может быть причиной значительных колебаний во времени выполнения программы на С++, которая распараллеливает свою работу с помощью std::async, даже если входные данные остаются постоянными? Можно ли связать такое поведение с особенностями планировщика задач в Linux или это может быть вызвано реализацией std::unordered_map и std::unordered_set, которые широко используются в программе?
  • 1 июля 2024 г. 14:22
Ответы на вопрос 4
Есть несколько возможных причин значительных колебаний во времени выполнения программы на С++, использующей std::async:

1. Планировщик задач в операционной системе (например, Linux) может неравномерно распределять ресурсы между потоками, что приводит к различным временам выполнения задач. Это может быть особенно заметно при распараллеливании работы с помощью std::async, так как планировщик самостоятельно распределяет задачи между доступными потоками.

2. Реализация структур данных std::unordered_map и std::unordered_set может иметь существенное влияние на время выполнения программы. Например, сложность операций вставки, удаления или поиска элементов в этих структурах данных может сильно варьироваться в зависимости от количества элементов, их распределения и используемой хеш-функции. Это может приводить к значительным колебаниям во времени выполнения программы.

3. Другие факторы, такие как использование кэша процессора, доступ к памяти и взаимодействие с внешними ресурсами (например, сетью или дисковой подсистемой), также могут оказывать влияние на время выполнения программы.

Для выяснения конкретных причин колебаний во времени выполнения программы следует провести более детальный анализ и профилирование кода. Возможно, стоит рассмотреть оптимизацию алгоритмов работы с std::unordered_map и std::unordered_set, а также более тщательно настроить использование std::async и управление потоками в программе.
… 75% cpu 2:23,52 total
… 113% cpu 1:22,71 total
… 73% cpu 2:20,75 total
количество потоков в программе равно количеству потоков процессора (в моём случае - 8…

Похоже, что твоя программа не справляется с загрузкой всех ядер, едва одно ядро она нагружает. Я бы смотрел в то как потоки взаимодействуют друг с другом и искал там явную или скрытую сериализацию.
У вас там работа с диском. Да еще многопоточная. Что там в кеше у SSD окажется, выполняется ли какой-нибудь TRIM в контроллере SSD, индексирует ли что-то какой-нибудь системный процесс, на какое ядро планировщик закинет какие потоки - куча факторов.
Любой вопрос о производительности своего приложения нужно начинать с профилирования. 
google: c++ profiler, первая же статья с хабра как пример.

И да отсутствие 100% нагрузки на все ядра уже повод задуматься что что то делаешь не так (или приложение упирается в недостаточно высокую скорость чтения файлов как пример). Кстати напоминаю, современные ядра что у intel что у amd - виртуальные, мало того, есть модели десктопных процессоров, где некоторые ядра не такие же как остальные (читай медленнее) а значит выкачивать 100% эффективности не так просто. Например многопоточные приложения числодробилки я сразу запускаю на 1/2 от количества ядер процессора (иначе можно получить замедление вместо ускорения).

Для начала попробуй зафиксировать для своего однопоточного приложения определенные ядра, например с помощью taskset (google: linux cpu core affinity for specified application). Если делать тупо, то перебирай доступные ядра по одному (может быть по два, так как асинхронные методы могут внутри себя использовать потоки, для реализации асинхронности тех действий для которых они в ОС не реализованы), иначе нужно проанализировать свои ядра по отдельности бенчмарками и принимать решение обосновано.
Похожие вопросы