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





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

У Страуструпа это было в 19.4.2 Распределители памяти, определяемые пользователем, а достойные прототипы я уже привел: в частности я бы использовал boost::pool
<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-2024 Dmitry Leonov   Page build time: 0 s   Design: Vadim Derkach