Author

Topic: Могопоточное приложение на C++ (Read 583 times)

newbie
Activity: 35
Merit: 0
Есть полно задач, где многопоточность просто необходима.
Смотря под какие задачи, есть уже много готового софта под различные требования
newbie
Activity: 15
Merit: 0
Спорить о полезности многопоточных приложений - это глупость, когда частота процессоров почти не растет, а ядер полно!
Есть полно задач, где многопоточность просто необходима.

Специализируюсь на написании многопоточных приложений на С/С++ Windows/Linux/FreeBSD
Кому нужно пишите, Telegram @dfservice, другие контакты: https://www.dfservice.com/mail/ru/
member
Activity: 238
Merit: 29
Сейчас бы Boost.ASIO. использовать в 2020 году  Cheesy
legendary
Activity: 2618
Merit: 2304
100 узлов. Допустим, на соединение с узлом и скачивание тратится 15 минут. В одном потоке обработать 100 узлов займёт 25 часов. В 100 потоках 15 минут.

Или 200 узлов. В данном случае в одном потоке это будет 50 часов, в 200 потоках... ты не поверишь... снова 15 минут.

Не факт. Скорость скачивания файла зависит, главным образом, от ширины пропускаемого канала.

На самом деле, создание TCP/IP-соединений и скачивание файлов можно реализовать в одном потоке путём использования неблокирующего режима, когда программа работает с сокетами через события Event. Кстати, иногда веб-серверы ограничивают скорость отдачи данных по каждому TCP/IP-соединению, тогда браузеры пытаются создать дополнительные соединения и скачивать файлы с директивой Range в HTTP-заголовках, но веб-серверы, в зависимости от настроек, обычно отвергают такие множественные соединения для обращения к одному ресурсу.

Вообще, описанная схема со множеством потоков, наверно, более актуальна для отправки ICMP-пакетов, когда необходимо быстро проверять узлы сети на доступность, пингуя большое количество IP-адресов.
legendary
Activity: 3108
Merit: 1359
100 узлов. Допустим, на соединение с узлом и скачивание тратится 15 минут. В одном потоке обработать 100 узлов займёт 25 часов. В 100 потоках 15 минут.
Не допустим. Если разработчик не умеет в неблокирующие сокеты, то ему надо не писать многопоточные приложения, а увольняться.  Roll Eyes

P.S. Скачивание файлов - это вообще бенчмарк для сети и диска, а никак не для процессора. Упереться в процессор на этой задаче нужно ещё постараться.
sr. member
Activity: 1316
Merit: 420
KTO EC/\U HUKTO?
Примерно в 100% случаев, многопоточность делает приложению только хуже.

Почему же, при работе, например, с сетями многопоточность рулит. То ли последовательно обрабатывать 100 подключений за 100 раз (соединение, запрос, запись ответа, рассоединение, следующий узел пока до сотого не дойдём), то ли многопоточно 100 соединений за 1 раз (запуск сразу 100 потоков, соединение, ...., рассоединение, готово!). Причём многопоточность будет выигрывать тем раз больше, чем больше количество подключений нужно обработать. А если учесть затупы связи, то это уже не преимущество даже, а необходимость.
Всё описанное не имеет никакого отношения к многопоточности и прекрасно работает в одном потоке.
Если кому-то из программистов для такого необходима многопоточность, то ему показана хирургическая коррекция расположения рук.

[_IMG]https://img.techpowerup.org/200214/8579688.jpg[/img]

100 узлов. Допустим, на соединение с узлом и скачивание тратится 15 минут. В одном потоке обработать 100 узлов займёт 25 часов. В 100 потоках 15 минут.

Или 200 узлов. В данном случае в одном потоке это будет 50 часов, в 200 потоках... ты не поверишь... снова 15 минут.

100-200 параллельных соединений без проблем обрабатываются в одном потоке. Зачем тебе отдельный поток для каждого соединения?
legendary
Activity: 2044
Merit: 1231
Примерно в 100% случаев, многопоточность делает приложению только хуже.

Почему же, при работе, например, с сетями многопоточность рулит. То ли последовательно обрабатывать 100 подключений за 100 раз (соединение, запрос, запись ответа, рассоединение, следующий узел пока до сотого не дойдём), то ли многопоточно 100 соединений за 1 раз (запуск сразу 100 потоков, соединение, ...., рассоединение, готово!). Причём многопоточность будет выигрывать тем раз больше, чем больше количество подключений нужно обработать. А если учесть затупы связи, то это уже не преимущество даже, а необходимость.
Всё описанное не имеет никакого отношения к многопоточности и прекрасно работает в одном потоке.
Если кому-то из программистов для такого необходима многопоточность, то ему показана хирургическая коррекция расположения рук.

[_IMG]https://img.techpowerup.org/200214/8579688.jpg[/img]

100 узлов. Допустим, на соединение с узлом и скачивание тратится 15 минут. В одном потоке обработать 100 узлов займёт 25 часов. В 100 потоках 15 минут.

Или 200 узлов. В данном случае в одном потоке это будет 50 часов, в 200 потоках... ты не поверишь... снова 15 минут.
kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange
Почему же, при работе, например, с сетями многопоточность рулит. То ли последовательно обрабатывать 100 подключений за 100 раз (соединение, запрос, запись ответа, рассоединение, следующий узел пока до сотого не дойдём), то ли многопоточно 100 соединений за 1 раз (запуск сразу 100 потоков, соединение, ...., рассоединение, готово!). Причём многопоточность будет выигрывать тем раз больше, чем больше количество подключений нужно обработать. А если учесть затупы связи, то это уже не преимущество даже, а необходимость.

Как я уже пару раз тут говорил: сервер это одна из очень немногих специфичных задач, которая легко распараллеливается. В большинстве реальных задач распараллеливание невозможно.
Конкретно касаемо сервера, как я уже тут упоминал, имеет практический смысл делать число потоков не более чем есть ядер в процессоре. Если количество потоков будет больше, то параллельность будет сугубо мнимой. Чудес не бывает.
Все это касается случая когда поток это поток, а не процесс. Процессы можно раскидать не только по ядрам одного процессора, но и по нескольким отдельным процессорам и/или по нескольким отдельным машинам.
legendary
Activity: 3108
Merit: 1359
Примерно в 100% случаев, многопоточность делает приложению только хуже.

Почему же, при работе, например, с сетями многопоточность рулит. То ли последовательно обрабатывать 100 подключений за 100 раз (соединение, запрос, запись ответа, рассоединение, следующий узел пока до сотого не дойдём), то ли многопоточно 100 соединений за 1 раз (запуск сразу 100 потоков, соединение, ...., рассоединение, готово!). Причём многопоточность будет выигрывать тем раз больше, чем больше количество подключений нужно обработать. А если учесть затупы связи, то это уже не преимущество даже, а необходимость.
Всё описанное не имеет никакого отношения к многопоточности и прекрасно работает в одном потоке.
Если кому-то из программистов для такого необходима многопоточность, то ему показана хирургическая коррекция расположения рук.

legendary
Activity: 2044
Merit: 1231
Примерно в 100% случаев, многопоточность делает приложению только хуже.

Почему же, при работе, например, с сетями многопоточность рулит. То ли последовательно обрабатывать 100 подключений за 100 раз (соединение, запрос, запись ответа, рассоединение, следующий узел пока до сотого не дойдём), то ли многопоточно 100 соединений за 1 раз (запуск сразу 100 потоков, соединение, ...., рассоединение, готово!). Причём многопоточность будет выигрывать тем раз больше, чем больше количество подключений нужно обработать. А если учесть затупы связи, то это уже не преимущество даже, а необходимость.
legendary
Activity: 3108
Merit: 1359
К счастью у меня нет готового примера, хотя... Можно глянуть на разработчиков апача наверное.
Разработчики Microsoft IIS, и даже получилось неплохо. Правда, там в центре вселенной драйвер http.sys, который в режиме ядра работает, т.е. вообще вся работа вебсервера с сокетами и большая часть i/o обходятся без переключений контекста, что немного жульничество.

Решили вопрос в лоб, так сказать.

P.S. Результаты не сферические, говорю как тот кто с IIS и nginx достаточно большое время просовокуплялся.
kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange
один рабочий процесс nginx обслуживает множество подключений одновременно,
Правильно

а в апаче только одно,
Правильно

и если nginx будет использовать классические потоки (то же самое что threads в винде), с точки зрения производительности не изменится абсолютно ничего

Если нам договориться между собой тут, что потоки и процессы это одно и тоже, то для nginx очевидно ничего не поменяется ни в плане производительности, ни в каком другом плане. С чего бы вдруг?
Что-то поменяется если вы вместо асинхронной однопоточной работы начнете внутри процесса nginx плодить потоки для каждого сокета как это сделано в апаче. Вот тогда и увидите ад как он есть!

Остался еще один шаг: что по вашему делает "мастер процесс"?
Если говорить про сеть, то раньше он висел на приеме входящих соединений, с появлением опции reuseport это тоже переехало в worker-ы, так что теперь мастер просто читает конфиг и управляет worker-ами.
Ну вот мы еще раз убеждаетмся, что "потоки" в nginx это никакие не потоки, а самые настоящие процессы. Каждый "поток" это отдельный самостоятельный экземпляр сервера. Вся "многопоточность" нгинкса заключается в клонировании единственного однопоточного процесса.
То есть если на примере стартового поста темы, то автору многопоточность в стиле nginx можно организовать очень просто: достаточно сказать слушающему сокету "O_REUSEPORT" и запустить несколько экземпляров демона коина.

Можно... Если хотите чтобы все встало колом из-за ваших потоков и опросов
Это с каких пор epoll колом встает из-за его вызова из нескольких потоков? Может тесты какие подтверждающие есть? Smiley Это у kqueue такие проблемы есть и там рекомендуют использовать несколько независимых экземпляров для цикла сообщений, а у epoll и виндового iocp всегда все ок с этим было.
Мы обсуждаем сферического коня какого-то. Давайте ваш многопоточный сервер уже посмотрим.

Но ничто не мешает асинхронному приложению быть многопоточным

Если договориться, что потоки и процессы это одно и тоже, то конечно.
newbie
Activity: 28
Merit: 6
Не совсем верно. Термин масштабирование означает увеличение производительности при добавлении ресурсов (обычно аппаратных, но не всегда).
Да, я просто частный случай упомянул, и то не совсем корректно, увеличение тактовой частоты (т.е. обычный разгон) ведь тоже увеличение мощности железа Smiley

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

ЗЫ: я тут на досуге документацию почитал, все таки я спрашивал про многопоточность в нгинкс, а вы переводите стрелки на процессы. Надеюсь вы в курсе, что потоки и процессы это разные вещи?
Это зависит от конкретной ОС, в винде это принципиально разные объекты, потоки одного процесса имеют только свой собственный контекст исполнения (регистры, стек, TLS и все что туда входит), все потоки одного процесса имеют общее адресное пространство, файловые дескрипторы, и.т.д.
В линуксе нет такой четкой границы между потоком и процессом, например 2 таких объекта могут иметь разные идентификаторы (тогда падение одного из них не затронет остальные), но работать в одном адресном пространстве, а может быть наоборот (см. параметры системного вызова clone).
Рабочие процессы в nginx в линуксе имеют отдельные идентификаторы, но предполагаю, что они имеют общее пространство файловых дескрипторов (иначе не очень понимаю, как реализивать опцию reuseport). Теперь по сути, вы называете nginx однопоточным и асинхронным, а апач синхронным многопоточным, реально же их рабочие процессы по сути одни и теже сущности в ОС (пусть будут процессы), только один рабочий процесс nginx обслуживает множество подключений одновременно, а в апаче только одно, и если nginx будет использовать классические потоки (то же самое что threads в винде), с точки зрения производительности не изменится абсолютно ничего, стабильность только пострадает.

По умолчанию поддержка многопоточности выключена (http://nginx.org/ru/docs/http/ngx_http_core_module.html#aio)
Это уже совсем другая настройка, так включается пул потоков для запуска синхронных (блокирющих) операций без блокировки цикла сообщений. В зависимости от типа нагрузки опция может поднять производительность на тысячи процентов, а может и ничего не дать.

Остался еще один шаг: что по вашему делает "мастер процесс"?
Если говорить про сеть, то раньше он висел на приеме входящих соединений, с появлением опции reuseport это тоже переехало в worker-ы, так что теперь мастер просто читает конфиг и управляет worker-ами.

Можно... Если хотите чтобы все встало колом из-за ваших потоков и опросов
Это с каких пор epoll колом встает из-за его вызова из нескольких потоков? Может тесты какие подтверждающие есть? Smiley Это у kqueue такие проблемы есть и там рекомендуют использовать несколько независимых экземпляров для цикла сообщений, а у epoll и виндового iocp всегда все ок с этим было.

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

---

Что-то перестал понимать что мы тут обсуждаем, поэтому хочу вернуться к мысли, которую хотел донести еще в первом сообщении, многопоточность и асинхронности никак не связаны между собой, первое про количество одновременно исполняемых потоков, второе про архитектуру, и, естесственно, асинхронные приложения более эффективно используют ресурсы. Но ничто не мешает асинхронному приложению быть многопоточным - запускать цикл сообщений одновременно в нескольких потоках, использовать отдельные потоки для тяжелых вычислений, или комбинировать все это, получая прирост производительности.
legendary
Activity: 2618
Merit: 2304
На одном ядре приложение захлебывается. В настоящее время самый оптимальный пусть масштабирования - это многопоточность (что бы потом можно было масштабировать производительность путем наращивания процессорных мощностей). Но столкнулись с проблемой при переходе на многопоточный режим ASIO.

Полагаю, что разбор Вашего кода новым программистом, пусть даже "скиллованным" в области имплементации "Boost.ASIO", займёт ещё больше времени. Скорее всего, для перевода приложения на многопоточную архитектуру потребуется переписать всю Вашу программу на C++ с добавлением нового объекта "boost::asio::io_context io".
Code:
#include

Честно говоря, не знаю, какие могут быть проблемы с реализацией многопоточности конкретно в Вашем случае. Документация "Boost.ASIO", описывающая использование таймеров синхронно и асинхронно, а также работу хэндлеров, в целом, ясная и понятная:
https://www.boost.org/doc/libs/1_72_0/doc/html/boost_asio/tutorial.html
kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange
Сам термин масштабирование означает увеличение производительности при увеличении мощности железа Smiley

Ну тогда сортировка пузырьком тоже масштабируется...
Я вообще тогда не знаю - что не масштабируется  Grin

Но вы же пишете
nginx запускает один мастер процесс и N рабочих процессов (параметр worker_processes в конфиге), каждый из которых мониторит события на своем наборе сокетов, подключения между рабочими процессами распределяются равномерно. Да, можно сделать 1 рабочий процесс и задействовать одно ядро, а можно выставить их по количеству ядер и задействовать CPU полностью, получив при этом почти линейное масштабирование по количеству ядер, что тут может быть не понятного? Smiley

Итак зафиксируем:
1. Один поток процесс работает сразу с большим набором сокетов.
2. Можно сделать несколько потоков процессов, которые никак друг с другом не взаимодействуют. Каждый поток процесс работает независимо со своим набором сокетов.

ЗЫ: я тут на досуге документацию почитал, все таки я спрашивал про многопоточность в нгинкс, а вы переводите стрелки на процессы. Надеюсь вы в курсе, что потоки и процессы это разные вещи?

По умолчанию поддержка многопоточности выключена

Остался еще один шаг: что по вашему делает "мастер процесс"?

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

Можно... Если хотите чтобы все встало колом из-за ваших потоков и опросов Grin

Хотелось бы увидеть на примере, как lock-free контейнеры приводят к однопоточному приложению, если они предназначены для доступа на чтение/запись из нескольких потоков Smiley

Хотите увидеть пример программиста, который решил сделать многопоточное приложение потому что так проще и якобы эффективнее, а в итоге сломал себе весь мозг костылями из нагромождений локфри структур и получил через год то же самое, что другой программист сделал в одном потоке за неделю?
К счастью у меня нет готового примера, хотя... Можно глянуть на разработчиков апача наверное.
sr. member
Activity: 1316
Merit: 420
KTO EC/\U HUKTO?
Масштабироваться будет, но скорости это не прибавит, а скорее наоборот... Вот такая загогулина ))
Это вообще как  Grin
Сам термин масштабирование означает увеличение производительности при увеличении мощности железа Smiley

Не совсем верно. Термин масштабирование означает увеличение производительности при добавлении ресурсов (обычно аппаратных, но не всегда).
newbie
Activity: 28
Merit: 6
Масштабироваться будет, но скорости это не прибавит, а скорее наоборот... Вот такая загогулина ))
Это вообще как  Grin
Сам термин масштабирование означает увеличение производительности при увеличении мощности железа Smiley

Попробуйте ответить на следующий вопрос: если нгинкс может использовать для работы всего один поток, то как он это делает?
Когда поймете ответ, тогда подумайте над следующим вопросом: что делают дополнительные потоки нгинкса?
nginx запускает один мастер процесс и N рабочих процессов (параметр worker_processes в конфиге), каждый из которых мониторит события на своем наборе сокетов, подключения между рабочими процессами распределяются равномерно. Да, можно сделать 1 рабочий процесс и задействовать одно ядро, а можно выставить их по количеству ядер и задействовать CPU полностью, получив при этом почти линейное масштабирование по количеству ядер, что тут может быть не понятного? Smiley
А еще можно использовать один файловый дескриптор epoll для всех подключений и опрашивать его из нескольких потоков, так получим более равномерное распределение нагрузки по потокам.

На винде нет ни poll ни тем более epoll. Последний и не на всех линуксах есть. Так что libevent использует то, что есть в системе: select в винде, а в других - по обстоятельствам...
Я же написал про винду и iocp, разве нет?
Poll есть на винде по немного другим названием WSAPoll https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsapoll (начиная с висты)
libevent использует select, в винде  потому что использовать iocp не выйдет, другая архитектура https://en.wikipedia.org/wiki/Proactor_pattern, тут, думаю, тоже должно быть понятно.
boost asio может задействовать iocp, но он сам по себе тяжеловесный и для high load не очень рекомендуется.

И мы вернемся к асинхронному однопоточному приложению ))
Хотелось бы увидеть на примере, как lock-free контейнеры приводят к однопоточному приложению, если они предназначены для доступа на чтение/запись из нескольких потоков Smiley
kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange

Не совсем так, внутри libevent использует epoll на Linux и kqueue на MacOS X/FreeBSD, отличия от select есть, и довольно заметные.
При использовании select или poll на вход этой функции передается список файловых дескрипторов, после того, как функция возвращает управление, вызывающий обязан в цикле перебрать все эти дескрипторы и проверить их состояние.

На винде нет ни poll ни тем более epoll. Последний и не на всех линуксах есть. Так что libevent использует то, что есть в системе: select в винде, а в других - по обстоятельствам...

Nginx использует фиксированное количество потоков (но никак не один, если это специально не указать в конфиге)

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

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

Масштабироваться будет, но скорости это не прибавит, а скорее наоборот... Вот такая загогулина ))

Если есть высокие требования к производительности, то можно внедрить lock-free контейнеры...

И мы вернемся к асинхронному однопоточному приложению ))
newbie
Activity: 28
Merit: 6
libevent это библиотека которая внутри использует тот же select или poll. Библиотека сама по себе не делает код синхронным или асинхронным, все зависит от того, кто эту библиотеку и как применяет.
Не совсем так, внутри libevent использует epoll на Linux и kqueue на MacOS X/FreeBSD, отличия от select есть, и довольно заметные.
При использовании select или poll на вход этой функции передается список файловых дескрипторов, после того, как функция возвращает управление, вызывающий обязан в цикле перебрать все эти дескрипторы и проверить их состояние. Т.е. если к серверу подключены 10000 клиентов, то в ответ на приходящие данные хотя бы от одного из 10000 клиентов сервер будет проверять состояние всех 10000 подключений, алгоритмическая сложность O(n). epoll и kqueue возвращают список именно тех файловых дескрипторов, состояние которых изменилось, получаем алг.сложность O(1).
Кроме того, между select и poll есть различия, select использует номер файлового дескриптора как индекс в массиве, и если у тебя открыто более 1024 файлов (даже не сокетов, просто обычных файлов достаточно), то приложение, использующее select просто упадет.
Также надо упомянуть и винду, ее API ввода/вывода использует "порты завершения" (IOCP и чуть более оптимизированную реализация с названием RIO начиная с 8.1), поэтому libevent вынужден использовать select под виндой и не может эффективно работать.. nginx это тоже касается Smiley

Однопоточный это и есть асинхронный в моем понимании.
Ну вот опять Smiley

Есть два подхода к написанию сервера, условно "как нгинкс" и "как апач".
Апач создает новый поток на каждое подключение и захлебывается, когда количество подключений приближается к 1000 или еще раньше. Nginx использует фиксированное количество потоков (но никак не один, если это специально не указать в конфиге), которые работают в цикле сообщений, используя наиболее эффективный механизм для ОС (см. про epoll и kqueue выше).

На практике, чтобы организовать межпоточное взаимодействие, придется расчехлять блокировки вроде мютексов и в результате код обретет кучу ненужных костылей, а производительность не улучшится, а скорее всего станет хуже по сравнению с асинхронным, однопоточным подходом..
Если блокироваться на мьютексе по приходу данных, как делает Bitcoin Core (да и наверное все остальные клиенты Smiley ) и отпускать блокировку по окончания обработки этих данных, то, безусловно, да Smiley А если делать по уму, и синхронизировать только передачу данных между потоками, то приложение будет масштабироваться. Если есть высокие требования к производительности, то можно внедрить lock-free контейнеры... только убедиться, что в данном конкретном случае они дают выигрыш производительности Smiley
kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange

Код для работы с сетью в bitcoin core вполне себе асинхронный и всегда им был (раньше использовал системный вызов select, сейчас вроде как libevent, хотя на 100% не уверен), но оптимизированным его назвать сложно, т.к. он при обработке сообщения захватывает мьютекс cs_main, т.е. фактически однопоточный.


libevent это библиотека которая внутри использует тот же select или poll. Библиотека сама по себе не делает код синхронным или асинхронным, все зависит от того, кто эту библиотеку и как применяет.

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

Есть два подхода к написанию сервера, условно "как нгинкс" и "как апач".
Первый это когда после выхода из select (или poll) для каждого сокета создается отдельный контекст в главном потоке, а второй - когда каждый сокет отправляется в отдельный поток. Вот этот второй способ, он на первый взгляд кажется простым и естественным, но это только на первый взгляд. На практике, чтобы организовать межпоточное взаимодействие, придется расчехлять блокировки вроде мютексов и в результате код обретет кучу ненужных костылей, а производительность не улучшится, а скорее всего станет хуже по сравнению с асинхронным, однопоточным подходом.
newbie
Activity: 28
Merit: 6
Раз уж мы тут на криптофоруме, то можно порассуждать какая архитектура у биткоин коры... Так вот, если быть откровенном, то глядя на код который отвечает за клиент-серверное взаимодействие в коре, складывается устойчивое ощущение что сетевая архитектура у коры написана левой пяткой каким-то школьником, который первый раз в жизни вообще сетевое приложение пишет! Это ощущение складывается именно из-за кривой даже для 2007 года (когда Сатоши по его словам начал работать над кодом) многопоточности.
Код для работы с сетью в bitcoin core вполне себе асинхронный и всегда им был (раньше использовал системный вызов select, сейчас вроде как libevent, хотя на 100% не уверен), но оптимизированным его назвать сложно, т.к. он при обработке сообщения захватывает мьютекс cs_main, т.е. фактически однопоточный.
Quote from: Balthazar
Однако же, ничто не мешает реализовать асинхронное многопоточное приложение, если это действительно нужно. Главное - это иметь в виду, что оно не должно реализовываться тупым дублированием event loop на всех ядрах
Дублирование event loop на всех ядрах не всегда хорошо, особенно если ядер много (16 и больше), а если их, к примеру, 4, то вполне нормально запустить 4 потока с циклом сообщений, и в них же выполнять обработку этих сообщений.
legendary
Activity: 3108
Merit: 1359
Сильно сомневаюсь, что Сатоши - один человек, и вполне вероятно что Сатоши на форуме не имел отношения к непосредственно реализации программы, как таковой, он скорее руководитель проекта и его персонификация, если так можно сказать.

Биткойн клиент ещё при Гэвине был капитальным говнокодом, от изучения которого хотелось пустить себе 9 мм в лоб. Чего стоит синхронный броадкаст блоков, к примеру... Анонсируем блок одной ноде, получаем ответ, затем шлем блок если запросили. Потом шлем анонс следующей ноде, получаем ответ, затем шлем блок уже ей, если запросили. И так для каждого из подключенных пиров, последовательно, после чего каждый пир проделает этот же процесс уже для своих пиров. А то, что обмен данными с одним пиром никак не помешает обменяться этими же данными с другим пиром, автору в голову почему-то не пришло.



Радует, что всё это уже давно переписано и забыто.


Возвращаясь же к теме, многопоточность и асинхронность - это как теплое и мягкое, глупо сравнивать многопоточное синхронное приложение с асинхронным однопоточным. Грамотно написанное асинхронное приложение, если оно не использует блокирующие операции, действительно может работать настолько быстро, что не будет никакого смысла делать его многопоточную реализацию. Взять тот же redis, к примеру, производительность которого почти всегда ограничивается реализацией TCP/IP протокола.

Однако же, ничто не мешает реализовать асинхронное многопоточное приложение, если это действительно нужно. Главное - это иметь в виду, что оно не должно реализовываться тупым дублированием event loop на всех ядрах, потому что такой подход просто добавит глюков и геморроя, но проблему с производительностью никак не решит. Цикл не должен блокироваться в любом случае, а тяжелые задачи нужно выносить в отдельные потоки или процессы, обмен данными с которыми нужно реализовывать сообщениями или иным механизмом, который должен быть неблокирующим.
kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange
Та же упомянутая библиотека boost.asio позволяет запускать цикл обработки сообщений о завершенных операциях отправки/приема данных, ничто не мешает запустить таких потоков по количеству ядер, и обрабатывать эти сообщения одновременно.

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

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

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

Раз уж мы тут на криптофоруме, то можно порассуждать какая архитектура у биткоин коры... Так вот, если быть откровенном, то глядя на код который отвечает за клиент-серверное взаимодействие в коре, складывается устойчивое ощущение что сетевая архитектура у коры написана левой пяткой каким-то школьником, который первый раз в жизни вообще сетевое приложение пишет! Это ощущение складывается именно из-за кривой даже для 2007 года (когда Сатоши по его словам начал работать над кодом) многопоточности.
Хотя если допустить, что Сатоши это Ник Сабо, то ничего удивительного - он ни разу не кодер, а скорее научный руководитель и вдохновитель ))
newbie
Activity: 28
Merit: 6
Что, прямо так мало задач распараллеливается? Smiley
Если посмотреть тест многоядерных процессоров (например 32-ядерного AMD) https://www.anandtech.com/show/15044/the-amd-ryzen-threadripper-3960x-and-3970x-review-24-and-32-cores-on-7nm то видно, что не так и мало бенчмарков, где он обгоняет обычные 6-8 ядерники с большим отрывом.
Серверы распарралеливаются почти все (не только веб), что мешает одновременно обрабатывать запросы от нескольких клиентов?
И причем тут вообще асинхронность? Та же упомянутая библиотека boost.asio позволяет запускать цикл обработки сообщений о завершенных операциях отправки/приема данных, ничто не мешает запустить таких потоков по количеству ядер, и обрабатывать эти сообщения одновременно.
kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange
но в контексте C++ написанное выглядит полным бредом Smiley Вот как можно противопоставлять многопоточность и асинхронность? Smiley Возьмем nginx, он асинхронный и многопоточный, в его конфиге указывается число рабочих потоков, и во многих случаях производительность линейно масштабируется в зависимости от кол-ва потоков.

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

Если серьезно, то многопоточность может давать какие-то преимущества за счет возможности распараллеливания некоторых (очень узкого круга) задач на многоядерных процессорах. Потому что под каждый поток операционная система может выделять отдельное ядро процессора.
Повторяю еще раз: очень мало есть реальных задач, где многопоточность может дать выигрыш в производительности. Веб сервер - как раз одна из таких немногих задач. Тот же нгинкс лучше настроить на работу в стольких потоках, сколько ядер у процессора где этот нгингкс крутится...
В большинстве же других задач, многопоточность только убивает производительность за счет всех этих мутексов-шмутексов и прочих семафоров...
newbie
Activity: 28
Merit: 6
Если что-то нужно распараллелить, то делить надо не на потоки, а на процессы. А параллелить внутри процесса.., ну я даже не могу представить себе для чего это может реально понадобиться... Разве что для какого-то специфичного стресс-теста многоядерного процессора.
Quote
Смотрите лучше в сторону оптимизации архитектуры. Многопоточность придумывали в те времена, когда еще были споры что лучше: многопоточность или асинхронность. К настоящему времени опытным путем давно установлено, что асинхронность всегда лучше.
Это смотря какой язык используется, если Python то из-за его особенностей это все правда, но в контексте C++ написанное выглядит полным бредом Smiley Вот как можно противопоставлять многопоточность и асинхронность? Smiley Возьмем nginx, он асинхронный и многопоточный, в его конфиге указывается число рабочих потоков, и во многих случаях производительность линейно масштабируется в зависимости от кол-ва потоков.
kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange
Я вам даю 100% гарантию, что при переходе на многопоточный режим, ваше приложение начнет "захлебываться" еще сильнее.
Смотрите лучше в сторону оптимизации архитектуры. Многопоточность придумывали в те времена, когда еще были споры что лучше: многопоточность или асинхронность. К настоящему времени опытным путем давно установлено, что асинхронность всегда лучше.
Разрабы биткоина кстати уже несколько лет пытаются окончательно выпилить boost из исходников, так что стоит задуматься: а вам оно точно надо?
jr. member
Activity: 320
Merit: 2
Примерно в 100% случаев, многопоточность делает приложению только хуже.
В данном случае, это не блаж - это необходимость.

Откуда такая уверенность?
И чем обусловлен выбор Boost.ASIO вместо например libev?
Если что-то нужно распараллелить, то делить надо не на потоки, а на процессы. А параллелить внутри процесса.., ну я даже не могу представить себе для чего это может реально понадобиться... Разве что для какого-то специфичного стресс-теста многоядерного процессора.

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

Про стресс-тест вообще не понял. ИМХО - неуместный сарказм. Многопоточность только для стресс-тестов придумывали что-ли?
kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange
Примерно в 100% случаев, многопоточность делает приложению только хуже.
В данном случае, это не блаж - это необходимость.

Откуда такая уверенность?
И чем обусловлен выбор Boost.ASIO вместо например libev?
Если что-то нужно распараллелить, то делить надо не на потоки, а на процессы. А параллелить внутри процесса.., ну я даже не могу представить себе для чего это может реально понадобиться... Разве что для какого-то специфичного стресс-теста многоядерного процессора.
jr. member
Activity: 320
Merit: 2
Примерно в 100% случаев, многопоточность делает приложению только хуже.
В данном случае, это не блаж - это необходимость.
kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange
Примерно в 100% случаев, многопоточность делает приложению только хуже.
jr. member
Activity: 320
Merit: 2
Многопоточное приложение на C++ с использованием Boost.ASIO.

Разыскивается опытный разработчик в указанной области.
Задача: перевести приложение с однопоточного режима во многопоточный.
Jump to: