Что посоветуете применить в данном примере, м.б. ФП?
От: sergunok  
Дата: 01.08.11 21:38
Оценка:
// класс Цель, обладает неизменным идентификатором, координатами и состоянием.
class Target
{
  public int Id;
  public int X;
  public int Y;
  public TargetStatus Status; 
}

// класс Читатель информации о целях.. вычитывает откуда-то снаружи информацию о целях. поддерживает массив актуальных целей и дергает события об их появлении и модификации
class TargetReader
{
   public IDictionary<int, Target> Targets; // Id -> Target
   public event Action<Target> NewTarget;
   public event Action<Target> TargetChanged;
 

   public void Run()
   {
     while(true)
     {
        // код, считывающий данные о Target'ах откуда-то снаружи, предобрабатывающий их, обновляющий this.Targets и дергающий соответствующие события
        // код не совсем пустой, потому  реализуется за счет многонитевой обработки, соответственно события могут дергаться в разных нитях
     }
   } 
}

Далее нужно построить некоторую логику, которая реагирует на события NewTarget и TargetChanged, смотрит на свойства соответствующего или нескольких Targets и "что-то делает". Логику можно описывать по-разному, можно использовать callback'и (или что-то новомодное из .NET типа Async, но не хочется делать вопрос .NET специфичным). Но как ни крути возникает проблема concurrency, например,
void onTargetChanged(Target target)
{
  if(target.X > 100)
  {
    if(target.X > 1000 && this.reader.Targets.Count > 5)
      ...
  }
}

К моменту второго if target.X мог измениться, а вложенные if рассчитаны на его неизменность.

Знатоки concurrency, подскажите, как подобный примерчик можно переделать, чтобы этой проблемы не было, но код "логики" при этом читался.. Интуитивно хочется передавать изменения "целей" по свойствам и избавляться от shared state — this.Targets.. А это напоминает тезисы функционального программирования Соответственно вопрос №2 знатокам ФП.. Как примерно и разумно ли переделывать сам TargetReader либо "логику" с помощью языков ФП в данном случае? Как в ФП реализуется shared state?

Заранее спасибо!
Re: OFF: В батл стар галактику взяли новых программистов? :)
От: fddima  
Дата: 01.08.11 21:52
Оценка:
Re: Что посоветуете применить в данном примере, м.б. ФП?
От: Sinix  
Дата: 02.08.11 00:11
Оценка: 2 (1) +2
Здравствуйте, sergunok, Вы писали:

Если производительность отдельного потока не на 1м месте, то можно посмотреть в сторону подхода RX — генерации потока значений из набора событий. В любом случае, стоит отказаться от public-полей, переименовать TargetReader в TargetDispatcher и (опционально) скидывать результаты обработки в очередь, вычитываемую другим набором потоков (отдельным потоком, если обработчики событий не подвисают).

S>К моменту второго if target.X мог измениться, а вложенные if рассчитаны на его неизменность.

К этому моменту "изя уже всё" — поздно дёргаться. Три варианта:
— работать со "снимками" нужных вам данных;
— мучаться с блокировками;
— изобретать lock-free алгоритмы. Не взлетит, но звучит очень круто.
Re[2]: Что посоветуете применить в данном примере, м.б. ФП?
От: sergunok  
Дата: 02.08.11 08:26
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, sergunok, Вы писали:


S>Если производительность отдельного потока не на 1м месте, то можно посмотреть в сторону подхода RX — генерации потока значений из набора событий. В любом случае, стоит отказаться от public-полей, переименовать TargetReader в TargetDispatcher и (опционально) скидывать результаты обработки в очередь, вычитываемую другим набором потоков (отдельным потоком, если обработчики событий не подвисают).

Понятненько, т.е. что с RX, что без него это будут потоки (или очереди) изменений свойств объектов и никаких разделяемых модифицируемых объектов.

Например, с RX как-то так:
public IObservable<Target> NewTargets;
public IObservable<Tuple<int, int>> XTargetChanged; // первый параметр — Id
public IObservable<Tuple<int, int>> YTargetChanged;
....
?

S>>К моменту второго if target.X мог измениться, а вложенные if рассчитаны на его неизменность.

S>К этому моменту "изя уже всё" — поздно дёргаться. Три варианта:
S>- работать со "снимками" нужных вам данных;
В своем примере, без использования блокировок я даже снимок не могу сделать.
Запросто может оказаться что в процессе копирования нужных свойств объекта эти свойства по совокупности неконсистентны.

S>- мучаться с блокировками;

S>- изобретать lock-free алгоритмы. Не взлетит, но звучит очень круто.
А есть не MS реализации STM для .NET? (STM.NET вроде как в процессе доработки)
Re: Что посоветуете применить в данном примере, м.б. ФП?
От: Undying Россия  
Дата: 02.08.11 09:00
Оценка:
Здравствуйте, sergunok, Вы писали:

S>К моменту второго if target.X мог измениться, а вложенные if рассчитаны на его неизменность.


S>Знатоки concurrency, подскажите, как подобный примерчик можно переделать, чтобы этой проблемы не было, но код "логики" при этом читался.. Интуитивно хочется передавать изменения "целей" по свойствам и избавляться от shared state — this.Targets.. А это напоминает тезисы функционального программирования Соответственно вопрос №2 знатокам ФП.. Как примерно и разумно ли переделывать сам TargetReader либо "логику" с помощью языков ФП в данном случае? Как в ФП реализуется shared state?


Сделай все поля Target readonly. При модификации создавай новый экземпляр target.
Re[2]: Что посоветуете применить в данном примере, м.б. ФП?
От: sergunok  
Дата: 02.08.11 10:33
Оценка:
Здравствуйте, Undying, Вы писали:

U>Здравствуйте, sergunok, Вы писали:


S>>К моменту второго if target.X мог измениться, а вложенные if рассчитаны на его неизменность.


S>>Знатоки concurrency, подскажите, как подобный примерчик можно переделать, чтобы этой проблемы не было, но код "логики" при этом читался.. Интуитивно хочется передавать изменения "целей" по свойствам и избавляться от shared state — this.Targets.. А это напоминает тезисы функционального программирования Соответственно вопрос №2 знатокам ФП.. Как примерно и разумно ли переделывать сам TargetReader либо "логику" с помощью языков ФП в данном случае? Как в ФП реализуется shared state?


U>Сделай все поля Target readonly. При модификации создавай новый экземпляр target.

Чем не вариант Только вот производительность и сборка мусора.. Модификаций будет много и часто.
Re[3]: Что посоветуете применить в данном примере, м.б. ФП?
От: Undying Россия  
Дата: 02.08.11 10:41
Оценка:
Здравствуйте, sergunok, Вы писали:

U>>Сделай все поля Target readonly. При модификации создавай новый экземпляр target.

S>Чем не вариант Только вот производительность и сборка мусора.. Модификаций будет много и часто.

Много это сколько? Миллионы в секунду что ли? Или размер копируемых данных очень велик?
Re[4]: Что посоветуете применить в данном примере, м.б. ФП?
От: sergunok  
Дата: 02.08.11 11:06
Оценка:
Здравствуйте, Undying, Вы писали:

U>Здравствуйте, sergunok, Вы писали:


U>>>Сделай все поля Target readonly. При модификации создавай новый экземпляр target.

S>>Чем не вариант Только вот производительность и сборка мусора.. Модификаций будет много и часто.

U>Много это сколько? Миллионы в секунду что ли? Или размер копируемых данных очень велик?


Десятки тысяч в секунду. Размер раз в 5 по-более чем приведенный Target.
Re[5]: Что посоветуете применить в данном примере, м.б. ФП?
От: Undying Россия  
Дата: 02.08.11 11:15
Оценка:
Здравствуйте, sergunok, Вы писали:

S>Десятки тысяч в секунду. Размер раз в 5 по-более чем приведенный Target.


Target занимает 16 байт, в 5 раз больше это 80 байт. Т.е. общий объем копирований будет менее 10 МБт (100 тысяч копирований по 100 байт) в секунду. Сборщик мусора этим явно не напрягешь.
Re[5]: Что посоветуете применить в данном примере, м.б. ФП?
От: Sinix  
Дата: 02.08.11 11:37
Оценка:
Здравствуйте, sergunok, Вы писали:

S>Например, с RX как-то так:

Как вариант. Но не для 10000 event per second.

S>Десятки тысяч в секунду. Размер раз в 5 по-более чем приведенный Target.


10 000 параллельных грабель в секунду? Что-то мне подсказывает, что неплохо бы пересмотреть подход в целом: сначала вы пытаетесь улучшить производительность, распараллеливая поток зависимых событий, а потом собираете его обратно и убиваете блокировками весь выигрыш. Где логика?
Re[6]: Что посоветуете применить в данном примере, м.б. ФП?
От: sergunok  
Дата: 02.08.11 13:26
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, sergunok, Вы писали:


S>>Например, с RX как-то так:

S>Как вариант. Но не для 10000 event per second.
Вы использовали его для высокой нагрузки? Очень интересно каковы результаты.

S>>Десятки тысяч в секунду. Размер раз в 5 по-более чем приведенный Target.


S>10 000 параллельных грабель в секунду? Что-то мне подсказывает, что неплохо бы пересмотреть подход в целом: сначала вы пытаетесь улучшить производительность, распараллеливая поток зависимых событий, а потом собираете его обратно и убиваете блокировками весь выигрыш. Где логика?


Пока блокировок особых нет, только при операциях с this.Targets.

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

То, что хочется:
1. Оставить возможность параллельной предобработки
2. Обеспечить удобство описания "логики" на базе генерируемых событий.. И чтоб этой логике по-минимуму мешала concurrency
Re[7]: Что посоветуете применить в данном примере, м.б. ФП?
От: Sinix  
Дата: 02.08.11 14:00
Оценка:
Здравствуйте, sergunok, Вы писали:

S>>>Например, с RX как-то так:

S>>Как вариант. Но не для 10000 event per second.
S>Вы использовали его для высокой нагрузки? Очень интересно каковы результаты.
Нет, сам RX такой вариант потянет, в крайнем случае несложно сделать state-машину самому. А вот как вы будете разбираться с остальной обвязкой, включая синхронизацию потоков — .

S>Пока блокировок особых нет, только при операциях с this.Targets.

Ок, как будем получать согласованные данные без блокировок?

S>То, что хочется:

S>1. Оставить возможность параллельной предобработки
S>2. Обеспечить удобство описания "логики" на базе генерируемых событий.. И чтоб этой логике по-минимуму мешала concurrency
Тут у вас противоречие Временно беру паузу — подождём ещё советов.
Re[5]: Что посоветуете применить в данном примере, м.б. ФП?
От: Undying Россия  
Дата: 03.08.11 04:34
Оценка: +1
Здравствуйте, sergunok, Вы писали:

S>Десятки тысяч в секунду. Размер раз в 5 по-более чем приведенный Target.


Так все-таки что несколько десятков тысяч выделений памяти по 100 байт в секунду это критическая нагрузка для сборщика мусора? Тестировали?

И собственно какие есть альтернативы? Для обработки вам нужно согласованное состояние Target, варианты есть следующие:

1) Уже предложенное превращение Target в readonly-объект. И создание нового экземпляра Target при любой модификации.
2) Выполнение операций над target под локом, запрещающим модификации target.
3) Если большинству операций нужно не все состояние target, а только его часть. То либо под локом запрещающим модификации target выполняем копирование нужной части target и затем с откопированной частью работаем. Либо разбиваем target на вложенные блоки и для операции выполняем копирование только нужных блоков.

Второй вариант это очень плохо, выполнение произвольных действий под локом это путь гарантированных грабель. Соответственно остается либо первый, либо третий.
Re[6]: Что посоветуете применить в данном примере, м.б. ФП?
От: sergunok  
Дата: 03.08.11 10:17
Оценка:
U>Так все-таки что несколько десятков тысяч выделений памяти по 100 байт в секунду это критическая нагрузка для сборщика мусора? Тестировали?
Нет. Но число запусков сборки мусора хочется минимизировать.

U>И собственно какие есть альтернативы? Для обработки вам нужно согласованное состояние Target, варианты есть следующие:


U>1) Уже предложенное превращение Target в readonly-объект. И создание нового экземпляра Target при любой модификации.

U>2) Выполнение операций над target под локом, запрещающим модификации target.
Тут наверное возможен лок типа читатель-писатель. Предобработка target'ов — это читатели. А хендлеры событий будут запускаться как писатели.

U>3) Если большинству операций нужно не все состояние target, а только его часть. То либо под локом запрещающим модификации target выполняем копирование нужной части target и затем с откопированной частью работаем. Либо разбиваем target на вложенные блоки и для операции выполняем копирование только нужных блоков.

По-сути это путь ФП.

U>Второй вариант это очень плохо, выполнение произвольных действий под локом это путь гарантированных грабель. Соответственно остается либо первый, либо третий.
Re[7]: Что посоветуете применить в данном примере, м.б. ФП?
От: Undying Россия  
Дата: 03.08.11 10:52
Оценка:
Здравствуйте, sergunok, Вы писали:

U>>Так все-таки что несколько десятков тысяч выделений памяти по 100 байт в секунду это критическая нагрузка для сборщика мусора? Тестировали?

S>Нет. Но число запусков сборки мусора хочется минимизировать.

Зачем?

U>>2) Выполнение операций над target под локом, запрещающим модификации target.

S>Тут наверное возможен лок типа читатель-писатель. Предобработка target'ов — это читатели. А хендлеры событий будут запускаться как писатели.

Не понял, что имеется в виду.

U>>3) Если большинству операций нужно не все состояние target, а только его часть. То либо под локом запрещающим модификации target выполняем копирование нужной части target и затем с откопированной частью работаем. Либо разбиваем target на вложенные блоки и для операции выполняем копирование только нужных блоков.

S>По-сути это путь ФП.

И?
Re[8]: Что посоветуете применить в данном примере, м.б. ФП?
От: sergunok  
Дата: 03.08.11 11:26
Оценка:
Здравствуйте, Undying, Вы писали:

U>Здравствуйте, sergunok, Вы писали:


U>>>Так все-таки что несколько десятков тысяч выделений памяти по 100 байт в секунду это критическая нагрузка для сборщика мусора? Тестировали?

S>>Нет. Но число запусков сборки мусора хочется минимизировать.

U>Зачем?

Чтобы из-за паузы, вызванное сборкой мусора, обработка новых данных не замирала.


U>>>2) Выполнение операций над target под локом, запрещающим модификации target.

S>>Тут наверное возможен лок типа читатель-писатель. Предобработка target'ов — это читатели. А хендлеры событий будут запускаться как писатели.

U>Не понял, что имеется в виду.

Код, который непосредственно модифицирует Target'ы — "читатель", вызов обработчика события "писатель".

U>>>3) Если большинству операций нужно не все состояние target, а только его часть. То либо под локом запрещающим модификации target выполняем копирование нужной части target и затем с откопированной частью работаем. Либо разбиваем target на вложенные блоки и для операции выполняем копирование только нужных блоков.

S>>По-сути это путь ФП.
U>И?
Пока что я не очень себе представляю насколько удобно будет описывать "логику" обработки событий с использованием ФП.

В общем надо еще подумать.
Re[9]: Что посоветуете применить в данном примере, м.б. ФП?
От: Undying Россия  
Дата: 03.08.11 15:32
Оценка:
Здравствуйте, sergunok, Вы писали:

S>Чтобы из-за паузы, вызванное сборкой мусора, обработка новых данных не замирала.


Так сколько сборка мусора будет занимать? Есть подозрение, что ее время даже под микроскопом не разглядишь.

S>>>Тут наверное возможен лок типа читатель-писатель. Предобработка target'ов — это читатели. А хендлеры событий будут запускаться как писатели.

S>Код, который непосредственно модифицирует Target'ы — "читатель", вызов обработчика события "писатель".

Что это дает? Ты хочешь раздельные локи для читателей и писателей использовать или что? Если первое, то не получится.

S>Пока что я не очень себе представляю насколько удобно будет описывать "логику" обработки событий с использованием ФП.


Главное достоинство копирования объектов при модификации в том, что лочить надо только обращения к коллекции target'ов. Соответственно элементарно пишется потокобезопасная обертка над этой коллекцией и коду обращающемуся к этой коллекции о потокобезопасности вообще думать не надо. А уж будет код обработки функциональным, императивным или еще каким это уже дело десятое, как удобней так и пиши.

ps
Кстати, число чтений target и число модификаций target'а сильно различается? Если да, то объект можно копировать только при чтении или только при модификации, что может значительно сократить число копирований.
Re: Что посоветуете применить в данном примере, м.б. ФП?
От: VladD2 Российская Империя www.nemerle.org
Дата: 04.08.11 01:37
Оценка:
Здравствуйте, sergunok, Вы писали:

S>Знатоки concurrency, подскажите, как подобный примерчик можно переделать, чтобы этой проблемы не было, но код "логики" при этом читался.. Интуитивно хочется передавать изменения "целей" по свойствам и избавляться от shared state — this.Targets.. А это напоминает тезисы функционального программирования Соответственно вопрос №2 знатокам ФП.. Как примерно и разумно ли переделывать сам TargetReader либо "логику" с помощью языков ФП в данном случае? Как в ФП реализуется shared state?


Мой совет таков — выбросить всю кашу что была приведена выше (поскипана мной), забыть о реализации на конкретном языке и описать задачу на русском языке. При этом постараться описывать именно задачу, а не свое видение ее реализации.

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

ЗЫ

Собственно решая задачи нужно всегда так действовать. Нужно четко понимать, что 99% языков программирования не позволяют реализовать задачу близко к ее реальном описанию. Это значит, что нам сначала нужно решить задачу алгоритмически (придумать как она решается), а потом закодировать ее реализацию на выбранном языке. По сему попытка размышлять о решении задачи на ЯП — это главная ошибка допущенная в программе.
http://nemerle.org/Banners/?g=dark
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Что посоветуете применить в данном примере, м.б. ФП?
От: SV.  
Дата: 04.08.11 07:02
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Собственно решая задачи нужно всегда так действовать. Нужно четко понимать, что 99% языков программирования не позволяют реализовать задачу близко к ее реальном описанию. Это значит, что нам сначала нужно решить задачу алгоритмически (придумать как она решается), а потом закодировать ее реализацию на выбранном языке. По сему попытка размышлять о решении задачи на ЯП — это главная ошибка допущенная в программе.


Согласен со всем, кроме выделенного. По-моему, сначала надо выделить значимые сущности (провести моделирование), а потом уже решать задачу алгоритмически. То есть, говоря на UML'е, сначала надо роли нарисовать, а потом уже вести от них палки таймлайнов к низу и обозначать сиквенс (алгоритм). Классы Target и TargetReader наводят на мысль, что где-то при моделировании не все было продумано. Каких только классов не доводилось видеть — и Settings {...от кита до кота...}, и QuickSorter, и SuperDrawer — сдается мне, что Target из той же оперы. Другая мысль — очень похоже на плагин к имеющейся суперабстрактной системе бухгалтерии или документооборота, там обычно этих Activity, Target и прочего — как собак нерезанных. Тогда моделирование провели за тебя, и надо смириться.
Re[3]: Что посоветуете применить в данном примере, м.б. ФП?
От: VladD2 Российская Империя www.nemerle.org
Дата: 04.08.11 13:42
Оценка:
Здравствуйте, SV., Вы писали:

SV.>Здравствуйте, VladD2, Вы писали:


VD>>Собственно решая задачи нужно всегда так действовать. Нужно четко понимать, что 99% языков программирования не позволяют реализовать задачу близко к ее реальном описанию. Это значит, что нам сначала нужно решить задачу алгоритмически (придумать как она решается), а потом закодировать ее реализацию на выбранном языке. По сему попытка размышлять о решении задачи на ЯП — это главная ошибка допущенная в программе.


SV.>Согласен со всем, кроме выделенного. По-моему, сначала надо выделить значимые сущности (провести моделирование), а потом уже решать задачу алгоритмически.


Вот это ваше "выделить значимые сущности (провести моделирование)" — это один из способов решения задачи алгоритмически. Под "алгоритмически" я имел в виду — "понять как решать задачу". А что делать чтобы это понять — дело десятое.

SV.>То есть, говоря на UML'е, сначала надо роли нарисовать, а потом уже вести от них палки таймлайнов к низу и обозначать сиквенс (алгоритм).


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

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

SV.>Классы Target и TargetReader наводят на мысль, что где-то при моделировании не все было продумано.


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

SV.>Каких только классов не доводилось видеть — и Settings {...от кита до кота...}, и QuickSorter, и SuperDrawer — сдается мне, что Target из той же оперы.


Вот именно. И чтобы понять нужны ли они вообще, нужно для начала описать задачу.

SV.> Другая мысль — очень похоже на плагин к имеющейся суперабстрактной системе бухгалтерии или документооборота, там обычно этих Activity, Target и прочего — как собак нерезанных. Тогда моделирование провели за тебя, и надо смириться.


Ну, и на фиг эти гадания на кофейной гуще? У нас тут клуб тупорылых малолетних девиц?
http://nemerle.org/Banners/?g=dark
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.