В глаз луч может попасть и, например, отразившись от обоев. Чтобы выжеть нафиг сетчатку отраженного луча при мощности достаточной для зажигания вполне хватит и отраженного луча.
Локинг он ведь и без потоков есть (мастер процесс, несколько рабочих процессов и разделяемая память) :) Собственно на низком уровне это инструкции xchg или cmpxchg (ну и lock по необходимости) в зависимости от реализации — для того чтобы их использовать не обязательно нужно быть потоком, а для процессов ведь примитивы также необходимы.
Про методику все правильно поняли. В сервере, что мы писали для себя это реализовано уже относительно давно (более года), хотя и метод передачи инфы дочерним процессам незначительно отличается — здесь есть варианты.
Только я не подразумевал, что нужны будут потоки — запутался в терминологии =)
Если кратко, то сейчас в nginx все события касающиеся полной жизни tcp-соединения валятся в общую kqueue (epoll и т.п. в зависимости от системы). Из нее мастер процесс эти события вытягивает, преобрабатывает и складывает во внутреннюю общую очередь, из которой уже дочерние процессы используя локинг тянут себе задания и обрабатывают эти события.
Таким образом сейчас обработка последовательных событий одного соединения гуляет по разным процессам (ядрам и т.п.). Плюс используется локинг. Плюс, собственно дочерние процессы вэйкапятся также через локинг, когда заданий нет, они успевают поспать.
Предложение в том, чтобы сразу после акцепта и получения нового fd — этот фд не добавлять даже в общую kqueue, а передавать их дочерним процессам и использовать индивидуальную kqueue для каждого процесса — тогда необходимость локинга для работы с остальными событиями отпадает — т.к. они всегда будут привязаны к одному и тому же процессу.
В начале статьи есть концептуальная ошибка где про «работает так быстро». Способен обслуживать большое число соединений и при этом шевелиться — да, но обычно raw-производительность именно по отдаче данных и выюзыванию канала в плешку у обычных серверов бывает выше, чем у асинхронных.
И еще я, как раработчик, посвятивший достаточно много времени (пара лет) асинхронщине, поспорил бы с некоторыми пунктами.
По ходу работы над поставленными задачами я, естественно, изучал чужой опыт и устройство nginx и алгоритм его работы знаю на уровне исходных кодов. Не знаю, как в самых свежих версиях (какое-то время назад я перестал следить за изменениями в них), но могу с уверенностью сказать что следующие два утвержения про «количество переключений между контекстами выполнения минимизируется» и «практически нет необходимости в синхронизации процессов или потоков» — неверны. И, если у вас достаточно нагруженные сайты, то вы можете заметить, как nginx грузит процессор — а ведь эту нагрузку можно уменьшить убрав то лишнее, чего в нем по ошибочному, с моей нескромной точки зрения, мнению не существует :)
Если быть более точным, то они верны только в том случае, когда nginx работает одним процессом, а когда дело доходит до SMP, то в нем используется и локинг и есть переключения контекста, которых Игорь, в принципе, может избежать, но пока он почему-то этим не занимался.
Да, он выравнивает размеры некоторых структур данных под пайплайн процессора, чтобы обойти проблема с обнулением кэша процессора, но это не главное.
nginx устроен так, что в нем сбор и предварительная обработка событий осуществляются одним потоком через одну очередь событийного механизма (kqueue и т.п.), когда же события преобработаны — они скадываются в общую очередь, которую, конкурируя между собой (в неопределенном порядке) с использованием локинга растаскивают рабочие процессы.
Таким образом установка соединения, прием/передача разных порций данных и последующий дисконнект с лингером для одного tcp-соединения (сессии) по очереди могут обрабатываться разными рабочими процессами. Таким образом данные из контекста одной серссии кидаются между разными cpu, что, в принципе, можно понять с какой-точки зрения. И пускай trylock не приводит к блокировке потока, зато он есть cpu.
Теперь, чтобы не быть совсем голословным о том, как это можно обойти.
Например, можно сделать так, чтобы сервер использовал не единственную системную очередь (kqueue), а отдельную для каждого процесса/потока (и соответственно cpu). При этом пусть слушающий сокет и будет только в одной из них, зато можно новые установленные соединения распределять между потоками не добавляя запросов на нотификацию событий о них в одну общую очередь, а наоборот — раскидывая их по собственным kqueue каждого потока.
Далее уже обработкой всех последующих операций по каждому конкретному соединению будет заниматься только один и тот же процесс/поток потому что нотификации о событиях будут приходить только в его приватную очередь. Естественно, при этом, поскольку только один поток работает с данными соединения, локинг уже не нужен.
Таким образом мы получаем немного другую внутреннюю кухню, где локинга (или другого оверхеда в зависимости от метода распределения задач между потоками) есть немного только на этапе распределения новых соединений между рабочими потоками, но где мы избегаем излишнего локинга при всех последующих операциях в рамках каждого установленного tcp-соединения. Взяв в руки калькулятор любой желающий сможет посчитать — сколько инструкций cpu можно сэкономить не используя локинг на каждом событии, а только на одном на сессию.
Хотя, конечно, и с таким решение можно поспорить и в нем найти недостатки :)
С другой стороны Игоря Сысоева тоже можно понять — ведь 1) «работает не трогай» и 2) нагрузить сервер так, чтобы разница между подобными подходами была сильно заметной сегодня практически невозможно из-за узкого места — производительности и пропускной способности современного сетевого оборудования — через сеть просто нельзя создать такой нагрузки, чтобы правильных софт основанный на асинхронной модели смогу утилизировать CPU на 100% ;-)
от фучерса можно отказаться в любой момент. он ни в коем случае не явлется гарантией того, что клиент что-то там купит. «может быть купит» это правильнее
Молодцы. До этого приходилось к подобным трюкам прибегать разбивая данные на таблицы ручками :) Хотя при использовании других движков типа SQLite все еще руками приходится разбивать.
Совет правильный про вдс. Только покупать имхо практически все равно у кого — мастехост тоже покатит. Главное, чтобы хостер через месяц-другой не канул в лету.
Сжимать не нужно. В данном случае можно еще ускориться используя bin/bucket/postman алгоритмы — тогда в конце слияние фактически не нужно будет делать в том виде, как это делает merge — достаточно будет просто склеить файлы. Если быть особенным извращенцем, то большой файл можно склеить из кусочков минуя чтение и запись — тупо объединив иноды, кластеры (или еще че в зависимости от FS) в один файл напрямую редактируя FS :)
Собственно гря в этом случае, как я понял до алгоритмов не дошло. Просто использовался трюк с (относительно неэффективным) разбиением на более мелкие куски.
А если вникнуть глубже в задачу, то можно специализировать алгоритм на тип данных, что может привести к тому, что в кусках, которые получаются после предварительной псевдо-сортировки, уже можно сравнивать даже не все 32 бита, а только половину, поскольку, например, старшие два байта в куске у всех чисел могут быть одинаковы :) +SMP, GPGPU и т.п. Пространства для оптимизации весьма и весьма много.
По этому порнографическому резюме, меня, кстати пригласили на работу сначала в дочернюю компании IBM-а, а потом и сам IBM. Так что порнография в таком виде не всегда приносит отрицательные результаты. Ведь правда? ;)
Представляю, как быстро ляжет сервак, если данные о голосовании сделать из трех таблиц включая отдельную таблицу для таймстампа на 8 полей. Жесть!
Только я не подразумевал, что нужны будут потоки — запутался в терминологии =)
Если кратко, то сейчас в nginx все события касающиеся полной жизни tcp-соединения валятся в общую kqueue (epoll и т.п. в зависимости от системы). Из нее мастер процесс эти события вытягивает, преобрабатывает и складывает во внутреннюю общую очередь, из которой уже дочерние процессы используя локинг тянут себе задания и обрабатывают эти события.
Таким образом сейчас обработка последовательных событий одного соединения гуляет по разным процессам (ядрам и т.п.). Плюс используется локинг. Плюс, собственно дочерние процессы вэйкапятся также через локинг, когда заданий нет, они успевают поспать.
Предложение в том, чтобы сразу после акцепта и получения нового fd — этот фд не добавлять даже в общую kqueue, а передавать их дочерним процессам и использовать индивидуальную kqueue для каждого процесса — тогда необходимость локинга для работы с остальными событиями отпадает — т.к. они всегда будут привязаны к одному и тому же процессу.
И еще я, как раработчик, посвятивший достаточно много времени (пара лет) асинхронщине, поспорил бы с некоторыми пунктами.
По ходу работы над поставленными задачами я, естественно, изучал чужой опыт и устройство nginx и алгоритм его работы знаю на уровне исходных кодов. Не знаю, как в самых свежих версиях (какое-то время назад я перестал следить за изменениями в них), но могу с уверенностью сказать что следующие два утвержения про «количество переключений между контекстами выполнения минимизируется» и «практически нет необходимости в синхронизации процессов или потоков» — неверны. И, если у вас достаточно нагруженные сайты, то вы можете заметить, как nginx грузит процессор — а ведь эту нагрузку можно уменьшить убрав то лишнее, чего в нем по ошибочному, с моей нескромной точки зрения, мнению не существует :)
Если быть более точным, то они верны только в том случае, когда nginx работает одним процессом, а когда дело доходит до SMP, то в нем используется и локинг и есть переключения контекста, которых Игорь, в принципе, может избежать, но пока он почему-то этим не занимался.
Да, он выравнивает размеры некоторых структур данных под пайплайн процессора, чтобы обойти проблема с обнулением кэша процессора, но это не главное.
nginx устроен так, что в нем сбор и предварительная обработка событий осуществляются одним потоком через одну очередь событийного механизма (kqueue и т.п.), когда же события преобработаны — они скадываются в общую очередь, которую, конкурируя между собой (в неопределенном порядке) с использованием локинга растаскивают рабочие процессы.
Таким образом установка соединения, прием/передача разных порций данных и последующий дисконнект с лингером для одного tcp-соединения (сессии) по очереди могут обрабатываться разными рабочими процессами. Таким образом данные из контекста одной серссии кидаются между разными cpu, что, в принципе, можно понять с какой-точки зрения. И пускай trylock не приводит к блокировке потока, зато он есть cpu.
Теперь, чтобы не быть совсем голословным о том, как это можно обойти.
Например, можно сделать так, чтобы сервер использовал не единственную системную очередь (kqueue), а отдельную для каждого процесса/потока (и соответственно cpu). При этом пусть слушающий сокет и будет только в одной из них, зато можно новые установленные соединения распределять между потоками не добавляя запросов на нотификацию событий о них в одну общую очередь, а наоборот — раскидывая их по собственным kqueue каждого потока.
Далее уже обработкой всех последующих операций по каждому конкретному соединению будет заниматься только один и тот же процесс/поток потому что нотификации о событиях будут приходить только в его приватную очередь. Естественно, при этом, поскольку только один поток работает с данными соединения, локинг уже не нужен.
Таким образом мы получаем немного другую внутреннюю кухню, где локинга (или другого оверхеда в зависимости от метода распределения задач между потоками) есть немного только на этапе распределения новых соединений между рабочими потоками, но где мы избегаем излишнего локинга при всех последующих операциях в рамках каждого установленного tcp-соединения. Взяв в руки калькулятор любой желающий сможет посчитать — сколько инструкций cpu можно сэкономить не используя локинг на каждом событии, а только на одном на сессию.
Хотя, конечно, и с таким решение можно поспорить и в нем найти недостатки :)
С другой стороны Игоря Сысоева тоже можно понять — ведь 1) «работает не трогай» и 2) нагрузить сервер так, чтобы разница между подобными подходами была сильно заметной сегодня практически невозможно из-за узкого места — производительности и пропускной способности современного сетевого оборудования — через сеть просто нельзя создать такой нагрузки, чтобы правильных софт основанный на асинхронной модели смогу утилизировать CPU на 100% ;-)
А так nginx — очень и очень хороший сервер.
Собственно гря в этом случае, как я понял до алгоритмов не дошло. Просто использовался трюк с (относительно неэффективным) разбиением на более мелкие куски.
А если вникнуть глубже в задачу, то можно специализировать алгоритм на тип данных, что может привести к тому, что в кусках, которые получаются после предварительной псевдо-сортировки, уже можно сравнивать даже не все 32 бита, а только половину, поскольку, например, старшие два байта в куске у всех чисел могут быть одинаковы :) +SMP, GPGPU и т.п. Пространства для оптимизации весьма и весьма много.