Optimize getSendBuffer for large sends
This commit is contained in:
@@ -19,10 +19,10 @@ int main() {
|
|||||||
.passphrase = "1234"
|
.passphrase = "1234"
|
||||||
}).ws<PerSocketData>("/*", {
|
}).ws<PerSocketData>("/*", {
|
||||||
/* Settings */
|
/* Settings */
|
||||||
.compression = uWS::SHARED_COMPRESSOR,
|
.compression = uWS::DEDICATED_COMPRESSOR_4KB,
|
||||||
.maxPayloadLength = 16 * 1024 * 1024,
|
.maxPayloadLength = 100 * 1024 * 1024,
|
||||||
.idleTimeout = 16,
|
.idleTimeout = 16,
|
||||||
.maxBackpressure = 1 * 1024 * 1024,
|
.maxBackpressure = 100 * 1024 * 1024,
|
||||||
.closeOnBackpressureLimit = false,
|
.closeOnBackpressureLimit = false,
|
||||||
.resetIdleTimeoutOnSend = false,
|
.resetIdleTimeoutOnSend = false,
|
||||||
.sendPingsAutomatically = true,
|
.sendPingsAutomatically = true,
|
||||||
|
|||||||
+25
-8
@@ -34,6 +34,12 @@
|
|||||||
|
|
||||||
namespace uWS {
|
namespace uWS {
|
||||||
|
|
||||||
|
enum SendBufferAttribute {
|
||||||
|
NEEDS_NOTHING,
|
||||||
|
NEEDS_DRAIN,
|
||||||
|
NEEDS_UNCORK
|
||||||
|
};
|
||||||
|
|
||||||
template <bool, bool, typename> struct WebSocketContext;
|
template <bool, bool, typename> struct WebSocketContext;
|
||||||
|
|
||||||
template <bool SSL>
|
template <bool SSL>
|
||||||
@@ -102,16 +108,27 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Returns a suitable buffer for temporary assemblation of send data */
|
/* Returns a suitable buffer for temporary assemblation of send data */
|
||||||
std::pair<char *, bool> getSendBuffer(size_t size) {
|
std::pair<char *, SendBufferAttribute> getSendBuffer(size_t size) {
|
||||||
/* If we are corked and we have room, return the cork buffer itself */
|
/* First step is to determine if we already have backpressure or not */
|
||||||
LoopData *loopData = getLoopData();
|
LoopData *loopData = getLoopData();
|
||||||
if (loopData->corkedSocket == this && loopData->corkOffset + size < LoopData::CORK_BUFFER_SIZE) {
|
BackPressure &backPressure = getAsyncSocketData()->buffer;
|
||||||
char *sendBuffer = loopData->corkBuffer + loopData->corkOffset;
|
size_t existingBackpressure = backPressure.length();
|
||||||
loopData->corkOffset += (unsigned int) size;
|
if ((!existingBackpressure) && (isCorked() || canCork()) && (loopData->corkOffset + size < LoopData::CORK_BUFFER_SIZE)) {
|
||||||
return {sendBuffer, false};
|
/* Cork automatically if we can */
|
||||||
|
if (isCorked()) {
|
||||||
|
char *sendBuffer = loopData->corkBuffer + loopData->corkOffset;
|
||||||
|
loopData->corkOffset += (unsigned int) size;
|
||||||
|
return {sendBuffer, SendBufferAttribute::NEEDS_NOTHING};
|
||||||
|
} else {
|
||||||
|
cork();
|
||||||
|
char *sendBuffer = loopData->corkBuffer + loopData->corkOffset;
|
||||||
|
loopData->corkOffset += (unsigned int) size;
|
||||||
|
return {sendBuffer, SendBufferAttribute::NEEDS_UNCORK};
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Slow path for now, we want to always be corked if possible */
|
/* Fallback is to use the backpressure as buffer */
|
||||||
return {(char *) malloc(size), true};
|
backPressure.resize(existingBackpressure + size);
|
||||||
|
return {(char *) backPressure.data() + existingBackpressure, SendBufferAttribute::NEEDS_DRAIN};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ struct BackPressure {
|
|||||||
void reserve(size_t length) {
|
void reserve(size_t length) {
|
||||||
buffer.reserve(length + pendingRemoval);
|
buffer.reserve(length + pendingRemoval);
|
||||||
}
|
}
|
||||||
|
void resize(size_t length) {
|
||||||
|
buffer.resize(length + pendingRemoval);
|
||||||
|
}
|
||||||
const char *data() {
|
const char *data() {
|
||||||
return buffer.data() + pendingRemoval;
|
return buffer.data() + pendingRemoval;
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-25
@@ -107,38 +107,21 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check to see if we can cork for the user */
|
/* Get size, allocate size, write if needed */
|
||||||
bool automaticallyCorked = false;
|
|
||||||
if (!Super::isCorked() && Super::canCork()) {
|
|
||||||
automaticallyCorked = true;
|
|
||||||
Super::cork();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get size, alloate size, write if needed */
|
|
||||||
size_t messageFrameSize = protocol::messageFrameSize(message.length());
|
size_t messageFrameSize = protocol::messageFrameSize(message.length());
|
||||||
auto [sendBuffer, requiresWrite] = Super::getSendBuffer(messageFrameSize);
|
auto [sendBuffer, sendBufferAttribute] = Super::getSendBuffer(messageFrameSize);
|
||||||
protocol::formatMessage<isServer>(sendBuffer, message.data(), message.length(), opCode, message.length(), compress);
|
protocol::formatMessage<isServer>(sendBuffer, message.data(), message.length(), opCode, message.length(), compress);
|
||||||
/* This is the slow path, when we couldn't cork for the user */
|
|
||||||
if (requiresWrite) {
|
|
||||||
/* We tried corking for the user but in the end we did not even fit in the cork buffer */
|
|
||||||
if (automaticallyCorked) {
|
|
||||||
Super::uncork();
|
|
||||||
automaticallyCorked = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto[written, failed] = Super::write(sendBuffer, (int) messageFrameSize);
|
|
||||||
|
|
||||||
/* For now, we are slow here */
|
|
||||||
free(sendBuffer);
|
|
||||||
|
|
||||||
|
/* Depending on size of message we have different paths */
|
||||||
|
if (sendBufferAttribute == SendBufferAttribute::NEEDS_DRAIN) {
|
||||||
|
/* This is a drain */
|
||||||
|
auto[written, failed] = Super::write(nullptr, 0);
|
||||||
if (failed) {
|
if (failed) {
|
||||||
/* Return false for failure, skipping to reset the timeout below */
|
/* Return false for failure, skipping to reset the timeout below */
|
||||||
return BACKPRESSURE;
|
return BACKPRESSURE;
|
||||||
}
|
}
|
||||||
}
|
} else if (sendBufferAttribute == SendBufferAttribute::NEEDS_UNCORK) {
|
||||||
|
/* Uncork if we came here uncorked */
|
||||||
/* Uncork here if we automatically corked for the user */
|
|
||||||
if (automaticallyCorked) {
|
|
||||||
auto [written, failed] = Super::uncork();
|
auto [written, failed] = Super::uncork();
|
||||||
if (failed) {
|
if (failed) {
|
||||||
return BACKPRESSURE;
|
return BACKPRESSURE;
|
||||||
|
|||||||
Reference in New Issue
Block a user