CSocket :: Send是否存在性能问题?

时间:2011-06-10 17:27:33

标签: c++ c sockets visual-c++ concurrency

2011年6月14日更新

快速更新......大多数受访者都专注于处理要记录的消息队列的狡猾方法,但是当然缺乏优化时,它肯定不是问题的根源。我们将Yield转换为短暂的睡眠(是的,一旦系统安静,Yield确实会产生100%的CPU)但是系统仍然无法跟上日志记录,即使它没有接近睡眠状态。从我所看到的发送只是不是非常有效。一位受访者评论说我们应该将Send()一起阻塞到一个发送中,这似乎是对更大的潜在问题的最合适的解决方案,这就是为什么我将此标记为原始问题的答案。我当然同意队列模型存在很大的缺陷,所以感谢对此的反馈,我已经投了所有为讨论做出贡献的答案。

然而,这个练习让我们回顾为什么我们正在使用像我们这样的套接字上的外部日志记录,虽然之前当日志记录服务器做了很多事情时它可能已经有意义了处理日志条目...它不再执行任何操作,因此我们选择远程访问整个模块并通过一些预先存在的日志记录框架寻求直接到文件的方法,这应该完全消除问题以及消除系统中不必要的复杂性。

再次感谢所有反馈。

原始问题

在我们的系统中,我们有两个对这个问题很重要的组件 - 一个是用Visual C ++开发的,另一个是Java(不要问,历史原因)。

C ++组件是主要服务并生成日志条目。这些日志条目通过CSocket :: Send out发送到Java日志服务。

问题

发送数据的性能似乎非常低。如果我们在C ++端排队,那么队列将在繁忙的系统上逐步备份。

如果我使用一个简单的C#应用​​程序访问Java Logging Server,那么我可以更快地锤击它,然后我将需要使用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的限制吗?这对我们来说不是很有用吗?误用了吗?或者整个红鲱鱼,问题出在其他地方?

非常感谢您的建议。

亲切的问候

Matt Peddlesden

4 个答案:

答案 0 :(得分:3)

好吧,你可以先使用阻塞生产者 - 消费者队列,然后摆脱'收益'。消息被阻塞我并不感到惊讶 - 当一个消息发布时,记录器线程通常是在繁忙的系统上准备好但没有运行。这将在处理队列上的任何消息之前引入大量可避免的延迟。后台线程比有一个量子试图摆脱队列中累积的所有消息。如果繁忙的系统上有很多就绪线程,那么线程可能没有足够的时间来处理消息。特别是如果已经建立了很多并且socket.send阻塞。

此外,在队列轮询中几乎完全浪费一个CPU内核可能不利于整体性能。

RGDS, 马丁

答案 1 :(得分:1)

这里的事情可能会减慢你的速度:

  • 您正在使用的队列。我认为这是过早优化的典型示例。这里没有理由使用Concurrency :: concurrent_queue类,而不是使用阻塞pop()方法的常规消息队列。如果我理解正确,并发类使用非阻塞算法,在这种情况下,您确实希望在队列为空时阻塞并释放CPU以供其他线程使用。
  • 对每条消息使用new和delete以及CString类的内部分配。您应该尝试查看回收消息和字符串(使用池)是否有助于提高性能有两个原因:1。消息和字符串对象的分配和释放。 2.如果字符串类将在内部循环其缓冲区,则可以避免在字符串内完成的分配和释放。

答案 2 :(得分:1)

在我看来,你绝对不是在寻找最有效的解决方案。你一定应该调用Send()一次。对于所有消息。连接用户端队列中的所有消息,使用Send(),然后 yield一次性发送所有消息。

此外,这真的不是你的意思。 PPL包含明确用于异步回调的构造 - 如call对象。你应该使用它而不是自己动手。

答案 3 :(得分:0)

您是否尝试过分析以查看应用程序遇到问题的位置?是仅在记录发件人存在问题时?是CPU绑定还是阻塞?

我唯一能看到的是你没有用任何类型的锁来保护消息队列,所以容器状态可能会变得奇怪,导致各种意外行为。