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

Вопрос зачем параллельно одновременно писать один и тот же файл?
Не обязательно файл. Я скорее про данные в оперативе имел ввиду. Но суть та же - если физический доступ к данным невозможен параллельно, то никакой настоящей параллельной работы с ними не может быть в принципе. Есть лишь различные реализации очередей, которые при некотором приближении (микрооптимизациях с очерёдностью доступа) и на некоторых уровнях абстракций могут рассматриваться как параллельная работа.
есть огромный файл, состоящий из стрингов по 64 символа, за цикл берём 32 ключа, раскидываем на 32 ядра и каждое обрабатывает свой ключ, потом обратно в другой файл складываем готовых 32 адреса, и так цикл за циклом,
Если данные не взаимозависимы, то можно параллельно их посчитать. Но это тогда можно рассматривать как разные, независимые данные, а не одно данное в целом. Но даже чтобы разбить файл (или даже кусок массива в оперативе) на части, сделать же это нужно последовательно (или в ОЗУ можно читать параллельно несколько данных сразу на физическом уровне)? Т.е. сначала последовательно считываем массив и разбиваем его на части, а потом начинаем параллельную работу. Когда нужно результаты соединить, параллельная работа заканчивается - снова последовательно собираем результаты в одно целое.
А если данные всё же зависимы? Например, нужно по массиву провести сглаживание скользящим средним - вообще типичная задачка. Если один проход сглаживания - ок. А если несколько? Тогда нужно ждать результатов предыдущего прохода, чтобы начать следующий. По крайней мере, пока для следующего не наберётся достаточно данных от первого прохода.
Да я просто затеял эти рассуждения, т.к. встречал мнение, что мол блокировки (lock) это плохо - доступ же блокируется. Это мол не настоящая многопоточность. Нужно всякие семафоры и барьеры памяти применять. Так это те же блокировки, только пониже уровнем и поменьше время удержания ресурса. А в реальности параллельного доступа почти всегда нет и почти всегда речь идёт о конкуренции и синхронизации, поэтому без блокирования доступа не обойтись.
Просто некоторые на собесах давай гонять по этим семафорам. На вопрос - а вы там действительно какие-то системы разрабатываете с критичным подсчётом наносекунд простоя, или просто "по приколу" спрашиваете? В ответ - настоящий сеньёр, даже если в основном джейсоны гоняет, должен всё это олично знать. Т.е. чел зубрит какую-то херню, которую не применяет или применяет "по приколу", когда она на самом деле не нужна. Ладно он сам такой мастурбацией занимается, так и других заставляет, и набирает лишь подобных ему. Потом правда понимает, что он же себе конкурентов создаёт, и немного осаживается.
Т.е. я считаю, что этими тонкостями заниматься должны не вообще любые программисты, пусть они и сеньёры, а лишь специализирующиеся на таких системах с тонкой настройкой многопоточного доступа к ресурсам. Остальным достаточно локнуть (lock) и следить за непоявлением дедлоков.
Т.е. я считаю, что этими тонкостями заниматься должны не вообще любые программисты
очень правильно считаете. А реальная польза начинается, когда перед этим вы изучите скорость доступа к памяти (блочно, рандом) скорость доступа к кешам разного уровня, арифметическим устройствам каждого ядра процессора, конвейера инструкций, и с удивлением обнаружите, что на современном компьютере можно слегка не подумав так по идиотски запрограммировать умножение двух довольно больших матриц, что оно будет раз так тысячу медленнее, аналогичного алгоритма, в котором просто будет учитываться все вышеперечисленное. А на графических картах (для тех, кто на них считается) можно и в миллион раз отличие по скорости получить.
Можно.
Но если я этим постоянно не занимаюсь, то учить это смысла нет, как и спрашивать на собесах "за настоящего сеньёра". Я либо воспользуюсь специальной библиотекой, которую написали люди, этим постоянно занимающиеся, либо попрошу специалиста написать критичный участок кода.
Судя по качеству и производительности современного ПО, включая игры, где на картах тоже неслабо так считают, в индустрии работает куча некомпетентных людей, которым норм и на пару порядков производительность просадить. И ничего - все при зарплатах, должностях, на Теслах. ))
А реальная польза начинается, когда перед этим вы изучите скорость доступа к памяти (блочно, рандом) скорость доступа к кешам разного уровня, арифметическим устройствам каждого ядра процессора, конвейера инструкций
Чушь. А предсказание переходов мне изучать не надо? И умножение матриц потому и работает так быстро на графике, что ничего синхронизировать не надо. Исходники не меняются, каждый поток считае свою ячейку для результата. Красота.
Каждый раз, когда программа использует потоки надо понимать что делаешь. Яваскриптерам и nodejs-истам можно расслабиться, они однопоточные :)
Сейчас такое встречается редко. Но встречается.
Чушь. А предсказание переходов мне изучать не надо?
я профкссионально уже лет так 20 занимаюсь именно выжиманием пиковой производительности на многопроцессорных многопоточных архитектурах. Предсказания переходов (в отличие от зависимости нескольких последовательных операций) обычно добавляют 20-30%, по сравнению с сотнями раз в остальном, мною вышеперечисленном
То, чем вы занимаетесь, не влияет на ошибку в высказывании. Чтобы использовать локи/семафоры/что-то ещё связанное с синзронизвцией доступа к данным, всё перечисленное вами знать не надо.
Вот будет заниматься "выжиманием пиковой производительности" - тогда придётся.
То, чем вы занимаетесь, не влияет на ошибку в высказывании.
и в чем она? Пока вы идеально все остальное в архитектуре не используете, пытаться уговорить компилятор точнее соптимизировать конвейер инструкций не реально, а любая ошибка кеша по доступу к памяти вам накинет столько задержки, что неугаданный бранч вы просто не заметите. Если кто и хочет, чтобы у него алгоритмы стали работать бысттрее, в первую очередь надо понять как данные в памяти лежат и на сколько часто происходит случайный доступ в память или непопадание в кеш-строку, а далее, можно все остальное делать. Я регулярно даю различным заказчикам консультации по оптимизации их алгоритмов. Часто происходит так: кундепришел научиться программировать на графических картах, и мечтает ускорить свою программу в 10-20 раз из-за графических
карт. После внимательно разборки полетов оказывается, что и без ГПУ его аглоритм можно ускорить раз так в 100 (однажды было в 1500 раз). А желание использовать ГПУ после этого бесследно исчезает.
и в чем она?
Эммм... Процитирую себя двумя постами выше. "Чтобы использовать локи/семафоры/что-то ещё связанное с синхронизвцией доступа к данным, всё перечисленное вами знать не надо."
(оказывается, что и без ГПУ его) аглоритм можно ускорить
-----
+1.
Байка времен Фортран-2:
После изучения кода и замены вымученной процедуры получения остатка от деления на вызов стандартной процедуры выполнение программы ускорилось на два порядка.
И ничего не поменялось с тех пор.
Свеженькие прогеры все так же не понимают что и как можно поменять.
Имею почти пять страниц описания проводимого расчета.
Имею 10-15 килобайт кода выполняющего этот вымученный расчет.
После анализа того что именно они считают - заменяю все эту байду
трехмерной табличкой, с просто кастированными к целому индексами.
Да, значения в табличке надо было посчитать отдельно, но всего один раз.
И что самое смешное - никто так и не понимает почему оно работает...
И что самое смешное - никто так и не понимает почему оно работает.
Как там у тебя ... +1
Всё слишком сложно, давайте сделаем проще. Как именно сейчас нужно.
значения в табличке надо было посчитать отдельно
Этими "грязными" штучками баловались с незапамятных времён:
2 - просто таблица заготовленных констант для корней из 2, 3 и т.д.
3 - вообще любые заранее посчитанные значения, а затем интерполяция между ближайшими (если функция между ними не проходит через экстремумы)
По третьему способу сделал банально в своём проекте на последней работе - из справочника нашёл значения для величин, но с шагом. Забил их в программу константами и промежуточные значения получал линейным интерполированием. Если прямо упороться надо - можно отдельно построить график и функцию для него найти, которую забить в прогу. Сейчас это много где предлагается.
Вообще, странно, что помидоры хвастаются такими детскими "оптимизациями". Я думал, они какой-то магией владеют, а они просто "хитро" лыбятся и недоговаривают - т.е. набивают себе цену дешёвыми понтами. )))
Т.е. молодой пионер задротит ночами, учит там всё - барьеры памяти, многопоточность, байтики считает, поразрядными сдвигами полтакта экономит... а сеньёр просто табличку расчитанную (ещё и не им поди) загнал и интерполирует себе.
Да, значения в табличке надо было посчитать отдельно, но всего один раз.
И что самое смешное - никто так и не понимает почему оно работает...
А в свои сорцы вы им заглянуть предусмотрительно не даёте, чтобы они веру в авторитет и волшебство не потеряли? ))
А в свои сорцы вы им заглянуть предусмотрительно не даёте
------
Почему не даю - даю - смотрите без ограничений...
Потому и пишу - не понимают...
Ну не понимают как построена таблица - там не всегда просто обратная функция...
Не понимают какие допущения сделаны и какие ограничения были проверены...
Соответственно, не понимают как ее можно заменить...
Начинают менять "от балды" - все начинает "сыпаться"...
Вот твой пример - чтобы тебе понять достаточно простые вещи - придется
что-то учить... а учить - ты не хочешь... соответственно - упорешься, но ничего
сделать не сможешь... у остальных часто так же...
Этими "грязными" штучками баловались с незапамятных времён: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
Начинают менять "от балды" - все начинает "сыпаться"...
Это повод для гордости? Умер от короны сопровождающий программист, смену не вырастил, документации нет, проект можно закрывать, не?

Этими "грязными" штучками баловались с незапамятных времён:2 - просто таблица заготовленных констант для корней из 2, 3 и т.д.
но в современных реалиях они могут привести к отрицательному эффекту :)
По ссылке 1, что я привёл, где-то там есть рассуждения, что сейчас такие "хаки" не нужны. Просто в старые времена (конец 80-х - начало 90-х) не было специнструкций для подобных математических вычислений, сопроцессоров, а обращение к специализированной библиотеке занимало слишком много времени. А в тех же игровых движках того времени старались выжимать производительность по-максимуму. Потом появилась куча инструкций-расширений в процессорах, большие кеши, в которые подобные вычисления можно целиком загнать, и надобность в подобных хаках отпала.
Начинают менять "от балды" - все начинает "сыпаться"...
Это повод для гордости? Умер от короны сопровождающий программист, смену не вырастил, документации нет, проект можно закрывать, не?
Старьё и надо периодически закрывать, каким бы хорошим оно ни было. Дорогу молодым! Пусть сами чё-нить придумывают. )
К таблице с "секретными" значениями должна быть приложена инструкция-спецификация, где объяснено, что, как, зачем сделано было. Объяснения граничных значений, ссылки на теорию, где почитать.
Это повод для гордости?
-----
Вполне.
смену не вырастил
------
Эээ????
А с каких пор ЭТО стало заботой программиста?
Рабобратель имеет возможность поискать прогера с необходимой подготовкой - за забором их дохренища...
документации нет
------
Учебники, как правило, общедоступны и не дублируются в документации.
Осталось найти того, кто не поленился изучить необходимое...
ссылки на теорию, где почитать
-----
Что видят мои глаза? Ярый противник изучения чего-либо говорит об том, что надо что-то изучить? Ну нифига себе...
Не для меня ссылки - просто должны быть в комментах для удобства. )
По ссылке 1, что я привёл, где-то там есть рассуждения, что сейчас такие "хаки" не нужны.
не буду комментировать
Просто в старые времена (конец 80-х - начало 90-х) не было специнструкций для подобных математических вычислений, сопроцессоров, а обращение к специализированной библиотеке занимало слишком много времени.
и сейчас вычисление синуса "стоит" раз так в 100 больше одного "флоп"а (одной операции с плавающей точкой с процессорного ядра. Так и раньше было, против математики не попрешь.
А в тех же игровых движках того времени старались выжимать производительность по-максимуму.
и сейчас и в играх и в куче других приложений также поступают, просто некоторые "программисты" кроме дотнета или жс ничего больше перед своим носом не видят, и из-за этого складывается такое впечатление.
Потом появилась куча инструкций-расширений в процессорах, большие кеши, в которые подобные вычисления можно целиком загнать, и надобность в подобных хаках отпала.
я бы сказал, что на процессорах 90-х (кроме тех, чт овыпускал Крей Ресеч) программировать было на порядки проще, чем на современных, если конечно цель
программирования - высокопроизводительные алгоритмы, а юзер-морда в дотнете. И даже если сравнить массивно-параллельный блок из векторно-конвейерного Cray-C90 и даже современную графическую карту, то на Крею тоже было проще выжимать пиковую производительность. Я это говорю не с чьих-то слов, а именно как специалист, который этим занимался и занимается.
Возможно из-за повышенной сложности современных компьютерных архитектур и существенно большего числа быдлокодеров чем было раньше (порог входа в профессию меньше), и возникают такие утверждения, как вы выше привели, которые потом массово такими же и тиражируются.
и сейчас вычисление синуса "стоит" раз так в 100 больше одного "флоп"а (одной операции с плавающей точкой с процессорного ядра. Так и раньше было, против математики не попрешь.
А все эти штуки не помогают быстрее синус вычислять?
Читал, что эти новые процы яблочные потому такие производительные и толстые, что они в них сопроцессоров для разных операций напихали. Для общего назначения они по-прежнему дерьмо, но в спецзадачах всех рвут.
А в тех же игровых движках того времени старались выжимать производительность по-максимуму.и сейчас и в играх и в куче других приложений также поступают
Может, в движках и поступают, а те, кто игры разрабатывают, херят это всё потом не моргнув глазом. Отрендерить море под землёй, потому что лень вырезать часть поверхности или просто производитель видеокарты денег занёс - как нефиг делать.
Возможно из-за повышенной сложности современных компьютерных архитектур и существенно большего числа быдлокодеров чем было раньше (порог входа в профессию меньше), и возникают такие утверждения, как вы выше привели, которые потом массово такими же и тиражируются.
Просто акценты сместились с экономии на железе на экономию на... уже не на софте, а на людях. Раньше компьютеры были дорогие и слабые, а требования к ПО и его архитектурная сложность (не алгоритмическая) - ниже. Зарплаты прогеров по сравнению с этим были относительно невелики. Можно было посадить отдел, который бы только тем и занимался, что такты экономил и байтики руками двигал. Теперь в большинстве случаев проще железа докупить, чем позволить программистам такую вахканалию.
А все эти штуки не помогают быстрее синус вычислять?
не, не помогают, как кордиком много лет считали, так и до сих пор считают (совсем на заре программирования еще в ряд Тейлора разлагали, но это был совсем ужас), вообще - синус очень редко встречающаяся процедура, в очень многих реальных задачах можно алгоритм так модифицировать, что вызывать синус надо в миллионы раз реже, чем умножение и сложение. В общем также все обстоит и с любой другой операцией кроме плюса, минуса и умножения.
Просто акценты сместились с экономии на железе на экономию на... уже не на софте, а на людях. Раньше компьютеры были дорогие и слабые, а требования к ПО и его архитектурная сложность (не алгоритмическая) - ниже. Зарплаты прогеров по сравнению с этим были относительно невелики. Можно было посадить отдел, который бы только тем и занимался, что такты экономил и байтики руками двигал. Теперь в большинстве случаев проще железа докупить, чем позволить программистам такую вахканалию.
Я предполагаю, что не только это, а больше - маркетинг.
Раньше компьютеры были не распространены, так как они только появлялись, хотя в США уже в 80-х когда я посещал обычные простые американские семьи, у каждого был дома компьютер, другое дело, что его только как пишущую машинку и игротеку использовали.
Сейчас компьютеры вытеснили все возможные и не возможные остальные направления, и их почти всегда используют для трансформации информации: например из веба на экран, из чата одного пользователя к другому. Для этого сильных мощностей не нужно, только для игрунов и немного для меломанов.
Закон Мура (не того, что в форуме, он крякнет, чтоб в его честь что-то бы назвали) ведь так толком
и не отменился, и топовый суперкомпьютер мира за каждые два года так и удваивает свою производительность, и, очевидно, на нем не майнятся, а что-то полезное считают.
Но вот современные топовые высокопроизводительные алгоритмы по их производительности отличаются от бытовых пользовательских программ на столько сильно, что действительно, знаний программистам уже не нужно, и, в большинстве случаев, современный программист - это тот, кто умеет пользоваться фреймворками, особо не разбираясь почему эти фреймворки так устроены.
Читал, что эти новые процы яблочные потому такие производительные и толстые, что они в них сопроцессоров для разных операций напихали. Для общего назначения они по-прежнему дерьмо, но в спецзадачах всех рвут.
Вы сравните их флопы с любой топовой графичесой картой. Вот для сравнения, на М1 от яблока сейчас можно теоретически снять 2.6 терафлопса на 32 битах, а на купленной мной в 2013 АМДшной карте (заметим, это почти 10 лет назад!!!) за пару сотен евро я довольно надежно по 6-7 терафлопс снимал при пиковой около 12 терафлопсов. Яблоко всегда любило маркетинг, а 2 ТФлопса - это же круто, можно втридорого впарить тому, кто на эти яблоки подсел и только сейчас услышал новое модное словечко.
И, кстати, про такие сопроцессоры, или ГПУ. На них действительно очень не тривиально программировать. Особенно если до этого писал на фреймворках и не сильно понимаешь хотя бы как писать программы для обычного процессора
с его многоуровневой памятью. Я кстати очень советую все-таки заценить тот пример, что я выше приводил по поводу синуса - как хороший показательный случай, когда криво использованная память убивает производительность. И ведь с этим ничего не поделаешь. На компьютерах, что большинство участников форума сейчас это пишут-читают, время прочтения одного числа со случайным адресом из общей памяти в тысячи раз больше времени на умножение двух чисел внутри процессорной АЛУшки.
Яблоко всегда любило маркетинг, а 2 ТФлопса - это же круто, можно втридорого впарить тому, кто на эти яблоки подсел и только сейчас услышал новое модное словечко.
В тестах, в которых тестят эти мобильные процы и потом всем хвастаются, есть специализация по разным типам операций. Яблоко просто напихало в свои новые процы сопроцессоров-ускорителей (не отдельно, а прямо в общем кристалле) для этих операций и теперь всех в тестах "рвёт". Такое мнение я слышал.
Операции, конечно, не от балды взяты - разные из них применяются часто в разных типах работы. Типа декодирование видео, обработка изображений и прочее. Но суть именно в сопроцессорах - для самых распространённых работ определили по специальному куску от общего кристалла. А часть проца для общей работы так и осталась "дохлой".