Как распределяется вычислительный объём на ядрах? | OTUS
👋 Канал OTUS в Telegram!
Посты от приглашенных гостей из IT-тусовки, полезные статьи, подборки вакансий от партнеров ➞
Подробнее

Курсы

Программирование
Web-разработчик на Python Разработчик Python Разработчик на Spring Framework Разработчик Golang iOS Разработчик. Продвинутый курс v 2.0. PostgreSQL Vue.js разработчик Архитектор программного обеспечения Разработчик C++ MS SQL Server разработчик Android-разработчик. Базовый курс Архитектор высоких нагрузок Backend-разработчик на PHP Алгоритмы для разработчиков Team Lead 2.0 Python-разработчик. Базовый курс VOIP инженер Базы данных Разработчик программных роботов (RPA) на базе UiPath и PIX Разработчик голосовых ассистентов и чат-ботов Agile Project Manager в IT
Специализации Курсы в разработке Подготовительные курсы
+7 499 938-92-02

Как распределяется вычислительный объём на ядрах?

C___Deep_24.5_site-5020-8af3aa.png

Пример из практики: изучали мы, значит, многопоточность. Для лучшего понимания и погружения нужно было выполнить простую задачу — написать функцию, которая максимально задействует CPU. Что угодно лишь бы без ввода-вывода. Для простоты можно было добиться того, чтобы на машине для запуска время работы такой функции было около двух секунд. Добившись этого, необходимо было запустить столько функций, сколько ядер.

Общее время работы измениться не должно было. То есть должно остаться в пределах тех же двух секунд, можно мерить на глаз, никакого подвоха тут нет. Времени на переключение мало, активные фоновые процессы притушим. Суть примера в том, чтобы увидеть, как распределяется вычислительный объём на ядрах.

Итак, запускаем пять таких функций. Напомню, наша функция работает около двух секунд. Это значит, общее время должно получиться около десяти секунд.

Как это проверить?

Запускаем все пять подряд. Время суммируется. Запускаем все пять одновременно, но с разрешением работать только на одном ядре, например командой:

taskset -c 0 ./a.out

Можем даже воспользоваться высокоточным профайлером (шутка конечно же, но его точности нам хватит):

time taskset -c 0 ./a.out

Что у нас должно получиться при пяти потоках и разном количестве доступных ядер:

ядер|секунд
   -|-
   1|10
   2| 5
   3| 3.3
   5| 2

Дальнейшее увеличение доступных ядер ни к чему не приведёт.

Градусник не врёт, поменяйте тело

Приходит вопрос от слушателя «Что за ерунда?». Не получаются такие замеры, хоть ты тресни. Причём не просто не похожи, а отличаются разительно. Получаю код, начинаем разбираться:

int vector_size = 1'000;
int repeat = 100'000;

int main(int argc, char *argv[]) 
{      
 int threads_count = start_parsing(argc,argv);
 std::vector<std::thread> tv;
 tv.reserve(threads_count);
 for (int i = 0; i < threads_count; i++) {
   tv.push_back(std::thread([](){
     std::vector<int> v;
     v.resize(vector_size);
     std::iota(v.begin(),v.end(), 0);
     for (int i = 0; i < repeat; i++) {
       std::random_shuffle(v.begin(),v.end());
     }
   }));
 }
 std::cout << tv.size() << '\n';
 for (auto& t : tv) {
   t.join();
 }
}

На первый взгляд кажется, всё верно. Запускаем threads_count потоков с функциями, которые оголтело перемешивают элементы вектора. Замеряем без ограничений по ядрам, четыре ядра точно есть.

Одна функция в своём потоке отрабатывает за пять секунд. Запущенные две функции в двух потоках вместо ожидаемых тех же пяти секунд отрабатывают аж за тридцать три. А в четыре потока на четырёх ядрах аж за пятьдесят три.

Аномалия скрывалась в алгоритме std::random_shuffle, который в используемой библиотеке использовал std::rand в качестве генератора случайных чисел. Замена его на std::shuffle проблему полностью решила и теперь в независимости от количества потоков и наличии свободных ядер время держится в районе пяти секунд.

Мораль

При написании многопоточного кода пристальное внимание приходится уделять поведению сторонних библиотек. Но это вовсе не означает, что стандартная библиотека должна этой участи избегать, она тоже может подбрасывать сюрпризы.

Есть вопрос? Напишите в комментариях!

Не пропустите новые полезные статьи!

Спасибо за подписку!

Мы отправили вам письмо для подтверждения вашего email.
С уважением, OTUS!

Автор
0 комментариев
Для комментирования необходимо авторизоваться
🎁 Дарим сертификаты на скидку!
Запишитесь на июньскую трансляцию интересного вам дня открытых дверей и участвуйте в Акции ➞