информационная безопасность
без паники и всерьез
 подробно о проектеRambler's Top100
Страшный баг в WindowsПортрет посетителяАтака на Internet
BugTraq.Ru
Русский BugTraq
 Анализ криптографических сетевых... 
 Модель надежности двухузлового... 
 Специальные марковские модели надежности... 
 Ростелеком заподозрили в попытке... 
 Линуксовый ботнет, распространяющийся... 
 Конец поддержки Internet Explorer 
главная обзор RSN блог библиотека закон бред форум dnet о проекте
bugtraq.ru / форум / programming
Имя Пароль
ФОРУМ
если вы видите этот текст, отключите в настройках форума использование JavaScript
регистрация





Легенда:
  новое сообщение
  закрытая нитка
  новое сообщение
  в закрытой нитке
  старое сообщение
  • Напоминаю, что масса вопросов по функционированию форума снимается после прочтения его описания.
  • Новичкам также крайне полезно ознакомиться с данным документом.
Не стал пока делать 20.01.08 05:31  Число просмотров: 4225
Автор: void <Grebnev Valery> Статус: Elderman
<"чистая" ссылка>
Не стал пока делать
" ...схему передачи владения (или совместного владения):...", равно как и использованье "стандартных" memory pool. Сделал простой массив назеранных кусков преадллокированного буфера.
Возможо, что написанный класс CMemoryArray далеко неоптимален, но получилось, наверное, то что и должно было получиться. Увеличение производительности не более 3%.
Три процента, всё же лучше чем ничего, хотя вцелом код с использование CMemoryArray получается более корявым по сравнению с new/delete.
<programming>
[Win32] Две проблемы с передачей сообщения thred-у 28.12.07 09:20  
Автор: void <Grebnev Valery> Статус: Elderman
<"чистая" ссылка>
Подскажите пожалуйста бест практис о следующем.
Приложение Publisher посылает обновление приложению Router. Roter пересылает это сообщение вначале в свой workthread (где сообщение обрабатывается), и далее пересылается приложению Subscriber:

HRESULT Router::OnPublisherPush(const string& skey1, const string& skey2, long key3, char* data, size_t len )
{
// Pack the publisher message
ConcreteTopicA* topicData = new ConcreteTopicA(INVALIDE_TOPIC_ID, skey1, skey2, key3);
memmove(topicData->m_data, data, len);

// Queue message to the Router thread
if (!::PostThreadMessage(m_RouterMsgThreadID, WM_USER_ON_PUBLISHER_PUSH, 0, (LPARAM) topicData ) )
{
DWORD err = GetLastError();
delete topicData;
if ( ERROR_NOT_ENOUGH_QUOTA == err ) {
std::cout << "\nERROR_NOT_ENOUGH_QUOTA" << std::endl;
g_cnt_lost ++;
//::SwitchToThread();
}
return E_FAIL;
}
//::Sleep(0);
return S_OK;
}

В рабочем потоке это сообщение, topicData, распаковывается в transmittedTopic, обрабатывается и удаляется delete transmittedTopic:

DWORD WINAPI Router::RouterMessageThreadProc( LPVOID lpParameter )
{
MSG msg;
// Create the message queue
...
...
BOOL bRet;
while( (bRet = ::GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet != -1 error)
{
if (WM_TIMER == msg.message) {
...
...
}
}
else if (WM_USER_ON_PUBLISHER_PUSH == msg.message) {
//Unpack the message
ConcreteTopicA* transmittedTopic = (ConcreteTopicA*) msg.lParam;

// TO DO
...
...

delete transmittedTopic;
...
...
...

Первая проблема, в том, что для обработки сообщения внутри RouterMessageThreadProc надо:
1) создать структуру ConcreteTopicA* topicData = new ConcreteTopicA
2) скопировать данные memmove из transmittedTopic для последующей обработки
3) удалить структуру delete transmittedTopic;
Всё это (new, memmove, delete) может быть накладно и долго как интерфейса Publisher, так и для самого Router. Кроме того, поскольку Publisher может весьма интенсивно обновлять данные, это может фрагментировать память и снизить общий перформанс.


Вторая проблема в том, что при интенсивной посылке сообщений в поток, часть сообщений теряется из очереди из-за её переполнения (GetLastError ==ERROR_NOT_ENOUGH_QUOTA ). Теряется приблизительно 50-80 сообщений из 1000 000. Изменение, размера очереди потока по умолчанию (10000 для W2k/XP/W2k3) - не подходит. Кроме того? при переполнении очереди появляется другая, вполне ожидаемая и более существенная проблема - сообщения по таймеру потока (WM_TIMER) вообще теряют всякий смысл.

По поводу первой проблемы - смутно мелькает такая мысль: данные Publisher копировать в преаллокированный глобальный массив, а в поток передавать индекс элемента этого массива. Массив никогда не очищать и не освобождать. Для синхронизации сообщений дополнительно использовать вектор индексов "свободных фреймов" памяти.

По поводу второй проблемы - не мелькает никакая мысль, даже смутно.

Может ли кто-нибудь помочь советом, примером?
Спасибо.
По поводу new/delete самого ConcreteTopic-а: можно... 28.12.07 15:22  
Автор: amirul <Serge> Статус: The Elderman
<"чистая" ссылка>
> Первая проблема, в том, что для обработки сообщения внутри
> RouterMessageThreadProc надо:
> 1) создать структуру ConcreteTopicA* topicData = new
> ConcreteTopicA
> 2) скопировать данные memmove из transmittedTopic для
> последующей обработки
> 3) удалить структуру delete transmittedTopic;
> Всё это (new, memmove, delete) может быть накладно и долго
> как интерфейса Publisher, так и для самого Router. Кроме
> того, поскольку Publisher может весьма интенсивно обновлять
> данные, это может фрагментировать память и снизить общий
> перформанс.

По поводу new/delete самого ConcreteTopic-а: можно организовать пул (пример пула насколько я помню есть у страуструпа в его C++ Programming Language). Идея в том, чтобы выделить некоторое количество памяти, порезать ее на одинаковые куски и связать куски в односвязный список. Выделение и освобождение памяти в этом пуле - простейшие операции добавления и удаления элемента в голову списка и имеют сложность O(const). Если надо поддержка увеличения, то можно выделять chunk-ами по. В принципе и писать то ничего не надо, все уже есть в boost::pool и в Loki::SmallObjAllocator. Причем решается одновременно и проблема оверхеда по времени и проблема фрагментации памяти.

Что до memmove, то я бы предпочел использовать какую нибудь схему передачи владения (или совместного владения): std::auto_ptr, boost::shared_ptr, boost::intrusive_ptr, Loki::SmartPtr и т.п.. Вместо копирования передать владение (выделить в одном месте, удалить в другом).

> Вторая проблема в том, что при интенсивной посылке
> сообщений в поток, часть сообщений теряется из очереди
> из-за её переполнения (GetLastError
> ==ERROR_NOT_ENOUGH_QUOTA ). Теряется приблизительно 50-80
> сообщений из 1000 000. Изменение, размера очереди потока по
> умолчанию (10000 для W2k/XP/W2k3) - не подходит. Кроме
> того? при переполнении очереди появляется другая, вполне
> ожидаемая и более существенная проблема - сообщения по
> таймеру потока (WM_TIMER) вообще теряют всякий смысл.

А это обязательно использовать стандартные виндовые сообщения?
Как уже написали, самопальная FIFO (std::list или даже std::deque) будет гораздо эффективнее и при этом будет уверенность, что потерявшихся сообщений не будет.
Не стал пока делать 20.01.08 05:31  
Автор: void <Grebnev Valery> Статус: Elderman
<"чистая" ссылка>
Не стал пока делать
" ...схему передачи владения (или совместного владения):...", равно как и использованье "стандартных" memory pool. Сделал простой массив назеранных кусков преадллокированного буфера.
Возможо, что написанный класс CMemoryArray далеко неоптимален, но получилось, наверное, то что и должно было получиться. Увеличение производительности не более 3%.
Три процента, всё же лучше чем ничего, хотя вцелом код с использование CMemoryArray получается более корявым по сравнению с new/delete.
Приблизительно это я имел ввиду. У Страуструпа не помню... 31.12.07 07:01  
Автор: void <Grebnev Valery> Статус: Elderman
<"чистая" ссылка>
> По поводу new/delete самого ConcreteTopic-а: можно
> организовать пул (пример пула насколько я помню есть у
> страуструпа в его C++ Programming Language). Идея в том,
> чтобы выделить некоторое количество памяти, порезать ее на
> одинаковые куски и связать куски в односвязный список.
> Выделение и освобождение памяти в этом пуле - простейшие
> операции добавления и удаления элемента в голову списка и
> имеют сложность O(const).

Приблизительно это я имел ввиду. У Страуструпа не помню такого. Попробую в инете поискать достойные прототипы. В принципе ясно, как это должно бы работать.

> Если надо поддержка увеличения,
> то можно выделять chunk-ами по.

Поддержка увеличения не нужна. По крайне мере так видится сейчас.

> А это обязательно использовать стандартные виндовые
> сообщения?
> Как уже написали, самопальная FIFO (std::list или даже
> std::deque) будет гораздо эффективнее и при этом будет
> уверенность, что потерявшихся сообщений не будет.

Не обязательно в принципе. Но в этом проекте, другие девелоперы используют виндовую очередь. Я только делаю там свой кусок. Так сложилось исторически (попросту был копипастнут кусок майкрософтовского примера RTD сервера). То, что и в других критических по производительности проектах виндовая очередь ведёт себя плохо - это факт. Местная публика лечит это увеличением размера очереди до 100 000. На мой вкус это лажа - приложение делает вид, что работает. Масштабируемости 0. Мульпроцессорной обработки - думаю, не больше.

Отчасти (как временное решение ?) можно подлечить проблему потери сообщений, если после посылки сообщения переключать контекст потока ::SwitchToThread(), см. ниже. Тогда в рабочем потоке мы "успеваем" вытащить элемент до переполнения очереди. По крайней мере в стресс-тестах (~20000 месаджей в секунду) сообщения не теряются:

HRESULT Router::OnPublisherPush(const string& skey1, const string& skey2, long key3, char* data, size_t len )
{
// Pack the publisher message
ConcreteTopicA* topicData = new ConcreteTopicA(INVALIDE_TOPIC_ID, skey1, skey2, key3);
...
if (!::PostThreadMessage(g_RouterMsgThreadID, WM_USER_ON_PUBLISHER_PUSH, 0, (LPARAM) topicData ) ) {
delete topicData;
...
return E_FAIL;
}
::SwitchToThread();
return S_OK;
}

Здесь две проблемы: как сам понимаешь, "улучшение синхронизации" при помощи Sleep(0) - полная лажа и никаких гарантий на будущее, поскольку проблема по существу не решается.
Вторая проблема - интерфейс Router::OnPublisherPush может быть синхроный (он таковым сейчас пока и является COM/DCOM). Поэтому, решение со ::SwitchToThread() до завершения функции Router::OnPublisherPush будет держать Publisher. А это пострашнее любых задержек и потерь месаджей на Router-е. Publisher выполняет основную работу в системе.
Короче, надо тестировать всю систему, чтобы сказать можно ли использовать ::SwitchToThread(), или нет с точки зрения производительности приложения Publisher.

Спасибо тебе за мнение, Сергей.
С Новым Годом!
У Страуструпа это было в 19.4.2 Распределители памяти,... 02.01.08 14:38  
Автор: amirul <Serge> Статус: The Elderman
<"чистая" ссылка>
> Приблизительно это я имел ввиду. У Страуструпа не помню
> такого. Попробую в инете поискать достойные прототипы. В
> принципе ясно, как это должно бы работать.

У Страуструпа это было в 19.4.2 Распределители памяти, определяемые пользователем, а достойные прототипы я уже привел: в частности я бы использовал boost::pool
Уж лучше самодельная очередь... 02.01.08 07:36  
Автор: void <Grebnev Valery> Статус: Elderman
<"чистая" ссылка>
> Здесь две проблемы: как сам понимаешь, "улучшение
> синхронизации" при помощи Sleep(0) - полная лажа и никаких
> гарантий на будущее, поскольку проблема по существу не
> решается.

Смешно... Получается с небольшой потерей производительности (~7%) защитить виндовую очередь от переполнения при помощи семафора, инициализируемого ~ размером очереди из реестра виндовз. Такой вот конгломерат - очередь виндовз с семафором ;))
Для передачи сообщений потокам хорошо использовать IOCP или... 28.12.07 11:51  
Автор: IgorR <Igor Razin> Статус: Member
<"чистая" ссылка>
Для передачи сообщений потокам хорошо использовать IOCP или самодельную FIFO очередь.
В части самодельной FIFO - ну что тут скажешь ... понятно. 31.12.07 08:11  
Автор: void <Grebnev Valery> Статус: Elderman
<"чистая" ссылка>
> Для передачи сообщений потокам хорошо использовать IOCP или
> самодельную FIFO очередь.
В части самодельной FIFO - ну что тут скажешь ... понятно.

В части IOCP для не I/O запросов. Поправьте, если я не прав (я слабый программист) -
на мой взгляд, это наиболее простое и ясное программирование для Windows для тех задач, где это можно применить. Есть и подводные камни и вопросы, которые придётся отстаивать:

1) для производительных систем (более 50 000 не I/O запросов в секунду, до 100 000) использование простых решений "всё в одном флаконе", типа QueueUserWorkItemможет_не_работать даже для минимального кол-ва потоков WT_EXECUTEINPERSISTENTTHREAD, если после посылки элемента queue в очередь IOCP не переключать контекст ::SwitchToThread(), типа:

static HRESULT OnPublisherPush(const string& skey1, const string& skey2, long key3, char* data, size_t len )
{
// Pack the publisher message
ConcreteTopicA* topicData = new ConcreteTopicA(INVALIDE_TOPIC_ID, skey1, skey2, key3);
...
// Queue message to the IOCP
if (!::QueueUserWorkItem(Router::RouterWorkItemProc, (PVOID) topicData, WT_EXECUTEINPERSISTENTTHREAD /*1 thread*/ ) )
delete topicData;
...
return E_FAIL;
}
...
::SwitchToThread();
return S_OK;
}

Причина в том, FIFO запросов IOCP забивается полностью. Реально при 1000 элементах очереди производительность снижается в разы. При 10 000 и более - может и reboot компа понадобтся. Увеличение числа потоков на порту (WT_EXECUTEDEFAULT, WT_EXECUTELONGFUNCTION) только ухудшат ситуацию, если посылки в порт запроса не переключиться ::SwitchToThread().

2) Использование контроля количества потоков и размера очереди запросов IOCP () несомненно улучшает ситуацию. Трюк в том, что можно переключаться ::SwitchToThread() не каждый раз после посылки очередного месаджа, когда очередь достигает N - элементов. Реально, у меня получается оптимальное число N ~ 100-250 и должно быть разным на разном хардваре:

static HRESULT OnPublisherPush(const string& skey1, const string& skey2, long key3, char* data, size_t len )
{
// Pack the publisher message
ConcreteTopicA* topicData = new ConcreteTopicA(INVALIDE_TOPIC_ID, skey1, skey2, key3);
...
// Queue message to the IOCP
if (!::PostQueuedCompletionStatus( g_hIOCP, 0, 0, (OVERLAPPED*)topicData )) {

delete topicData;
...
return E_FAIL;
}
...

if (GetIoCompletionQueueLen(g_hIOCP) > 250)
::SwitchToThread();
return S_OK;
}

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

Мне IOCP больше нравится для многопроцессорной обработки, чем самодельный FIFO. Тем более, что "самодельный" нормальный пул потоков (работающий с этой FIFO) с контролем их состояний - очень трудная задача.

Спасибо, Игорь за мнение. С Новым Годом!
а использование windows messages принципиально? 28.12.07 10:49  
Автор: dl <Dmitry Leonov>
<"чистая" ссылка>
> По поводу первой проблемы - смутно мелькает такая мысль:
> данные Publisher копировать в преаллокированный глобальный
> массив, а в поток передавать индекс элемента этого массива.
> Массив никогда не очищать и не освобождать. Для
> синхронизации сообщений дополнительно использовать вектор
> индексов "свободных фреймов" памяти.
> По поводу второй проблемы - не мелькает никакая мысль, даже
> смутно.


Если с сообщением будет передаваться только индекс очередного элемента, то может быть это оформить как event-объект ядра, который только фиксирует факт увеличения индекса. И даже если не хватит времени обрабатывать каждое событие, если видно, что индекс увеличился на n по сравнению с прошлым разом, эти n сообщений и обрабатывать.
Использование windows messages не принципиально на мой... 31.12.07 07:22  
Автор: void <Grebnev Valery> Статус: Elderman
<"чистая" ссылка>
Использование windows messages не принципиально на мой взгляд. Это просто существующий дизайн, к которому привыкли местные. Если зайдёт в тупик, то буду отстаивать другие решения (пока не решил какие).

> Если с сообщением будет передаваться только индекс
> очередного элемента, то может быть это оформить как
> event-объект ядра, который только фиксирует факт увеличения
> индекса.

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

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

Если правильно понял тебя, то ты попал в яблочка. Последние обновления - самые важные. Их и надо обрабатывать. Если по какой-то причине пропущены предудущие месаджи - не беда.

Спасибо. С Новым годом! Всех тебе благ, Дмитрий, в новом году!
примерно то же, что говорилось в других ответах 31.12.07 12:50  
Автор: dl <Dmitry Leonov>
<"чистая" ссылка>
> > Если с сообщением будет передаваться только индекс
> > очередного элемента, то может быть это оформить как
> > event-объект ядра, который только фиксирует факт
> увеличения
> > индекса.
> Поясни, пожалуйта, детали. Просто чувствую, что здесь может
> быть очень неплохое зерно.
> > И даже если не хватит времени обрабатывать каждое
> > событие, если видно, что индекс увеличился на n по
> > сравнению с прошлым разом, эти n сообщений и
> обрабатывать.
> Если правильно понял тебя, то ты попал в яблочка. Последние
> обновления - самые важные. Их и надо обрабатывать. Если по
> какой-то причине пропущены предудущие месаджи - не беда.

Использовать для передачи собственно данных некую очередь в глобальной или разделяемой (если общение между разными процессами) памяти. С сообщением передавать индекс очередного элемента этой очереди - либо в данных сообщения (если это windows message), либо опять же через глобальную/разделяемую память (если объект ядра). Хранить индекс последнего обработанного сообщения, при приходе очередного проверять, случились ли пробелы в обработке - если передавать индекс новой порции данных через глобальную/разделяемую память, на обработку всегда будет поступать последний из переданных. А дальше уже решать, что делать с пропущенными и на какую глубину.

> Спасибо. С Новым годом! Всех тебе благ, Дмитрий, в новом
> году!

Аналогично :)
1




Rambler's Top100
Рейтинг@Mail.ru


  Copyright © 2001-2022 Dmitry Leonov   Page build time: 0 s   Design: Vadim Derkach