LINUX.ORG.RU

Принципиальный вопрос о многопоточности, кешах ЦП.


1

2

1. Что конкретно имеют ввиду под высокими накладными расходами при переключении потоков? Мне кажется, 250 раз в секунду перезаписывать содержимое всех регистров - это фигня полная при современных частотах в район 2-3 Ггц. Вероятно имеется ввиду удар по кешам? Только кеши «привыкли» к текущему потоку, как надо всё забывать и переориентироваться на другой. Это главный враг?

2. Правильно я понимаю, что при наличии 4 физических ядер и 4 (или менее) активных потоках (остальные спят - редко получают управление) современный планировщик linux/windows/другое постарается оставить каждый тред на своём ядре?

3. Верно ли, что установленные 250 Гц в конфиге ядра - это можно понимать как «как минимум 250 шансов переключения контекста в секунду»? Шансов - ибо если пришло очередное прерывание таймера и пора переключаться, шедулер не будет переключать контекст если в этот момент все остальные процессы/потоки спят (ждут i/o, таймеров...). То есть один что-то активно вычисляющий поток будет получать подряд много квантов?

4. Я тут выше за кеши переживал... Но ведь когда много раз в секунду шедулер получает управление из обработчика прерывания таймера, это ведь тоже переключение контекста (из текущей задачи в код обработчика таймера). Это ведь наверное тоже бьёт кешам по мозгам или как?

Все эти вопросы связаны вот с чем.

У меня есть 50 очередей из 100млн заданий и 4 ядра. Я могу запустить 50 потоков на каждую очередь, либо запустить 4 потока и давать им пачки заданий. Второй случай выглядит (интуитивно) лучше - меньше переключений между тяжёлыми потоками, каждый работает на своём ядре, кеш и конвееры не сбиваются. При 50 потоках, будет больше смен контекста в смысле смен состояний кеша и конвееров и прочих подобных вещей (извините, слабо понимаю все тонкости пока).

2. нет. Есть такое понятие, как CPU affinity. Так вот, в старом планировщике O(1) оно соблюдалось достаточно строго, и потоки не мигрировали туда-сюда без необходимости. CFS порвал с этой практикой, поэтому мигрирует потоки несколько раз в секунду. BFS пошёл ещё дальше — миграция происходит больше сотни раз в секунду. Это делается ради буквы «F» в названии CFS.

4 постоянных обработчика с асинхронной моделью обработки запросов действительно лучше. nginx доказал это.

post-factum ★★★★★
()

У меня есть 50 очередей из 100млн заданий и 4 ядра.

Фигасе. А может пора посмотреть в сторону более подходящего инструмента, чем изобретать сверхзвуковые 4-колесные велосипеды?

blexey ★★★★★
()
Ответ на: комментарий от post-factum

Спасибо! Самое интересное - понять, по какой причине 4 тяжёлых потока лучше 50 тяжёлых, учитывая, что 4 будут дико мигрировать. Чем прыгание 4 потоков по процессорам отличается от прыгания 50-ти?

Для nginx такой вопрос немного по-другому стоит: не «4 vs 50», а «4 vs 10000» :) При подходе «поток на соединение» в современных мега-серверах шедулер бы много думал + ушла бы память на служебную информацию о каждом потоке.

Но если при 4 ядрах запущено 4 потока и они скачут так же, как 50, то почему выигрывают 4?

Я интуитивно привык думать, что запускать тяжёлых потоков больше, чем ядер смысла нет - физику не обманешь, но хочу понять - по какой причине это так? Из-за кешей и конвееров, которые при переключении сбрасываются? Если так - тогда ещё один вопрос в моём пункте (4) :)

kiverattes ★☆
() автор топика
Ответ на: комментарий от kiverattes

4 лучше 50 из-за меньшего оверхеда на переключение. Конечно, каждый из этих 4-х потоков должен загружать ядро на 100%, т.е. не простаивать. Это задача распределения запросов между потоками.

post-factum ★★★★★
()
Ответ на: комментарий от post-factum

Вот в «принципе действия» этого оверхеда вопрос и заключается. Интересно знать как этот оверхед оверхедит, анатомия оверхеда (-;

Простаивать не собираемся, это не сервер, всё лежит в ОЗУ.

kiverattes ★☆
() автор топика

зацени простенькую програмку - которая тока и делает что контент свичиться у меня максимум получаеться 110тон свитчей в секунду - при этом проц на 100% занят бесполезной (почти) работой

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

main(){
    int i,j,k,fd[2],fd2[2];
    time_t t;

    j=0;
    pipe(fd);
    pipe(fd2);

    if(fork()==0){
        i=0;
        t=0;
        for(i=0;i<10000000;i++){
            if(!(i%100))
                if(time(NULL)!=t){
                    printf("%d\n",j);
                    j=0;
                    t=time(NULL);
                    }
            write(fd[1],&k,1);
            read(fd2[0],&k,1);
            j++;
            }
        }
    else
        for(j=0;j<1000000;j++){
            read(fd[0],&k,1);
            write(fd2[1],&k,1);
            }
    }

даже если у тебя проц будет 25тон в секунду свитчиться в твоей проге - то 25% процессорной мощности будет расходоваться бесполезно

реч неидет о 250 свитчей в секунду - реч идет о размерностях на пару порядков выше

ae1234 ★★
()

на практике перескок потока приводит к тому, что у него оказываются неактуальными кэши L1 и L2. L3 обычно общий и в нем данные сохраняются(если он inclusive)

CFS не скачет, если загрузка не потолковая. у него очереди вида О(1), которые время от времени балансируются.

ckotinko ☆☆☆
()

куда больший удар наносят операции вида mmap/munmap

изза них надо останавливать ядра, где крутится задетая программа, заставлять их сбрасывать TLB, обновлять страницы, запускать ядра обратно. особенно сие актуально на коре2 у которых TLB работает через жопу.

ckotinko ☆☆☆
()
Ответ на: комментарий от ckotinko

CFS не скачет, если загрузка не потолковая.

А можно пояснить, не потолковая загрузка - это что? Типа все потоки занимают не более какого-то % суммарно? Тогда они остаются на своих ядрах?

Видимо потому, что когда есть свободный ресурс - на нём выполняются мелко-требовательные потоки, а как только времени перестаёт хватать им тоже, начинают вытесняться тяжёлые треды. Да?

kiverattes ★☆
() автор топика
Ответ на: комментарий от ae1234

Я, честно говоря, не понял как вы померяли частоту переключений контекста. Пайпы имеют буфферные очереди, один процесс туда запишет килобайт, переключится, второй прочитает килобайт и т.п. Не понял методику измерений, короче )

kiverattes ★☆
() автор топика
Ответ на: комментарий от kiverattes

ну грубо говоря, у вас 2 ведра и 3 потока. 1й жрет 70%, второй -60%, третий 50%. При любом раскладе кто-то будет обижен, и поэтому CFS равномерно обижает все потоки. А на четырех ведрах все потоки будут сидеть на жопе ровно

ckotinko ☆☆☆
()
Ответ на: комментарий от kiverattes

а чего ее мерить - ее программа пишет
ну иль в соседнем окне vmstat 1

а методика простая - записали 4 байта по направлению ко 2ому процессу - читам 4 байта от второго процесса
на втором процессе тож самое
(read write)

ae1234 ★★
()
Ответ на: комментарий от ckotinko

ну грубо говоря, у вас 2 ведра и 3 потока. 1й жрет 70%, второй -60%, третий 50%.
При любом раскладе кто-то будет обижен, и поэтому CFS равномерно обижает все
потоки. А на четырех ведрах все потоки будут сидеть на жопе ровно

То есть всё-таки CFS не трогает потоки, когда по ресурсам на это есть возможность? Например 4 ядра, и 4 потока: 100%, 100%, 90%, 80% - первые два можно не трогать, а прочие малоактивные потоки (dhcpd) можно выполнять за счёт свободного времени других ядер? Есть у CFS такое «понимание»?

kiverattes ★☆
() автор топика

а чего ты так переживаеш за это колво работающих тредов ? если логика работы позволяет - то стандартно делают так определяют колво процессоров в системе (вместе с гипертредингом) и запускают равное им (или +1) колво работающих тредов (+1 это на случай - когда скажем один тред напрмиер будет записывать данные на диск скажем - чтоб нетерять простой)

общая производительность возрастает - пока колво тредов не сравняется с колвом реальных процессоров а потом - с увеличением колва тредов - общая производительность начинает (медленно) но падать

ae1234 ★★
()
Ответ на: комментарий от ae1234

тоесть 1 байт

Запустил вашу программку, пока под рукой только VirtualBox / windows, железный linux только дома :)

При кол-ве процессоров в виртуалке = 1, переключений было в районе 200K - 300K, действительно дофига. При включении в виртуалке поддержки 4 ядер, скорость упала до 19-20K :)

Интересно, почему так часто. Видимо частота системного таймера, установленная при конфиг. ядра - это не максимальная частота переключения потоков/процессов. Видимо если процесс выполняет блокирующую операцию (read при отсутствующем байте в буффере), исполняться в этом процессе больше нечему, пока байт не придёт, значит шедулер может отдать управление другому процессу сразу же. Поэтому и получается так много переключений.

kiverattes ★☆
() автор топика
Ответ на: комментарий от ae1234

Я это понимаю, но чисто интуитивно, на уровне нечётко сформулированных образов в голове. Хотелось бы разобраться поглубже, чтобы обосновывать своё мнение перед коллегами/начальниками (-;

kiverattes ★☆
() автор топика
Ответ на: комментарий от kiverattes

ты переключения какие смотрел ? которые программа показывает или vmstat ?

если vmstat то дели их на 2 - потому как 2 потока свитчаться посуществу
на ксеона2.4 - програама показывает около 90тон свитчей максимум что возможно

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

вот смотри - идеальная ситуация - в системе нет никаких устройств - 2 ядра - и 2 работающих процесса - процессы просто сидят и думают чтото про себя - ничем недергая ядро
в системе нет никаких девайсов - тоесть нет никаких прерываний - точнее есть одно - как раз того самого системного таймера - который 100-1000 раз в секунду дергает ядро прерыванием

ну и запустили 3тий процесс - такойже как эти 2
пока работают первые 2 - ядро неполучает никакой возможности чтото сделать (эти проги никак необращаються в ядро) - но таймер сделал прерывание - и ядро получило управление - и внутри решило про себя что дескать пока бы и 3тий поток запустить на исполнение
и так далее

тоесть при такой схеме - шедуллер будет решать чего куда переключить с частотой таймера

а вот если потоки общаються - через пайпы или семафоры там разные - то ядро будет куда чаще получаться управление на себя - и потоки бьудут весьма часто ожидать данные с другого потока иль наоборот посылать их в дургой поток
и при этом шедуллер будет очень часто щелкать процессами
и величина что вносит в это работу системного таймера будет куда как мала

ae1234 ★★
()
Ответ на: комментарий от ae1234

1. Которые программа показывает.

2. Описанное вами в принципе совпадает с тем, как я всё представляю после запуска вашего примера (-;

Спасибо.

kiverattes ★☆
() автор топика
Ответ на: комментарий от kiverattes

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

пока все не будут одинаково несчастны

ckotinko ☆☆☆
()
Ответ на: комментарий от bk_

Дреппер был адекватен, когда писал это? Это можно читать?

Дреппер всю жизнь был умён и адекватен. Он просто большая сволочь, но интеллекту это никак не вредит.

mv ★★★★★
()

ну всё так, но насколько просядет именно твоя конфигурация от этого покажут только эксперименты. Причём не обязательно «1 поток - одно ядро» это лучшая конфигурация. В общем, смотри по максимому производительности.

true_admin ★★★★★
()
Ответ на: комментарий от ckotinko

на практике перескок потока приводит к тому, что у него оказываются неактуальными кэши L1 и L2. L3

эти кэши всегда актуальны, они тэгируются по физическому адресу.

dimon555 ★★★★★
()
Ответ на: комментарий от dimon555

(КО mode)по условию задачи поток переехал на соседнее ядро

ckotinko ☆☆☆
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.