Здравствуйте, eao197, Вы писали:
E>Здравствуйте, Gaperton, Вы писали:
G>>Вопрос по существу — ты сказал, что реализация очереди будет "избыточнее", чем то неизвестно что, что ты там планируешь в данной задаче вместо нее применять. Докажи.
E>В разговоре с Siclair я несколько раз упоминал использование atomic_read. И он понял о чем я говорю, поскольку идея очень простая.
Sinclair не просто понял, о чем ты говоришь (это дело нехитрое), он, если ты не заметил, еще и объяснил тебе, почему ты неправ со своим atomic_read.
Мне очень жаль, но это совершенно очевидно будет работать медленнее, чем мое решение на очередях сообщений. Потому, что у меня будет просто обычный read на каждый lookup. Я думал, еще раз тебе этого объяснять не стоит, и ты придумал что-то новенькое?
Re[46]: пример eao197: "сообщения" рвут "разделяемую память"
Здравствуйте, Gaperton, Вы писали:
G>Sinclair не просто понял, о чем ты говоришь (это дело нехитрое), он, если ты не заметил, еще и объяснил тебе, почему ты неправ со своим atomic_read.
Может быть тогда ты сможешь сказать, у какой именно очереди сообщений накладные расходы на проверку наличия нового элемента будут меньше одного atomic_read?
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[47]: пример eao197: "сообщения" рвут "разделяемую память"
Здравствуйте, eao197, Вы писали:
E>>>Как и 7% преимущества в скорости. Известная песня.
G>>7% преимущества в скорости — это и правда фигня полная
E>Это полная фигня на словах в форуме.
Про то, что 7% это "не фигня" — пионерам рассказывай, ладно? На практике 7% называется "одинаковая производительность", и никто не будет расшибаться в лепешку, чтобы эти 7% ликвидировать.
E>На практике, ты не сможешь этот выигрыш нивелировать.
На практике, смогу. Если захочу. В любом случае, тебе-то откуда знать, что я смогу а что нет.
G>>(особенно по сравнению с проигрышем OpenMP на примерно 140% в одном тесте, что ты предпочел забыть)
E>Я не забыл. Проигрыш OpenMP всего лишь доказывает, что не всегда у разделяемой памяти есть преимущества. Но ты забыл, что этого здесь никто и не отрицал.
Да? Тогда прокомментируй еще вот это:
А уж по поводу ситуации, где нам надо только считать данные — обмен сообщениями (на любых очередях), так и останется медленнее на несколько порядков.
G>>Задача, которую ты описал — это малюсенькая часть функциональности контакт-центра.
E>Ее масштаб вовсе не является преградой к тому, чтобы на ее примере показать наличие преимуществ у разделяемой памяти.
Так вэлкам! Что ж ты никак не покажешь-то эти преимущества? Что тогда для тебя преградой то является, а?
Re[47]: пример eao197: "сообщения" рвут "разделяемую память"
Здравствуйте, eao197, Вы писали:
G>>Sinclair не просто понял, о чем ты говоришь (это дело нехитрое), он, если ты не заметил, еще и объяснил тебе, почему ты неправ со своим atomic_read.
E>Может быть тогда ты сможешь сказать, у какой именно очереди сообщений накладные расходы на проверку наличия нового элемента будут меньше одного atomic_read?
Может быть, я тебе это уже сказал во второй части своего сообщения, которую ты предусмотрительно вырезал? Может быть, Синклер тебе все то же самое объяснил?
G>Мне очень жаль, но это совершенно очевидно будет работать медленнее, чем мое решение на очередях сообщений. Потому, что у меня будет просто обычный read на каждый lookup. Я думал, еще раз тебе этого объяснять не стоит, и ты придумал что-то новенькое?
Ты что, не знаешь, что atomic_read заметно медленнее обычного read? Может быть, ты наконец перестанешь прикидываться валенком? Обкакался, так будь мужиком, блин, и имей смелость это признать. Тем более — это всего лишь форум, и всего знать невозможно. Я был о тебе лучшего мнения, короче.
Здравствуйте, EvilChild, Вы писали:
S>>Хорошо, worker получил пакет, в котором 100 транзакций, каждая из которых читает дерево. Выполнил — полез в очередь — получил новое дерево — поехал дальше. EC>А почему нельзя перед обработкой пакета тупо скопировать ссылку себе на стек?
Почему нельзя? Можно. Получишь в точности очередь сообщений, а именно — ее предельный случай с глубиной 1 и полиси вытеснения при переполнении по FIFO — что станет совершенно очевидно даже тем, кто в танке, если завернуть то что получится в класс, и посмотреть на функциональный интерфейс.
Потом можно пойти еще дальше, упереться, и под разными предлогами не называть это очередью сообщений. Видишь ли, asynchronous message passing — это не более чем дизайн-идиома, которая в конечном счете на современных архитектурах все равно превратится в те примитивы, через которые реализуются семафоры все остальное. Так же, как программа на функциональном языке в конечном счете превратится в ассемблерные инструкции.
Здравствуйте, Gaperton, Вы писали:
EC>>А почему нельзя перед обработкой пакета тупо скопировать ссылку себе на стек?
G>Почему нельзя? Можно. Получишь в точности очередь сообщений, а именно — ее предельный случай с глубиной 1 и полиси вытеснения при переполнении по FIFO — что станет совершенно очевидно даже тем, кто в танке, если завернуть то что получится в класс, и посмотреть на функциональный интерфейс.
Можно и о скаляре думать как об одномерном массиве с одним элементом, вопрос в том когда такой взгляд на вещи полезен. G>Потом можно пойти еще дальше, упереться, и под разными предлогами не называть это очередью сообщений.
Я ниразу не против называть это очередью.
Я спрашивал не в разрезе вашего увлекательного спора message passing vs shared memory,
а исключительно в контексте конкретного решения с иммутабельным деревом. G>Видишь ли, asynchronous message passing — это не более чем дизайн-идиома, которая в конечном счете на современных архитектурах все равно превратится в те примитивы, через которые реализуются семафоры все остальное. Так же, как программа на функциональном языке в конечном счете превратится в ассемблерные инструкции.
Мне кажется важным различать то, в каких терминах мы можем думать о решении и то, как оно реализуется.
G>Если использовать это как аргумент, все закончится Теоремой Существования Дворкина (http://rsdn.ru/forum/message/3194838.1.aspx
), на которую любят опираться люди, которым с пеной у рта нечего сказать. Ну так что на таких время тратить — только портить.
Что вы так Дворкина любите пинать? Я с ним во многих случаях тоже не согласен, это же не повот его пинать по любому поводу.
Здравствуйте, EvilChild, Вы писали:
EC>>>А почему нельзя перед обработкой пакета тупо скопировать ссылку себе на стек?
G>>Почему нельзя? Можно. Получишь в точности очередь сообщений, а именно — ее предельный случай с глубиной 1 и полиси вытеснения при переполнении по FIFO — что станет совершенно очевидно даже тем, кто в танке, если завернуть то что получится в класс, и посмотреть на функциональный интерфейс. EC>Можно и о скаляре думать как об одномерном массиве с одним элементом, вопрос в том когда такой взгляд на вещи полезен.
Можно и об очередях сообщений (вернее, мэйлбоксах, так правильнее) думать как о разделяемой памяти, вопрос, когда такой взгляд на вещи полезен. Мой взгляд на вещи позволяет писать такие программы, пример которой я привел. Насколько он полезен для тебя — это ты сам решай, а я тебе немного расскажу пока про то, какие вообще бывают мэйлбоксы, и ситуации их употребления.
Данный объект по всем своим свойствам объективно является мэйлбоксом для приема асинхронных сообщений, и в данном случае употребляется в том контексте, где применяется мэйлбокс. Подобного рода мэйлбоксы на одно сообщения — распространенная вещь в дизайне на асинхронных сообщениях. А вот, например, семафор по всем своим свойствам мэйлбоксом не является.
Характерный пример — во время обработки какого-либо сообщения, процесс хочет выполнить RPC-вызов. Для этого, он отправляет сообщение процессу, и встает в блокирующее ожидание на мэйлбокс. Второй процесс выполняет запрос, и возвращает результат. Один. В виде одного объекта. В указанный мэйлбокс, специально для этого созданный.
В случае Эрланга, где присутствует сопоставление с образцом и селективный receive, заводить явно этого mailbox не надо. Достаточно написать:
И не важно, свалятся в мэйлбокс во время выполнения RPC другие запросы, или нет. В случае тех языков, где selective receive не поддержан, например, Java, C++, и C#, для приема результата разумные люди заводят отдельный мэйлбокс в вызывающем процессе. Что, кстати, еще и эффективнее.
И разумеется, полной очереди здесь создавать не обязательно — здесь гарантированно хватит специального мэйлбокса, рассчитанного на одно сообщение. Групповой receive в данном случае на группе мэйлбоксов изобразить также возможно, кстати, — он ждет первого сообщения из любой из указанных очередей. Получишь почти полный изоморфизм с моделью Эрланга, и характерными для него приемами проектирования.
А то, какую именно реализацию очереди ты применяешь в каждом конкретном случае — исключительно вопрос оптимизации, а не модели программирования. И более того, количество разных видов этих очередей сильно конечно.
G>>Потом можно пойти еще дальше, упереться, и под разными предлогами не называть это очередью сообщений. EC>Я ниразу не против называть это очередью. EC>Я спрашивал не в разрезе вашего увлекательного спора message passing vs shared memory, EC>а исключительно в контексте конкретного решения с иммутабельным деревом.
В контексте данного примера — речь об предварительной оптимизации, дающей совершенно мизерный эффект на общую производительность, в силу небольшого потока данных по данному каналу в сравнении с основным. Конкретнее, речь идет о специальной оптимизированной реализации подобного мэйлбокса на одно сообщение, соответствующего мультикаст-группе.
G>>Видишь ли, asynchronous message passing — это не более чем дизайн-идиома, которая в конечном счете на современных архитектурах все равно превратится в те примитивы, через которые реализуются семафоры все остальное. Так же, как программа на функциональном языке в конечном счете превратится в ассемблерные инструкции. EC>Мне кажется важным различать то, в каких терминах мы можем думать о решении и то, как оно реализуется.
А мне кажется важным применять в разработке фреймворки, которые позволяют тебе делать реализацию решения в тех терминах, в которых формулируется решение. Боюсь, я не один, кому так кажется.
G>>Если использовать это как аргумент, все закончится Теоремой Существования Дворкина (http://rsdn.ru/forum/message/3194838.1.aspx
), на которую любят опираться люди, которым с пеной у рта нечего сказать. Ну так что на таких время тратить — только портить. EC>Что вы так Дворкина любите пинать? Я с ним во многих случаях тоже не согласен, это же не повот его пинать по любому поводу.
Достал ты уже со своим ерлангом , Я не ничего не имею против ассинхронных сообщений (сделать это можно на любом языле я думаю)
Как абстракции на нем писать предлагаешь , можно конешно через жопу (и на асемблере можно), Но в данном
случае например предпочитаю нормальные обьектно ориентированные языки.
Я смотрел на синтаксис ерланга и чесно говоря он него тошнит , хотя в некоторых случаях думаю имеет смысл
заюзать.
G>В случае Эрланга, где присутствует сопоставление с образцом и селективный receive, заводить явно этого mailbox не надо. Достаточно написать:
G>rpc_call( CalleePID, Method, ArgList ) -> G>CalleePID ! { self(), Method, ArgList }, G>receive { CalleePID, Result } -> Result.
Здравствуйте, kdw, Вы писали:
kdw>Достал ты уже со своим ерлангом,
Так не читай мои посты. Кто-то заставляет?
kdw>Как абстракции на нем писать предлагаешь , можно конешно через жопу (и на асемблере можно), Но в данном kdw>случае например предпочитаю нормальные обьектно ориентированные языки.
С чего ты взял, что я тебе что-то предлагаю.
kdw>Я смотрел на синтаксис ерланга и чесно говоря он него тошнит , хотя в некоторых случаях думаю имеет смысл
заюзать.
Меня совершенно не трогают твои предпочтения, предположения, душевные терзания, и от чего именно тебя тошнит.
Здравствуйте, kdw, Вы писали:
G>>Так не читай мои посты. Кто-то заставляет?
kdw>Кроме тебя в этом форуме есть и другие люди , kdw>как ты себе представляешь чьи то посты читать а чьи то нет. kdw>вот приходиться и твои читать.
Значит, прими касторки. Мне, знаешь, пофигу, читаешь ты, не читаешь.
Здравствуйте, Gaperton, Вы писали:
G>Характерный пример — во время обработки какого-либо сообщения, процесс хочет выполнить RPC-вызов. Для этого, он отправляет сообщение процессу, и встает в блокирующее ожидание на мэйлбокс. Второй процесс выполняет запрос, и возвращает результат. Один. В виде одного объекта. В указанный мэйлбокс, специально для этого созданный.
G>В случае Эрланга, где присутствует сопоставление с образцом и селективный receive, заводить явно этого mailbox не надо. Достаточно написать:
G>rpc_call( CalleePID, Method, ArgList ) -> G>CalleePID ! { self(), Method, ArgList }, G>receive { CalleePID, Result } -> Result.
G>И не важно, свалятся в мэйлбокс во время выполнения RPC другие запросы, или нет. В случае тех языков, где selective receive не поддержан, например, Java, C++, и C#, для приема результата разумные люди заводят отдельный мэйлбокс в вызывающем процессе. Что, кстати, еще и эффективнее.
G>И разумеется, полной очереди здесь создавать не обязательно — здесь гарантированно хватит специального мэйлбокса, рассчитанного на одно сообщение. Групповой receive в данном случае на группе мэйлбоксов изобразить также возможно, кстати, — он ждет первого сообщения из любой из указанных очередей. Получишь почти полный изоморфизм с моделью Эрланга, и характерными для него приемами проектирования.
Собственно, я не сказал вам самого интересного про эти однообъектные мэйлбоксы. У них есть специальное название — фьючерс. И они сами по себе представляют интересную концепцию, настолько, что она достойна поддержания на уровне языка.
Фьючерс — это значение, которое будет вычислено. Его вычисление форсируется в момент первого обращения к фьючерсу. Это обобщение ленивых вычислений и асинхронного межпроцессного взаимодействия.
То есть, в случае примера с RPC — мы просим другой процесс асинхронно обработать наш запрос, положив результат во фьючерс. И, когда мы попробуем из фьючерса что-то считать, а там ничего нет — то мы встанем в блокирующее ожидание.
Более интересный пример, демонстрирующий силу фьючерсов. Допустим, мы хотим рассчитать массив из десяти значений. Каждое — параллельно. Мы просто заполняем этот массив в цикле фьючерсами, каждый из которых — RPC-вызов к какому-то потоку (или старт отдельного потока, выполняющего рассчет, возможно — асинхронный старт, по мере доступности потоков в пуле). И все. Потом — просто пользуемся массивом, не выставляя никакой явной синхронизации, и не принимая сообщений. Допустим, так же пробегаемся по нему в цикле, считая сумму.
При этом, мы можем сделать вычисление не параллельным, а "ленивым", чтобы оно вычислялось в момент обращения. Программа будет выглядеть так же.
Фьючерсы — это совершенно потрясающая модель, дружественная к асинхронным сообщениям и тому стилю программирования, про который я сейчас рассказал, но еще более высокоуровневая, и в еще меньшей степени подверженная ошибкам. Пример того, как фьючерсы поддержаны в языке, можно увидеть в Alice ML.
Здравствуйте, Gaperton, Вы писали:
G>Более интересный пример, демонстрирующий силу фьючерсов. Допустим, мы хотим рассчитать массив из десяти значений. Каждое — параллельно. Мы просто заполняем этот массив в цикле фьючерсами, каждый из которых — RPC-вызов к какому-то потоку (или старт отдельного потока, выполняющего рассчет, возможно — асинхронный старт, по мере доступности потоков в пуле). И все. Потом — просто пользуемся массивом, не выставляя никакой явной синхронизации, и не принимая сообщений. Допустим, так же пробегаемся по нему в цикле, считая сумму.
что характерно, все эти, действительно полезные вещи, скажем, в java доступны на уровне стандартной библиотеки, начиная с версии 5. т.е. я хочу сказать, что это не какое-то вчерашнее открытие, раз есть давно и в таком популярном языке
что касается их применения, то мне доводилось использовать Future в реализациях протоколов уровня приложения с возможностью ожидания подтверждения от контрагента для более, чем одного посланного пакета (windowing > 1)
Здравствуйте, Gaperton, Вы писали:
G>Я думаю, фьючерсы — это лучшее, что придумано на текущий момент в области языкостроения и параллельного программирования.
До тех пор, пока код фьючерса не попробует обращаться к разделяемой памяти...
Хардкорный Эрланг. Добавляем туда самые настоящие, честные фьючера и ленивые вычисления. Две процедуры, которые возвращают фьючерс. Фьючерс — это функция без аргументов. Ее можно вызывать многократно, ничего страшного не произойдет, она вычисляет результат только один раз. Поэтому, в функции-фьючере допустимы побочные эффекты.
Даю две функции — первая вычисляется параллельно и энергично, вторая — тупое ленивое вычисление.
Функция eval — служебная, решение полагается на замыкания и словарь процессов. Внимание — код писан в один проход и не проверен, может не сработать.
concurrent_eval( F ) ->
PID = spawn( F ),
Eval = fun() ->
receive { PID, Result } -> Result end
end,
fun() -> eval( PID, Eval ) end.
lazy_eval( F ) ->
fun() -> eval( F, F ) end.
eval( Key, F ) ->
case get( Key ) of
none -> put( Key, Result = F() ), Result;
Value -> Value
end.
Хм... Подчеркнем-ка мы явно связь между ленивыми вычислениями и параллелизмом...
lazy_eval( F ) ->
fun() ->
case get( F ) of
none -> put( F, Result = F() ), Result;
Value -> Value
end
end.
concurrent_eval( F ) ->
PID = spawn( F ),
Receive = fun() ->
receive { PID, Result } -> Result end
end,
lazy_eval( Receive ).
rpc_future( PID, Message ) ->
PID ! Message,
Receive = fun() ->
receive { PID, Result } -> Result end
end,
lazy_eval( Receive ).
Правда, эта реализация с багой. Багу вызывает использование внутри lazy_eval аргумента в качестве ключа, что выйдет боком в случае аргумента с побочным эффектом. Здесь надо что-то придумать. Самое надежное — использовать в качестве ключа уникальный номер с автоинкрементом, который генерировался бы внутри lazy_eval. Однако, правильнее дать программисту над этим контроль.
Однако, думаю, идея понятна. RPC в таком стиле мне нравится даже в Эрланге. Приятный rpc. И более эффективный в том плане, что не режет параллелизм. В чем, собственно, и состоит преимущество фьючерсов.
Забыл передать аргументом новому процессу self(). И ваще, послать результат обратно . Но мысль мне нравится. Надо ее думать. Кажется, что-то похожее предлагал Армстронг со своим оператором bang-bang, и вроде как написал специальный модуль.
G>
Здравствуйте, C0s, Вы писали:
G>>Более интересный пример, демонстрирующий силу фьючерсов. Допустим, мы хотим рассчитать массив из десяти значений. Каждое — параллельно. Мы просто заполняем этот массив в цикле фьючерсами, каждый из которых — RPC-вызов к какому-то потоку (или старт отдельного потока, выполняющего рассчет, возможно — асинхронный старт, по мере доступности потоков в пуле). И все. Потом — просто пользуемся массивом, не выставляя никакой явной синхронизации, и не принимая сообщений. Допустим, так же пробегаемся по нему в цикле, считая сумму.
C0s>что характерно, все эти, действительно полезные вещи, скажем, в java доступны на уровне стандартной библиотеки, начиная с версии 5. т.е. я хочу сказать, что это не какое-то вчерашнее открытие, раз есть давно и в таком популярном языке
Вообще, в этом деле довольно мало вещей, которые являются вчерашним открытием. Фьючерсы были известны в 80-х. Кажется. А про асинхронные сообщения Дийкстра писал ваще черт знает когда. Просто тема параллельного программирования пошла в мэйнстрим относительно недавно, с приходом в жизнь многоядерных мультитредных процов. Рядовому программеру просто не нужно было этого всего знать еще 5 лет назад. Да и сейчас в массе своей обходятся. Пока Нехалемы с Ниагарами не стали обыденными вещами, вызывающими зевоту.
Ну, это же очень хорошо, что они уже есть в библиотеке. Это означает, что можно начинать их "правильно готовить" немедленно . А если б их не было, их несложно было б добавить. Мое мнение — давно пора.