Здравствуйте, Maxim S. Shatskih, Вы писали:
R>>Единственая возможность сделать что-то нормальное на NUMA и современных многоядерных процессорах — VirtualAllocExNuma() — Windows Vista, Windows Server "Longhorn"
MSS>При чем тут NUMA? мы про SMP, а не про NUMA.
Все современные процессоры *будут* NUMA. Не в том плане, как суперкомпьютеры, а внутри одного процессора. Смотри последние процессоры от AMD. И новые разработки по северным мостам от Intel и AMD.
Здравствуйте, Maxim S. Shatskih, Вы писали:
MSS>Ну что я могу сказать... кто-то занимается разработкой, стыкуя готовые кусочки и обвешивая их скриптами типа Ruby.
MSS>А кто-то разрабатывает сами эти кусочки.
Вы повторили в точности мою мысль -- переход от Win16 к Win32 больше всего затронул тех, кто писал кусочки, из которых остальные стыковали что-то прикладное.
Переход от одноядерных процессоров к многоядерным приведет к тому же самому, т.е. реально заниматься многоядерностью будет совсем небольшой процент разработчиков, а остальные будут обвешивать готовые кусочки какими-то скриптами (и хорошо, если бы это был Ruby). Зато шуму от многоядерности столько, как будто программировать 80 ядер придется каждому второму.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[4]: Многопоточность сегодня - не от хорошей жизни
Здравствуйте, eao197, Вы писали:
E>Вы повторили в точности мою мысль -- переход от Win16 к Win32 больше всего затронул тех, кто писал кусочки, из которых остальные стыковали что-то прикладное. E>Переход от одноядерных процессоров к многоядерным приведет к тому же самому
Это с какой стати? Win16->Win32 это те же самые принципы, те же самые приемы, те же самые архитектурные решения (паттерны). А параллельное программирование — другое во всём.
Здравствуйте, Кодёнок, Вы писали:
Кё>Здравствуйте, eao197, Вы писали:
E>>Вы повторили в точности мою мысль -- переход от Win16 к Win32 больше всего затронул тех, кто писал кусочки, из которых остальные стыковали что-то прикладное. E>>Переход от одноядерных процессоров к многоядерным приведет к тому же самому
Кё>Это с какой стати? Win16->Win32 это те же самые принципы, те же самые приемы, те же самые архитектурные решения (паттерны). А параллельное программирование — другое во всём.
Параллельное программирование существует давно и успешно. OpenMP не сегодня появился, PVM и MPI уже очень давно используются в вычислительных задачах, решаемых на кластерах в сотни и тысячи машин, Erlang находится в промышленном использовании с середины 90-х годов. Web-приложения, в которых запросы обрабатываются тысячами параллельно работающих процессов -- уже давно не диковинка. Так что те, кому параллельное программирование было надо, давно им пользовались. И умели пользоваться, что характерно.
Не важно, насколько параллельное программирование отличается, важно какому проценту разработчиков оно реально нужно. Может быть здесь так же сработает закон потребления пива -- 80% разработчиков потребовуется всего лишь 20% возможностей новых многоядерных процессоров.
Тем более, что для массового потребления всю эту многоядерность завернут в какие-нибудь непрозрачные одежды, типа LINQ или параллельных модификаций STL. Станет в одночасье в C++ной программе std::sort или std::transform распараллеливаться на свободные ядра, а программист этого и не заметит.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
R>А ну если поддержкой многоядерности считать только возможность выполняться на нескольких аппаратных потоках и много локов, а не один. То пожалуйста. Я имел в виду, не теоритетическую возможность выполняться на нескольких аппаратных потоках, а *эффективную* работу.
Мой пойнт был в том, что в Windows NT было умение работать SMP с самого первого релиза, и по этому умению она в течение многих лет опережала (да и опережает поди) Linux и FreeBSD. Вполне возможно, что она и SunOS 5 опережала.
Весь мой пойнт подтвердился.
Что же касается эффективности — ну, не было до Висты вызовов, которые позволяли сделать финальную "полировочную" оптимизацию и выбрать последние 5%. Но базовые вещи — были, и зачастую их хватало.
R>Этот треш из 70-ых годов меня не интересует. Хотя, да, он позволяет работать на нескольких аппаратных потоках.
В огромном количестве случаев спинлок (который, кажется, еще и 60х годов, у Дейтела он описан, а Дейтел вроде как начало 70х) — лучше, чем наворочанные lock-free алгоритмы.
Причина проста. Практически любой lock-free — это 2 interlocked операции. Спинлок — они же. Есть, кстати, еще и queued spinlock, чуть умнее, и ничто не мешало его сделать в своем коде и до XP. Это по большому счету библиотечный объект "в себе", от ОС ему надо только KeRaiseIrql.
Недостаток спинлока только в пустопорожней растрате процессора в случае возникновения конкуренции. Недостаток lock-free — в некоторых случаях придется делать значительно больше, чем 2 interlocked операции. Lock-free не универсален, нет единого способа решения всех задач в стиле lock-free, и там придется пользоваться тем же спинлоком или мутексом.
FAST_MUTEX вырождается в спинлок при отсутствии конкуренции (что на "маленьком" локе, защищающем коротенький кусок кода, реально). Опять же всего 2 interlocked операции.
R>Все современные процессоры *будут* NUMA. Не в том плане, как суперкомпьютеры, а внутри одного процессора. Смотри последние процессоры от AMD. И новые разработки по северным мостам от Intel и AMD.
Верю. Только мое утверждение "SMP сделана в Windows полноценно с самого начала" — таки истинно.
Здравствуйте, Maxim S. Shatskih, Вы писали:
R>>А ну если поддержкой многоядерности считать только возможность выполняться на нескольких аппаратных потоках и много локов, а не один. То пожалуйста. Я имел в виду, не теоритетическую возможность выполняться на нескольких аппаратных потоках, а *эффективную* работу.
MSS>Мой пойнт был в том, что в Windows NT было умение работать SMP с самого первого релиза, и по этому умению она в течение многих лет опережала (да и опережает поди) Linux и FreeBSD. Вполне возможно, что она и SunOS 5 опережала.
MSS>Весь мой пойнт подтвердился.
Меня не очень интересует "номинальная возможность работать на SMP". Возможно, что я слишком молодой, что бы удивляться и радоваться таким вещам. Для меня такая номинальная поддержка кажется очевидной. Видимо поэтому мы и говорим о разном...
MSS>Что же касается эффективности — ну, не было до Висты вызовов, которые позволяли сделать финальную "полировочную" оптимизацию и выбрать последние 5%. Но базовые вещи — были, и зачастую их хватало.
Распределение памяти по узлам NUMA и эффективные примитивы синхронизации — 5%... если в программе не устранены боттлнеки в других местах, то и 5% может не набежать...
R>>Этот треш из 70-ых годов меня не интересует. Хотя, да, он позволяет работать на нескольких аппаратных потоках.
MSS>В огромном количестве случаев спинлок (который, кажется, еще и 60х годов, у Дейтела он описан, а Дейтел вроде как начало 70х) — лучше, чем наворочанные lock-free алгоритмы.
MSS>Причина проста. Практически любой lock-free — это 2 interlocked операции.
Ты говоришь о том, чего не знаешь.
Вопрос не в том, какое максимальное кол-во interlocked операций можно запихать в алгоритм. Я думаю, что достаточно легко написать lock-free алгоритм и с 100 interlocked операциями. Вопрос в том, какое *минимальное* кол-во interlocked операций может быть в том или ином алгоритме.
А минимальное кол-во interlocked операций обычно бывает от 2 до 0 (прописью: ноль). Ещё раз повторю: ноль interlocked операций в примитиве синхронизации.
MSS>Спинлок — они же. Есть, кстати, еще и queued spinlock, чуть умнее, и ничто не мешало его сделать в своем коде и до XP. Это по большому счету библиотечный объект "в себе", от ОС ему надо только KeRaiseIrql.
Если цель использования queued spinlock — уменьшить конкуренцию за кэш, то это не более чем затыкание дыр в кривом примитиве. Класс Wait-free алгоритмов создаёт минимально необходимую нагрузку на кэш и больше уменьшать или затыкать нечего.
Если цель использования queued spinlock — обеспечить честный порядок захвата, то это опять же затыкание дыр в кривом примитиве. Класс Lock-free алгоритмов всегда обеспечивает честный порядок.
MSS>Недостаток спинлока только в пустопорожней растрате процессора в случае возникновения конкуренции. Недостаток lock-free — в некоторых случаях придется делать значительно больше, чем 2 interlocked операции. Lock-free не универсален, нет единого способа решения всех задач в стиле lock-free, и там придется пользоваться тем же спинлоком или мутексом.
Так же как нет единого и универсального способа делать производительные сетевые сервера, организовывать структуру базы данных и т.д. и т.п.
MSS>FAST_MUTEX вырождается в спинлок при отсутствии конкуренции (что на "маленьком" локе, защищающем коротенький кусок кода, реально). Опять же всего 2 interlocked операции.
Если у тебя маленький кусочек кода, то лучше используй спин-мьютекс — это всего 1 interlocked операция. Можно добиться существенного повышения производительности. (и не в коем случае не используй reader-writer мьютекс)
MSS>Так что я не думаю, что эти объекты есть треш.
Здравствуйте, Maxim S. Shatskih, Вы писали:
R>>Все современные процессоры *будут* NUMA. Не в том плане, как суперкомпьютеры, а внутри одного процессора. Смотри последние процессоры от AMD. И новые разработки по северным мостам от Intel и AMD.
MSS>Верю. Только мое утверждение "SMP сделана в Windows полноценно с самого начала" — таки истинно. MSS>Про NUMA на этих машинах тогда и не думал никто.
Я тебя не понял, т.к. ты отвечал на мой пост, который был совсем о другом.
Но тогда становится неверно твоё другое утверждение, что "Для системных программистов никакого перегиба развития нету", т.к. теперь об этих вещах *надо* думать. И только несколько лет назад некоторые разработчики Windows о них задумались.
E>Переход от одноядерных процессоров к многоядерным приведет к тому же самому, т.е. реально заниматься многоядерностью будет совсем небольшой процент разработчиков, а остальные будут обвешивать готовые кусочки какими-то скриптами (и хорошо, если бы это был Ruby). Зато шуму от многоядерности столько, как будто программировать 80 ядер придется каждому второму.
А кто шум-то разводит? только маркетологи Интела.
Системные программисты знали SMP еще в 90ые годы, и для них это не новость.
Здравствуйте, Maxim S. Shatskih, Вы писали:
E>>Переход от одноядерных процессоров к многоядерным приведет к тому же самому, т.е. реально заниматься многоядерностью будет совсем небольшой процент разработчиков, а остальные будут обвешивать готовые кусочки какими-то скриптами (и хорошо, если бы это был Ruby). Зато шуму от многоядерности столько, как будто программировать 80 ядер придется каждому второму.
MSS>А кто шум-то разводит? только маркетологи Интела.
И участники Интернет-форумов. Смотрите, например, сколько людей только в этой теме засветилась. А ведь это и не первая тема про многоядерность только в Философии Программирования на RSDN.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, Maxim S. Shatskih, Вы писали:
R>>Все современные процессоры *будут* NUMA. Не в том плане, как суперкомпьютеры, а внутри одного процессора. Смотри последние процессоры от AMD. И новые разработки по северным мостам от Intel и AMD.
MSS>Верю. Только мое утверждение "SMP сделана в Windows полноценно с самого начала" — таки истинно. MSS>Про NUMA на этих машинах тогда и не думал никто.
За полтора месяца так никто ничего и не ответил и, видимо, уже не ответит. У тебя в профиле написано "Windows DDK MVP", может быть ты можешь пролить какой-то свет на этот вопрос, или посоветовать кого-нибудь, кто может пролить. А то я уж совсем обезнадёжился...
Судя по тому, что ты часто упоминаешь функции, начинающиеся с Ke, ты разбираешься в ядре.
Правильно ли я понимаю, что с портом завершения связана *одна* очередь сообщений, и с этими сообщениями не связан идентификатор процессора, с которого поступило данное сообщение? Т.е. при вызове GetQueuedCompletionStatus() ОС не может автоматически стараться распределять обработку сообщения на те же процессоры, на которых они были сгенерированы?
И в обратную сторону. Связан ли с IRP идентификатор процессора, на котором он был сгенерирован? И старается ли ОС распределять IRP на тот процессор, на котором он был сгенерирован? Там есть IRP.Tail.Overlay.Thread, но я так понимаю, что он для других целей.
Здравствуйте, remark, Вы писали:
MSS>>Причина проста. Практически любой lock-free — это 2 interlocked операции.
R>Ты говоришь о том, чего не знаешь. R>Вопрос не в том, какое максимальное кол-во interlocked операций можно запихать в алгоритм. Я думаю, что достаточно легко написать lock-free алгоритм и с 100 interlocked операциями. Вопрос в том, какое *минимальное* кол-во interlocked операций может быть в том или ином алгоритме. R>А минимальное кол-во interlocked операций обычно бывает от 2 до 0 (прописью: ноль). Ещё раз повторю: ноль interlocked операций в примитиве синхронизации.
Как раз любой мьютекс — "это 2 interlocked операции". Одно из основных отличий lock-free алгоритмов — это то, что есть "непаханное поле" для различных "амортизаций", "оптимизаций" и т.д. lock-based алгоритмы нет смысла оптимизировать каким-либо образом, точнее нет возможности. Т.к. что бы что-либо сделать, надо вначале захватить мьютекс, а раз он уже всё равно захвачен, то далее нет смысла что-либо оптимизировать — надо просто быстрее "доделать работу" и отпускать мьютекс. Это — принципиальная ограниченность подхода.
При использовании lock-free подхода нет никакой "обязательной части", которую надо выполнить. Помимо всего прочего можно делать "что-то" не каждый раз, можно делать "что-то" только когда возведён какой-либо флаг, а что бы проверять флаг не надо никакой синхронизации и т.д. и т.п.
Выскажу несколько косвенных соображений, потому как актуальной информации (скажем, по Висте) о том, как это сделано внутри, у меня нет.
Если у нити не задана affinity, то все это суть бессмыслица. Ее перекинут на другой CPU между практически любыми двумя опкодами (на < DISPATCH_LEVEL, понятно).
Таким образом, любое понятие current CPU в этом случае суть эдакая гейзенберговщина, значение, которое можно померять, но оно через микросекунду может измениться, и потому бессмысленно.
Я вообще не припоминаю в Win32 вызова "получить номер текущего процессора". В ядре он есть, но в ядре и DISPATCH_LEVEL есть, т.е. временный запрет преемптивности на текущем CPU. В Win32 этого нет.
Таким образом, чтобы сделать такую привязку к процессору, нужно в IRPе тащить за собой affinity нити, которая его создала. В принципе можно вместо этого использовать affinity нити напрямую через Tail.Overlay.Thread, но ассоциация, заданная этим полем, рвется в момент помещения IRPа в очередь IOCP при его завершении, т.е. с этого момента Tail.Overlay.Thread есть junk.
Я не помню в IRPе поля affinity. Структура — публичная. Можете посмотреть и сделать вывод о том, есть ли оно там.
Также я не помню ничего связанного с affinity в параметрах создания IOCP или file object.
Похоже, что этого нет. То есть — если вам нужна привязка завершения IRPа к CPU, то либо используйте другие механизмы завершения — OVERLAPPED::hEvent или Read/WriteFileEx и user APC, либо же _создайте по одному IOCP на процессор_ (и, конечно, по одному file object га процессор). Сделать последнее не так и сложно.
R>Правильно ли я понимаю, что с портом завершения связана *одна* очередь сообщений, и с этими сообщениями не связан идентификатор процессора, с которого поступило данное сообщение?
В NT4 точно была одна, дальше не знаю.
В очереди IOCP лежат именно IRPs (зацеплены за Tail.Overlay.ListEntry), а я не припоминаю такого поля (CpuId или Affinity) в IRPе.
R>Т.е. при вызове GetQueuedCompletionStatus() ОС не может автоматически стараться распределять обработку сообщения на те же процессоры, на которых они были сгенерированы?
Если в IRPе нет идентификатора CPU, и также его нет в параметрах создания IOCP — то это, понятно, невозможно.
R>И в обратную сторону. Связан ли с IRP идентификатор процессора, на котором он был сгенерирован?
А где? структура-то публичная. Где он там, в каком поле?
R>И старается ли ОС распределять IRP на тот процессор, на котором он был сгенерирован?
Если нет поля — то, значит, невозможно это.
R>Там есть IRP.Tail.Overlay.Thread, но я так понимаю, что он для других целей.
Ага. Кстати, когда IRP попал в IOCP, оно уже junk — т.е. нет гарантии, что эта нить не умерла сразу после.
Занимайтесь LoveCraftом, а не WarCraftом!
Re[3]: Многопоточность сегодня - не от хорошей жизни
Здравствуйте, Maxim S. Shatskih, Вы писали:
MSS>Выскажу несколько косвенных соображений, потому как актуальной информации (скажем, по Висте) о том, как это сделано внутри, у меня нет.
MSS>Если у нити не задана affinity, то все это суть бессмыслица. Ее перекинут на другой CPU между практически любыми двумя опкодами (на < DISPATCH_LEVEL, понятно).
MSS>Таким образом, любое понятие current CPU в этом случае суть эдакая гейзенберговщина, значение, которое можно померять, но оно через микросекунду может измениться, и потому бессмысленно.
MSS>Я вообще не припоминаю в Win32 вызова "получить номер текущего процессора". В ядре он есть, но в ядре и DISPATCH_LEVEL есть, т.е. временный запрет преемптивности на текущем CPU. В Win32 этого нет.
В Win32 это GetCurrentProcessorNumber(). В Linux это numa_node_id(), либо ещё какой-то вызов в юзер спейс недавно сделали — не помню точно.
Ну в целом, да, это "гейзенберговщина". Но тем не менее используется относительно широко для различных оптимизаций. Например можно структуру данных партицировать по процессорам и защитить таблицей из мьютексов, и блокировать мьютексом всегда ту, которая = GetCurrentProcessorNumber(). В подавляющем большинстве случаев всё будет замечательно — с каждым мьютексом работают только с одного процессора. Ну а если произошло переключение — не страшно — всё равно это мьютекс.
В данном случае с сокетом такая оптимизация тоже бы подошла — т.е. стараемся работать с локальными данными, но если и произошло переключение контекста — не страшно. Но далее предположим, что даже есть строгая affinity. Т.е. всегда можно точно сказать на каком процессоре работаем.
MSS>Таким образом, чтобы сделать такую привязку к процессору, нужно в IRPе тащить за собой affinity нити, которая его создала. В принципе можно вместо этого использовать affinity нити напрямую через Tail.Overlay.Thread, но ассоциация, заданная этим полем, рвется в момент помещения IRPа в очередь IOCP при его завершении, т.е. с этого момента Tail.Overlay.Thread есть junk.
MSS>Я не помню в IRPе поля affinity. Структура — публичная. Можете посмотреть и сделать вывод о том, есть ли оно там.
MSS>Также я не помню ничего связанного с affinity в параметрах создания IOCP или file object.
MSS>Похоже, что этого нет. То есть — если вам нужна привязка завершения IRPа к CPU, то либо используйте другие механизмы завершения — OVERLAPPED::hEvent или Read/WriteFileEx и user APC, либо же _создайте по одному IOCP на процессор_ (и, конечно, по одному file object га процессор). Сделать последнее не так и сложно.
R>>Правильно ли я понимаю, что с портом завершения связана *одна* очередь сообщений, и с этими сообщениями не связан идентификатор процессора, с которого поступило данное сообщение?
MSS>В NT4 точно была одна, дальше не знаю.
MSS>В очереди IOCP лежат именно IRPs (зацеплены за Tail.Overlay.ListEntry), а я не припоминаю такого поля (CpuId или Affinity) в IRPе.
R>>Т.е. при вызове GetQueuedCompletionStatus() ОС не может автоматически стараться распределять обработку сообщения на те же процессоры, на которых они были сгенерированы?
MSS>Если в IRPе нет идентификатора CPU, и также его нет в параметрах создания IOCP — то это, понятно, невозможно.
R>>И в обратную сторону. Связан ли с IRP идентификатор процессора, на котором он был сгенерирован?
MSS>А где? структура-то публичная. Где он там, в каком поле?
R>>И старается ли ОС распределять IRP на тот процессор, на котором он был сгенерирован?
MSS>Если нет поля — то, значит, невозможно это.
R>>Там есть IRP.Tail.Overlay.Thread, но я так понимаю, что он для других целей.
MSS>Ага. Кстати, когда IRP попал в IOCP, оно уже junk — т.е. нет гарантии, что эта нить не умерла сразу после.
Так хорошо. Первый вывод такой, что автоматически Win32 такого сделать просто не может.
По поводу "OVERLAPPED::hEvent или Read/WriteFileEx и user APC, либо же _создайте по одному IOCP на процессор_ (и, конечно, по одному file object га процессор)".
OVERLAPPED::hEvent и Read/WriteFileEx+APC позволят работать с одним сокетом из разных потоков, но при этом направлять Completion Notification на тот же процессор, откуда был инициирован запрос. Но основной вопрос — будет ли ОС стараться обрабатывать запрос на том же процессоре? Если она не будет это делать, то пропадает смысл и обрабатывать завершение там же, откуда был запрос...
Один IOCP на процессор + один file object на процессор в принципе тоже приемлимый вариант, в том плане, что наврядли всё равно надо работать с одним сокетом из разных потоков. Если на запись ещё можно сообщения "сливать", то на чтение получится мешанина.
Но тот же вопрос остаётся — а имеет ли это какой-то смысл? Т.е. как заставить ОС сделать "аффинити сокета/порта завершения на процессор"?
R>OVERLAPPED::hEvent и Read/WriteFileEx+APC позволят работать с одним сокетом из разных потоков, но при этом направлять Completion Notification на тот же процессор, откуда был инициирован запрос. Но основной вопрос — будет ли ОС стараться обрабатывать запрос на том же процессоре? Если она не будет это делать, то пропадает смысл и обрабатывать завершение там же, откуда был запрос...
Dispatch routine в AFD.SYS исполняется в контексте юзер нити, если у нее стоит affinity — то да, есть гарантия процессора.
IopCompleteRequest опять исполняется в той же нити, опять гарантия процессора.
Все остальное, что между — это bottom half, говоря языком Линукса. Это вызовы, инициированные снизу из TCPIP — типа ClientEventReceive. Контекст этих вызовов прослеживается аж до ndisMDpc, которая зовется на том же CPU, что и прерывание от сетевой карты. А вот это может быть совсем не тот процессор, что у юзерской нити.
R> Если на запись ещё можно сообщения "сливать", то на чтение получится мешанина.
Вполне можно кинуть в 1 сокет много IRPов на чтение. Надо только заполнить порядок, в котором кидали, и при обработке завершенных IRP заглянуть в этот порядок. Порядок самого по себе завершения, кстати, не важен.
Но а) socket read совсем не обязательно полностью заполняет весь запрошенный размер, что делает слияние таких данных тяжелой задачей б) про несколько нитей вы правы, неизвестно, какая первой добежит до лока, защищающего очередь в AFD.
R>Но тот же вопрос остаётся — а имеет ли это какой-то смысл? Т.е. как заставить ОС сделать "аффинити сокета/порта завершения на процессор"?
Здравствуйте, Maxim S. Shatskih, Вы писали:
R>>OVERLAPPED::hEvent и Read/WriteFileEx+APC позволят работать с одним сокетом из разных потоков, но при этом направлять Completion Notification на тот же процессор, откуда был инициирован запрос. Но основной вопрос — будет ли ОС стараться обрабатывать запрос на том же процессоре? Если она не будет это делать, то пропадает смысл и обрабатывать завершение там же, откуда был запрос...
MSS>Dispatch routine в AFD.SYS исполняется в контексте юзер нити, если у нее стоит affinity — то да, есть гарантия процессора.
MSS>IopCompleteRequest опять исполняется в той же нити, опять гарантия процессора.
MSS>Все остальное, что между — это bottom half, говоря языком Линукса. Это вызовы, инициированные снизу из TCPIP — типа ClientEventReceive. Контекст этих вызовов прослеживается аж до ndisMDpc, которая зовется на том же CPU, что и прерывание от сетевой карты. А вот это может быть совсем не тот процессор, что у юзерской нити.
Т.е. облом
Интересна как раз "нижняя половина", которая непосредственно "трогает" данные, а не "верхняя половина", которая просто передаёт указатель.
R>>Но тот же вопрос остаётся — а имеет ли это какой-то смысл? Т.е. как заставить ОС сделать "аффинити сокета/порта завершения на процессор"?
MSS>Можно только об аффинити нити говорить.
Это я условно так обозвал то, чего я хочу добиться.
Кстати, Intel в пресс-релизе по Nehalem здесь говорит:
Improved addressing for interrupts with xAPIC capability
Возможно, это как раз даст возможность более правильно направлять прерывания по нужным ядрам. Правда, что конкретно это значит — непонятно. И на уровне TCP соединений, всё равно эта штука не сможет разруливать прерывания от сетевой карты...
Слушай, а может ответ даст старое доброе блокирующее АПИ (send/recv)?
На recv, правда, всё равно надежды нет, т.к. прерывание от сетевой карты не сможет обработаться на нужном ядре.
А вот на блокирующий send есть некоторая надежда. Я тут вижу 2 возможных варианта реализации:
Хороший: ОС сделает всю работу, включая копирование/отправку непосредственно в сетевую карту, на пользовательской нити.
Плохой: на пользовательской нити ОС только создаст и положит в очередь IRP, а далее заблокирует этот поток, пока IRP не будет обработан. Реальную же работу будет делать рабочий поток ОС, возможно на другом ядре.
Ты не в курсе, как это происходит в реальности при блокирующем send()?