Changeset 129239 in webkit


Ignore:
Timestamp:
Sep 21, 2012 10:28:24 AM (12 years ago)
Author:
commit-queue@webkit.org
Message:

[WebSocket] Receiving a large message is really slow
https://bugs.webkit.org/show_bug.cgi?id=97237

Patch by Evan Wallace <evan.exe@gmail.com> on 2012-09-21
Reviewed by Alexey Proskuryakov.

WebSocketChannel always reallocates its internal buffer when it receives
and appends new data which causes dramatic slowdowns for messages over
2 MB in size. This patch changes the internal buffer of WebSocketChannel
from a raw char array to a Vector<char> and uses its amortized append()
method. This brings the time to receive a 5 MB message from 5.2 seconds
to 0.25 seconds.

This patch is only for optimization. No new tests are needed.

  • Modules/websockets/WebSocketChannel.cpp:

(WebCore::WebSocketChannel::WebSocketChannel):
(WebCore::WebSocketChannel::~WebSocketChannel):
(WebCore::WebSocketChannel::fail):
(WebCore::WebSocketChannel::resume):
(WebCore::WebSocketChannel::didReceiveSocketStreamData):
(WebCore::WebSocketChannel::appendToBuffer):
(WebCore::WebSocketChannel::skipBuffer):
(WebCore::WebSocketChannel::processBuffer):
(WebCore::WebSocketChannel::resumeTimerFired):
(WebCore::WebSocketChannel::processFrame):

  • Modules/websockets/WebSocketChannel.h:
Location:
trunk/Source/WebCore
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r129236 r129239  
     12012-09-21  Evan Wallace  <evan.exe@gmail.com>
     2
     3        [WebSocket] Receiving a large message is really slow
     4        https://bugs.webkit.org/show_bug.cgi?id=97237
     5
     6        Reviewed by Alexey Proskuryakov.
     7
     8        WebSocketChannel always reallocates its internal buffer when it receives
     9        and appends new data which causes dramatic slowdowns for messages over
     10        2 MB in size. This patch changes the internal buffer of WebSocketChannel
     11        from a raw char array to a Vector<char> and uses its amortized append()
     12        method. This brings the time to receive a 5 MB message from 5.2 seconds
     13        to 0.25 seconds.
     14
     15        This patch is only for optimization. No new tests are needed.
     16
     17        * Modules/websockets/WebSocketChannel.cpp:
     18        (WebCore::WebSocketChannel::WebSocketChannel):
     19        (WebCore::WebSocketChannel::~WebSocketChannel):
     20        (WebCore::WebSocketChannel::fail):
     21        (WebCore::WebSocketChannel::resume):
     22        (WebCore::WebSocketChannel::didReceiveSocketStreamData):
     23        (WebCore::WebSocketChannel::appendToBuffer):
     24        (WebCore::WebSocketChannel::skipBuffer):
     25        (WebCore::WebSocketChannel::processBuffer):
     26        (WebCore::WebSocketChannel::resumeTimerFired):
     27        (WebCore::WebSocketChannel::processFrame):
     28        * Modules/websockets/WebSocketChannel.h:
     29
    1302012-09-21  Andrey Adaikin  <aandrey@chromium.org>
    231
  • trunk/Source/WebCore/Modules/websockets/WebSocketChannel.cpp

    r124846 r129239  
    7474    : m_document(document)
    7575    , m_client(client)
    76     , m_buffer(0)
    77     , m_bufferSize(0)
    7876    , m_resumeTimer(this, &WebSocketChannel::resumeTimerFired)
    7977    , m_suspended(false)
     
    9896WebSocketChannel::~WebSocketChannel()
    9997{
    100     fastFree(m_buffer);
    10198}
    10299
     
    207204    RefPtr<WebSocketChannel> protect(this); // The client can close the channel, potentially removing the last reference.
    208205    m_shouldDiscardReceivedData = true;
    209     if (m_buffer)
    210         skipBuffer(m_bufferSize); // Save memory.
     206    if (!m_buffer.isEmpty())
     207        skipBuffer(m_buffer.size()); // Save memory.
    211208    m_deflateFramer.didFail();
    212209    m_hasContinuousFrame = false;
     
    239236{
    240237    m_suspended = false;
    241     if ((m_buffer || m_closed) && m_client && !m_resumeTimer.isActive())
     238    if ((!m_buffer.isEmpty() || m_closed) && m_client && !m_resumeTimer.isActive())
    242239        m_resumeTimer.startOneShot(0);
    243240}
     
    313310        return;
    314311    }
    315     while (!m_suspended && m_client && m_buffer)
     312    while (!m_suspended && m_client && !m_buffer.isEmpty())
    316313        if (!processBuffer())
    317314            break;
     
    393390bool WebSocketChannel::appendToBuffer(const char* data, size_t len)
    394391{
    395     size_t newBufferSize = m_bufferSize + len;
    396     if (newBufferSize < m_bufferSize) {
    397         LOG(Network, "WebSocket buffer overflow (%lu+%lu)", static_cast<unsigned long>(m_bufferSize), static_cast<unsigned long>(len));
    398         return false;
    399     }
    400     char* newBuffer = 0;
    401     if (!tryFastMalloc(newBufferSize).getValue(newBuffer))
    402         return false;
    403 
    404     if (m_buffer)
    405         memcpy(newBuffer, m_buffer, m_bufferSize);
    406     memcpy(newBuffer + m_bufferSize, data, len);
    407     fastFree(m_buffer);
    408     m_buffer = newBuffer;
    409     m_bufferSize = newBufferSize;
     392    size_t newBufferSize = m_buffer.size() + len;
     393    if (newBufferSize < m_buffer.size()) {
     394        LOG(Network, "WebSocket buffer overflow (%lu+%lu)", static_cast<unsigned long>(m_buffer.size()), static_cast<unsigned long>(len));
     395        return false;
     396    }
     397    m_buffer.append(data, len);
    410398    return true;
    411399}
     
    413401void WebSocketChannel::skipBuffer(size_t len)
    414402{
    415     ASSERT(len <= m_bufferSize);
    416     m_bufferSize -= len;
    417     if (!m_bufferSize) {
    418         fastFree(m_buffer);
    419         m_buffer = 0;
    420         return;
    421     }
    422     memmove(m_buffer, m_buffer + len, m_bufferSize);
     403    ASSERT(len <= m_buffer.size());
     404    memmove(m_buffer.data(), m_buffer.data() + len, m_buffer.size() - len);
     405    m_buffer.resize(m_buffer.size() - len);
    423406}
    424407
     
    427410    ASSERT(!m_suspended);
    428411    ASSERT(m_client);
    429     ASSERT(m_buffer);
    430     LOG(Network, "WebSocketChannel %p processBuffer %lu", this, static_cast<unsigned long>(m_bufferSize));
     412    ASSERT(!m_buffer.isEmpty());
     413    LOG(Network, "WebSocketChannel %p processBuffer %lu", this, static_cast<unsigned long>(m_buffer.size()));
    431414
    432415    if (m_shouldDiscardReceivedData)
     
    434417
    435418    if (m_receivedClosingHandshake) {
    436         skipBuffer(m_bufferSize);
     419        skipBuffer(m_buffer.size());
    437420        return false;
    438421    }
     
    441424
    442425    if (m_handshake->mode() == WebSocketHandshake::Incomplete) {
    443         int headerLength = m_handshake->readServerHandshake(m_buffer, m_bufferSize);
     426        int headerLength = m_handshake->readServerHandshake(m_buffer.data(), m_buffer.size());
    444427        if (headerLength <= 0)
    445428            return false;
     
    457440            skipBuffer(headerLength);
    458441            m_client->didConnect();
    459             LOG(Network, "remaining in read buf %lu", static_cast<unsigned long>(m_bufferSize));
    460             return m_buffer;
     442            LOG(Network, "remaining in read buf %lu", static_cast<unsigned long>(m_buffer.size()));
     443            return !m_buffer.isEmpty();
    461444        }
    462445        ASSERT(m_handshake->mode() == WebSocketHandshake::Failed);
     
    478461
    479462    RefPtr<WebSocketChannel> protect(this); // The client can close the channel, potentially removing the last reference.
    480     while (!m_suspended && m_client && m_buffer)
     463    while (!m_suspended && m_client && !m_buffer.isEmpty())
    481464        if (!processBuffer())
    482465            break;
     
    518501bool WebSocketChannel::processFrame()
    519502{
    520     ASSERT(m_buffer);
     503    ASSERT(!m_buffer.isEmpty());
    521504
    522505    WebSocketFrame frame;
    523506    const char* frameEnd;
    524507    String errorString;
    525     WebSocketFrame::ParseFrameResult result = WebSocketFrame::parseFrame(m_buffer, m_bufferSize, frame, frameEnd, errorString);
     508    WebSocketFrame::ParseFrameResult result = WebSocketFrame::parseFrame(m_buffer.data(), m_buffer.size(), frame, frameEnd, errorString);
    526509    if (result == WebSocketFrame::FrameIncomplete)
    527510        return false;
     
    531514    }
    532515
    533     ASSERT(m_buffer < frameEnd);
    534     ASSERT(frameEnd <= m_buffer + m_bufferSize);
     516    ASSERT(m_buffer.data() < frameEnd);
     517    ASSERT(frameEnd <= m_buffer.data() + m_buffer.size());
    535518
    536519    OwnPtr<InflateResultHolder> inflateResult = m_deflateFramer.inflate(frame);
     
    586569        }
    587570        m_continuousFrameData.append(frame.payload, frame.payloadLength);
    588         skipBuffer(frameEnd - m_buffer);
     571        skipBuffer(frameEnd - m_buffer.data());
    589572        if (frame.final) {
    590573            // onmessage handler may eventually call the other methods of this channel,
     
    618601            else
    619602                message = "";
    620             skipBuffer(frameEnd - m_buffer);
     603            skipBuffer(frameEnd - m_buffer.data());
    621604            if (message.isNull())
    622605                fail("Could not decode a text frame as UTF-8.");
     
    628611            ASSERT(m_continuousFrameData.isEmpty());
    629612            m_continuousFrameData.append(frame.payload, frame.payloadLength);
    630             skipBuffer(frameEnd - m_buffer);
     613            skipBuffer(frameEnd - m_buffer.data());
    631614        }
    632615        break;
     
    636619            OwnPtr<Vector<char> > binaryData = adoptPtr(new Vector<char>(frame.payloadLength));
    637620            memcpy(binaryData->data(), frame.payload, frame.payloadLength);
    638             skipBuffer(frameEnd - m_buffer);
     621            skipBuffer(frameEnd - m_buffer.data());
    639622            m_client->didReceiveBinaryData(binaryData.release());
    640623        } else {
     
    643626            ASSERT(m_continuousFrameData.isEmpty());
    644627            m_continuousFrameData.append(frame.payload, frame.payloadLength);
    645             skipBuffer(frameEnd - m_buffer);
     628            skipBuffer(frameEnd - m_buffer.data());
    646629        }
    647630        break;
     
    668651        else
    669652            m_closeEventReason = "";
    670         skipBuffer(frameEnd - m_buffer);
     653        skipBuffer(frameEnd - m_buffer.data());
    671654        m_receivedClosingHandshake = true;
    672655        startClosingHandshake(m_closeEventCode, m_closeEventReason);
     
    679662    case WebSocketFrame::OpCodePing:
    680663        enqueueRawFrame(WebSocketFrame::OpCodePong, frame.payload, frame.payloadLength);
    681         skipBuffer(frameEnd - m_buffer);
     664        skipBuffer(frameEnd - m_buffer.data());
    682665        break;
    683666
     
    685668        // A server may send a pong in response to our ping, or an unsolicited pong which is not associated with
    686669        // any specific ping. Either way, there's nothing to do on receipt of pong.
    687         skipBuffer(frameEnd - m_buffer);
     670        skipBuffer(frameEnd - m_buffer.data());
    688671        break;
    689672
    690673    default:
    691674        ASSERT_NOT_REACHED();
    692         skipBuffer(frameEnd - m_buffer);
     675        skipBuffer(frameEnd - m_buffer.data());
    693676        break;
    694677    }
    695678
    696     return m_buffer;
     679    return !m_buffer.isEmpty();
    697680}
    698681
  • trunk/Source/WebCore/Modules/websockets/WebSocketChannel.h

    r124846 r129239  
    196196    OwnPtr<WebSocketHandshake> m_handshake;
    197197    RefPtr<SocketStreamHandle> m_handle;
    198     char* m_buffer;
    199     size_t m_bufferSize;
     198    Vector<char> m_buffer;
    200199
    201200    Timer<WebSocketChannel> m_resumeTimer;
Note: See TracChangeset for help on using the changeset viewer.