Есть ли проблемы с производительностью в CSocket :: Send? - PullRequest
3 голосов
/ 10 июня 2011

ОБНОВЛЕНИЕ 14 июня 2011

Быстрое обновление ... Большинство респондентов сконцентрировались на хитром методе обработки очереди сообщений, которые должны регистрироваться, однако, хотя там, безусловно, не хватает оптимизации, это, конечно, не является корнем проблемы. Мы переключили Yield на короткий сон (да, выход действительно привел к 100% CPU, когда система отключилась), однако система все еще не может идти в ногу с журналированием, даже если она не приближается к этому сну. Из того, что я вижу, отправка просто не очень эффективна. Один респондент отметил, что мы должны объединить Send () в одну отправку, и это кажется наиболее подходящим решением для основной проблемы, и именно поэтому я отметил это как ответ на первоначальный вопрос. Я, конечно, согласен с тем, что модель очереди очень ошибочна, поэтому спасибо за отзывы об этом, и я проголосовал за все ответы, которые внесли вклад в обсуждение.

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

Еще раз спасибо за все отзывы.

ОРИГИНАЛЬНЫЙ ВОПРОС

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

Компонент C ++ является основным сервисом и генерирует записи в журнале. Эти записи журнала отправляются через CSocket :: Send to Java logging service.

Проблема

Производительность отправки данных кажется очень низкой. Если мы ставим в очередь на стороне C ++, то эта очередь постепенно резервируется в загруженных системах.

Если я попаду на сервер журналирования Java с помощью простого приложения на C #, то смогу справиться с этим быстрее, чем когда-либо, с помощью инструмента C ++, и он будет прекрасно работать.

В мире C ++ функция, добавляющая сообщения в очередь:

void MyLogger::Log(const CString& buffer)
{
    struct _timeb timebuffer;
    _ftime64_s( &timebuffer );

    CString message;
    message.Format("%d%03d,%04d,%s\r\n", (int)timebuffer.time, (int)timebuffer.millitm, GetCurrentThreadId(), (LPCTSTR)buffer);

    CString* queuedMessage = new CString(message);
    sendMessageQueue.push(queuedMessage);
}

Функция запускается в отдельном потоке, который отправляет в сокет:

void MyLogger::ProcessQueue()
{
    CString* queuedMessage = NULL;
    while(!sendMessageQueue.try_pop(queuedMessage))
    {
        if (!running)
        {
            break;
        }
        Concurrency::Context::Yield();
    }

    if (queuedMessage == NULL)
    {
        return;
    }
    else
    {
        socket.Send((LPCTSTR)*queuedMessage, queuedMessage->GetLength());
        delete queuedMessage;
    }
}

Обратите внимание, что ProcessQueue многократно запускается самим потоком внешнего цикла, что исключает кучу бессмысленной преамбулы:

while(parent->running)
{
    try
    {
        logger->ProcessQueue();
    }
    catch(...)
    {
    }
}

Очередь:

Concurrency::concurrent_queue<CString*> sendMessageQueue;

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

Это ограничение CSocket :: Send, которое делает его менее полезным для нас? Неправильное использование этого? Или целая красная сельдь и проблема в другом?

Ваш совет очень ценится.

С уважением

Мэтт Педлсден

Ответы [ 4 ]

3 голосов
/ 10 июня 2011

Ну, вы могли бы начать с использования блокирующей очереди производитель-потребитель и избавиться от «Доходности».Я не удивлен, что сообщения блокируются - когда сообщение публикуется, поток журнала, как правило, в загруженной системе готов, но не работает.Это обеспечит большую задержку, которую можно избежать, прежде чем любое сообщение в очереди сможет быть обработано.Фоновый поток имеет квант, чтобы попытаться избавиться от всех сообщений, накопленных в очереди.Если в занятой системе много готовых потоков, вполне возможно, что потоку просто не хватает времени для обработки сообщений.особенно, если много накопилось, и socket.send блокируется.

Кроме того, почти полная потеря одного ядра ЦП при опросе очереди не может быть хорошей для общей производительности.

Rgds, Martin

1 голос
/ 11 июня 2011

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

Кроме того, это действительно не то, как вы должны это сделать. PPL содержит конструкции, явно предназначенные для асинхронных обратных вызовов, например, объект call. Вы должны использовать это вместо того, чтобы раскатывать свои собственные.

1 голос
/ 10 июня 2011

Вещи, которые могут вас замедлить:

  • Очередь, которую вы используете. Я думаю, что это классический пример преждевременной оптимизации.Здесь нет причин использовать класс Concurrency :: concurrent_queue, а не обычную очередь сообщений с блокирующим методом pop ().Если я правильно понимаю, классы Concurrency используют неблокирующие алгоритмы, когда в этом случае вы хотите заблокировать, когда очередь пуста, и освободить ЦП для использования другими потоками.
  • Использование новыхи удалите для каждого сообщения и внутренних распределений класса CString. Вы должны попытаться выяснить, поможет ли переработка сообщений и строк (с использованием пула) повысить производительность по двум причинам: 1. Распределение и освобождение сообщений истроковые объекты.2. Распределения и освобождения, выполняемые внутри строк, можно избежать, если строковый класс будет внутренне перерабатывать свои буферы.
0 голосов
/ 10 июня 2011

Вы пробовали профилирование, чтобы увидеть, где у вашего приложения возникли проблемы? Только при входе в систему возникают проблемы с отправителем? Это процессор связан или блокируется?

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...