Как разделяемая память и передача сообщений обрабатывают большие структуры данных?

При взгляде на подход Go и Erlang на concurrency я заметил, что оба они полагаются на передачу сообщений.

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

Однако рассмотрим случай, когда многие клиенты хотят параллельного доступа только для чтения к одной большой структуре данных в памяти - как массив суффикса.

Мои вопросы:

  • Будет ли использование общего состояния быстрее и меньше памяти, чем передача сообщений, поскольку блокировки в большинстве случаев не нужны, потому что данные доступны только для чтения и должны существовать только в одном месте?

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

  • Учитывая архитектуру современных процессоров и памяти, существует большая разница между этими двумя решениями - т.е. совместная память может считываться параллельно несколькими ядрами, то есть отсутствует узкое место в оборудовании, которое в противном случае реализации примерно одинаковы?

Ответ 1

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

  • Да, вы можете написать "серверный процесс", чтобы поделиться им. Благодаря действительно легким процессам, это не более тяжело, чем писать небольшой API для доступа к данным. Подумайте, как объект (в смысле ООП), который "владеет" данными. Разделение данных в кусках на повышение parallelism (называемое "очертание" в кругах DB) помогает в больших случаях (или если данные находятся на медленном хранении).

  • Даже если NUMA становится основным, у вас все еще есть все больше ядер в ячейке NUMA. И большая разница в том, что сообщение может быть передано между двумя ядрами, в то время как блокировка должна быть сброшена из кэша на всех ядрах, ограничивая ее задержкой между ячейками (даже медленнее, чем доступ к ОЗУ). Во всяком случае, shared-state/locks становится все более и более неосуществимым.

вкратце.... привыкнуть к передаче сообщений и серверным процессам, это все ярость.

Изменить: пересматривая этот ответ, я хочу добавить о фразе, найденной в документации Go:

обмениваться памятью, обмениваясь сообщениями, не обмениваясь данными с помощью общей памяти.

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

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

Ответ 2

Одна вещь, которую нужно понять, состоит в том, что модель Erlang concurrency делает НЕ действительно указывать, что данные в сообщениях должны копироваться между процессами, в ней указано, что отправка сообщений является единственным способом связи, и что нет общего состояния. Поскольку все данные неизменяемы, что фундаментально, тогда реализация может очень не копировать данные, а просто отправлять ссылку на нее. Или может использовать комбинацию обоих методов. Как всегда, лучшего решения не существует, и при выборе способов его компромиссов необходимо сделать компромисс.

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

Ответ 3

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

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

Ответ 4

Большинство современных процессоров используют варианты протокола MESI. Из-за общего состояния передача данных только для чтения между разными потоками очень дешева. Модифицированные общие данные очень дороги, хотя, поскольку все остальные кэши, хранящие эту строку кэша, должны аннулировать ее.

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

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

Ответ 5

Одно из решений, которое не было представлено здесь, - это репликация master-slave. Если у вас есть большая структура данных, вы можете реплицировать изменения на все подчиненные устройства, которые выполняют обновление на своей копии.

Это особенно интересно, если вы хотите масштабировать до нескольких машин, которые даже не имеют возможности обмениваться памятью без очень искусственных установок (mmap блочного устройства, которое читает/записывает из памяти удаленного компьютера?)

Вариант его состоит в том, чтобы иметь менеджера транзакций, который попросил бы лучше обновить реплицированную структуру данных, и он будет следить за тем, чтобы он одновременно обслуживал один и тот же запрос обновления. Это больше относится к модели mnesia для репликации master-master табличных данных mnesia, которые квалифицируются как "большая структура данных".

Ответ 6

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

Будет ли использование общего состояния быстрее и меньше памяти, чем передача сообщений, поскольку блокировки в большинстве случаев не нужны, потому что данные доступны только для чтения и должны существовать только в одном месте?

Использование общего состояния будет намного быстрее.

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

Можно использовать любой подход.

Учитывая архитектуру современных процессоров и памяти, существует ли большая разница между этими двумя решениями - то есть, может ли разделяемая память считываться параллельно несколькими ядрами, то есть отсутствует аппаратное узкое место, которое в противном случае обеспечивало бы выполнение обеих реализаций то же?

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

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

Ответ 7

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

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

Если ваша система/проблема нелегко разбивается на несколько простых и простых данных, то у вас есть проблема. И не все проблемы могут быть решены путем передачи сообщений. Вот почему все еще есть некоторые суперкомпьютеры на базе Itanium, поскольку они имеют терабайт общей RAM и до 128 процессоров, работающих в одной и той же общей памяти. Они являются более дорогим, чем основной кластер x86 с той же мощью процессора, но вам не нужно разбивать ваши данные.

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

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

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

Ответ 8

Что такое структура большая?

Один человек большой - это другие лица, маленькие.

На прошлой неделе я поговорил с двумя людьми: один человек делал встроенные устройства, в которых использовал слово "большой" - я спросил его, что это значит - он говорит более 256 килобайт - позже на той же неделе парень говорил о распространении средств массовой информации - он использовал слово "большой" , я спросил его, что он значит, он немного подумал и сказал, что "не поместится на одной машине", скажем, 20-100 TBytes

В терминах Эрланга "большой" может означать "не вписывается в ОЗУ" - так что с 4 ГБ ОЗУ структуры данных > 100 мегабайт могут считаться большими - копирование структуры данных 500 Мбайт может быть проблемой. Копирование небольших структур данных (скажем, менее 10 мегабайт) никогда не является проблемой в Erlang.

Действительно большие структуры данных (т.е. те, которые не помещаются на одной машине) должны быть скопированы и "чередуются" на нескольких машинах.

Итак, у вас есть следующее:

Малые структуры данных не представляют проблемы - поскольку они являются небольшими временами обработки данных, быстро, копирование выполняется быстро и так далее (только потому, что они малы)

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

Ответ 9

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

Ответ 10

Другой параллельной парадигмой является STM, программная транзакционная память. Clojure ref уделяют много внимания. Тим Брей имеет хорошую серию исследований эрлангов и Clojure параллельных механизмов

http://www.tbray.org/ongoing/When/200x/2009/09/27/Concur-dot-next

http://www.tbray.org/ongoing/When/200x/2009/12/01/Clojure-Theses