Deutsch
Germany.ruФорумы → Архив Досок→ Программирование

Про многопоточность

1358  1 2 все
alex445 коренной житель15.02.22 00:46
NEW 15.02.22 00:46 
Последний раз изменено 15.02.22 00:53 (alex445)

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


Да и просто на физическом уровне - если данные хранятся в одном экземпляре, то их даже считать можно лишь поочереди. Допустим, что память позволяет считывать данные из одних и тех же ячеек параллельно (хотя навряд ли - это ж дополнительные линии связи на физическом уровне резервировать надо, а там и так в микросхемах и на контактных площадках тесно). А позволяет ли контроллер памяти в процессоре параллельный доступ к памяти? Т.е. доступ к самому контроллеру параллельный? Допустим, что да. Но у всех ли процессоров, на которых запускается наш код, такой параллельный доступ? Это всё неочевидно и зависит от. А в общем случае нужно считать так - становись в очередь, чтобы считать данные.


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


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


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


Можно вообще более обще и философски рассуждать. Ни одно событие в мире не происходит в одно время. Всегда что-то раньше, а что-то позже - вплоть до самого малого кванта времени, если таковой существует. А значит, даже теоретически единичный ресурс доступен только последовательно. Параллельность лишь в голове, на определённом уровне абстракции.

#1 
7495 местный житель15.02.22 11:25
7495
NEW 15.02.22 11:25 
в ответ alex445 15.02.22 00:46

У тебя какая конкретная задача стоит? Смотри чем многопоточность, многозадачность и асинхронность отличаются.


Вопрос зачем параллельно одновременно писать один и тот же файл?


Сейчас на ходу придумал такой пример: есть огромный файл, состоящий из стрингов по 64 символа, за цикл берём 32 ключа, раскидываем на 32 ядра и каждое обрабатывает свой ключ, потом обратно в другой файл складываем готовых 32 адреса, и так цикл за циклом,

но это многозадачность, но ты сам начал разговор про железо, а закончил программированием.


в твоих игрушках, наверное будет один вход и много выходов на разные кнопочки интерфейса, не?

Вопросы и Ответы - Программируем калькулятор пособий для беженцев вместе.
#2 
alex445 коренной житель15.02.22 12:38
NEW 15.02.22 12:38 
в ответ 7495 15.02.22 11:25
Вопрос зачем параллельно одновременно писать один и тот же файл?

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

#3 
alex445 коренной житель15.02.22 12:44
NEW 15.02.22 12:44 
в ответ 7495 15.02.22 11:25
есть огромный файл, состоящий из стрингов по 64 символа, за цикл берём 32 ключа, раскидываем на 32 ядра и каждое обрабатывает свой ключ, потом обратно в другой файл складываем готовых 32 адреса, и так цикл за циклом,

Если данные не взаимозависимы, то можно параллельно их посчитать. Но это тогда можно рассматривать как разные, независимые данные, а не одно данное в целом. Но даже чтобы разбить файл (или даже кусок массива в оперативе) на части, сделать же это нужно последовательно (или в ОЗУ можно читать параллельно несколько данных сразу на физическом уровне)? Т.е. сначала последовательно считываем массив и разбиваем его на части, а потом начинаем параллельную работу. Когда нужно результаты соединить, параллельная работа заканчивается - снова последовательно собираем результаты в одно целое.


А если данные всё же зависимы? Например, нужно по массиву провести сглаживание скользящим средним - вообще типичная задачка. Если один проход сглаживания - ок. А если несколько? Тогда нужно ждать результатов предыдущего прохода, чтобы начать следующий. По крайней мере, пока для следующего не наберётся достаточно данных от первого прохода.

#4 
alex445 коренной житель15.02.22 12:45
NEW 15.02.22 12:45 
в ответ alex445 15.02.22 12:44, Последний раз изменено 15.02.22 12:55 (alex445)

Да я просто затеял эти рассуждения, т.к. встречал мнение, что мол блокировки (lock) это плохо - доступ же блокируется. Это мол не настоящая многопоточность. Нужно всякие семафоры и барьеры памяти применять. Так это те же блокировки, только пониже уровнем и поменьше время удержания ресурса. А в реальности параллельного доступа почти всегда нет и почти всегда речь идёт о конкуренции и синхронизации, поэтому без блокирования доступа не обойтись.


Просто некоторые на собесах давай гонять по этим семафорам. На вопрос - а вы там действительно какие-то системы разрабатываете с критичным подсчётом наносекунд простоя, или просто "по приколу" спрашиваете? В ответ - настоящий сеньёр, даже если в основном джейсоны гоняет, должен всё это олично знать. Т.е. чел зубрит какую-то херню, которую не применяет или применяет "по приколу", когда она на самом деле не нужна. Ладно он сам такой мастурбацией занимается, так и других заставляет, и набирает лишь подобных ему. Потом правда понимает, что он же себе конкурентов создаёт, и немного осаживается.


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

#5 
wasja-de постоялец15.02.22 20:14
NEW 15.02.22 20:14 
в ответ alex445 15.02.22 12:45
Т.е. я считаю, что этими тонкостями заниматься должны не вообще любые программисты


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

#6 
alex445 коренной житель15.02.22 20:39
NEW 15.02.22 20:39 
в ответ wasja-de 15.02.22 20:14, Последний раз изменено 15.02.22 20:41 (alex445)

Можно.


Но если я этим постоянно не занимаюсь, то учить это смысла нет, как и спрашивать на собесах "за настоящего сеньёра". Я либо воспользуюсь специальной библиотекой, которую написали люди, этим постоянно занимающиеся, либо попрошу специалиста написать критичный участок кода.


Судя по качеству и производительности современного ПО, включая игры, где на картах тоже неслабо так считают, в индустрии работает куча некомпетентных людей, которым норм и на пару порядков производительность просадить. И ничего - все при зарплатах, должностях, на Теслах. ))

#7 
MrSanders коренной житель16.02.22 09:54
NEW 16.02.22 09:54 
в ответ wasja-de 15.02.22 20:14
А реальная польза начинается, когда перед этим вы изучите скорость доступа к памяти (блочно, рандом) скорость доступа к кешам разного уровня, арифметическим устройствам каждого ядра процессора, конвейера инструкций

Чушь. А предсказание переходов мне изучать не надо? И умножение матриц потому и работает так быстро на графике, что ничего синхронизировать не надо. Исходники не меняются, каждый поток считае свою ячейку для результата. Красота.

Каждый раз, когда программа использует потоки надо понимать что делаешь. Яваскриптерам и nodejs-истам можно расслабиться, они однопоточные :)
Сейчас такое встречается редко. Но встречается.

#8 
wasja-de постоялец16.02.22 11:09
NEW 16.02.22 11:09 
в ответ MrSanders 16.02.22 09:54
Чушь. А предсказание переходов мне изучать не надо?


я профкссионально уже лет так 20 занимаюсь именно выжиманием пиковой производительности на многопроцессорных многопоточных архитектурах. Предсказания переходов (в отличие от зависимости нескольких последовательных операций) обычно добавляют 20-30%, по сравнению с сотнями раз в остальном, мною вышеперечисленном

#9 
MrSanders коренной житель16.02.22 13:29
NEW 16.02.22 13:29 
в ответ wasja-de 16.02.22 11:09

То, чем вы занимаетесь, не влияет на ошибку в высказывании. Чтобы использовать локи/семафоры/что-то ещё связанное с синзронизвцией доступа к данным, всё перечисленное вами знать не надо.

Вот будет заниматься "выжиманием пиковой производительности" - тогда придётся.

#10 
wasja-de постоялец16.02.22 14:18
NEW 16.02.22 14:18 
в ответ MrSanders 16.02.22 13:29
То, чем вы занимаетесь, не влияет на ошибку в высказывании.

и в чем она? Пока вы идеально все остальное в архитектуре не используете, пытаться уговорить компилятор точнее соптимизировать конвейер инструкций не реально, а любая ошибка кеша по доступу к памяти вам накинет столько задержки, что неугаданный бранч вы просто не заметите. Если кто и хочет, чтобы у него алгоритмы стали работать бысттрее, в первую очередь надо понять как данные в памяти лежат и на сколько часто происходит случайный доступ в память или непопадание в кеш-строку, а далее, можно все остальное делать. Я регулярно даю различным заказчикам консультации по оптимизации их алгоритмов. Часто происходит так: кундепришел научиться программировать на графических картах, и мечтает ускорить свою программу в 10-20 раз из-за графических карт. После внимательно разборки полетов оказывается, что и без ГПУ его аглоритм можно ускорить раз так в 100 (однажды было в 1500 раз). А желание использовать ГПУ после этого бесследно исчезает.

#11 
MrSanders коренной житель16.02.22 15:01
NEW 16.02.22 15:01 
в ответ wasja-de 16.02.22 14:18
и в чем она?

Эммм... Процитирую себя двумя постами выше. "Чтобы использовать локи/семафоры/что-то ещё связанное с синхронизвцией доступа к данным, всё перечисленное вами знать не надо."

#12 
alex445 коренной житель16.02.22 15:02
16.02.22 15:02 
в ответ MrSanders 16.02.22 15:01

Если нижние абстракции не протекут.

#13 
Murr патриот16.02.22 17:00
Murr
NEW 16.02.22 17:00 
в ответ wasja-de 16.02.22 14:18

(оказывается, что и без ГПУ его) аглоритм можно ускорить

-----

+1.


Байка времен Фортран-2:

После изучения кода и замены вымученной процедуры получения остатка от деления на вызов стандартной процедуры выполнение программы ускорилось на два порядка.

И ничего не поменялось с тех пор.

Свеженькие прогеры все так же не понимают что и как можно поменять.

Имею почти пять страниц описания проводимого расчета.

Имею 10-15 килобайт кода выполняющего этот вымученный расчет.

После анализа того что именно они считают - заменяю все эту байду

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

Да, значения в табличке надо было посчитать отдельно, но всего один раз.

И что самое смешное - никто так и не понимает почему оно работает...

#14 
AlexNek патриот16.02.22 17:54
AlexNek
NEW 16.02.22 17:54 
в ответ Murr 16.02.22 17:00
И что самое смешное - никто так и не понимает почему оно работает.

Как там у тебя ... +1 спок

Всё слишком сложно, давайте сделаем проще. Как именно сейчас нужно.


#15 
alex445 коренной житель16.02.22 18:28
NEW 16.02.22 18:28 
в ответ Murr 16.02.22 17:00
значения в табличке надо было посчитать отдельно

Этими "грязными" штучками баловались с незапамятных времён:

1

2 - просто таблица заготовленных констант для корней из 2, 3 и т.д.

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


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

#16 
alex445 коренной житель16.02.22 18:35
16.02.22 18:35 
в ответ alex445 16.02.22 18:28, Последний раз изменено 16.02.22 18:58 (alex445)

Вообще, странно, что помидоры хвастаются такими детскими "оптимизациями". Я думал, они какой-то магией владеют, а они просто "хитро" лыбятся и недоговаривают - т.е. набивают себе цену дешёвыми понтами. )))


Т.е. молодой пионер задротит ночами, учит там всё - барьеры памяти, многопоточность, байтики считает, поразрядными сдвигами полтакта экономит... а сеньёр просто табличку расчитанную (ещё и не им поди) загнал и интерполирует себе.


Да, значения в табличке надо было посчитать отдельно, но всего один раз.
И что самое смешное - никто так и не понимает почему оно работает...

А в свои сорцы вы им заглянуть предусмотрительно не даёте, чтобы они веру в авторитет и волшебство не потеряли? ))

#17 
Murr патриот16.02.22 19:21
Murr
NEW 16.02.22 19:21 
в ответ alex445 16.02.22 18:35

А в свои сорцы вы им заглянуть предусмотрительно не даёте

------

Почему не даю - даю - смотрите без ограничений...

Потому и пишу - не понимают...

Ну не понимают как построена таблица - там не всегда просто обратная функция...

Не понимают какие допущения сделаны и какие ограничения были проверены...

Соответственно, не понимают как ее можно заменить...

Начинают менять "от балды" - все начинает "сыпаться"...


Вот твой пример - чтобы тебе понять достаточно простые вещи - придется

что-то учить... а учить - ты не хочешь... соответственно - упорешься, но ничего

сделать не сможешь... у остальных часто так же...

#18 
wasja-de постоялец16.02.22 21:04
NEW 16.02.22 21:04 
в ответ alex445 16.02.22 18:28, Последний раз изменено 16.02.22 21:28 (wasja-de)
Этими "грязными" штучками баловались с незапамятных времён:2 - просто таблица заготовленных констант для корней из 2, 3 и т.д.


но в современных реалиях они могут привести к отрицательному эффекту :)


Ниже - пример вычисления синусов и косинусов в лоб и точно такой же алгоритм с предвычисленными значениями, которые были сохранены в таблицу. На "удивление", вариант с таблицей работает слегка медленнее, так как в нем происходит случайный доступ по памяти, который просто в сотни раз портит производительность. Распараллеливание на процессорные ядра обоих алгоритмов только усугубит проблему, то есть первый вариант будет работать еще быстрее, а второй - не ускорится (на некоторых компьютерных архитектурах может даже замедлиться).



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


#define N (4*1024*1024)
#define M 32

#if 0 // первый вариант с вычислением синусов в лоб
int main()
{ float s=0.1, c=0.1;

for(int j=0; j for(int i=0; i { float s1, c1;
int pos= lrand48() & (N-1);
sincosf(((float)pos)*0.00001, &s1, &c1);
float s2=s1*c+c1*s;
float c2=c1*c-s1*s;
c=c2; s=s2;
}
printf("%lg %lg\n", c, s);
return 0;
}
#else // метод с предвычисленной таблицей тригонометрических функций
float Sin[N];
float Cos[N];
int main()
{ float s=0.1, c=0.1;

for(int i=0; i sincosf(((float)i)*0.00001, Sin+i, Cos+i);

for(int j=0; j for(int i=0; i { float s1, c1;
int pos= lrand48() & (N-1);
s1=Sin[pos];
c1=Cos[pos];
float s2=s1*c+c1*s;
float c2=c1*c-s1*s;
c=c2; s=s2;
}
printf("%lg %lg\n", c, s);
return 0;
}
#endif



#19 
7495 местный житель16.02.22 21:17
7495
NEW 16.02.22 21:17 
в ответ Murr 16.02.22 19:21
Начинают менять "от балды" - все начинает "сыпаться"...


Это повод для гордости? Умер от короны сопровождающий программист, смену не вырастил, документации нет, проект можно закрывать, не?

Вопросы и Ответы - Программируем калькулятор пособий для беженцев вместе.
#20 
1 2 все