uwebsockets receive performance is good, but reply performance is more lower than receive.
f you send 200*10000 message, you will see uwebsockets receive all message in about 2-4s, which is very fast。when it reply message to client, it will need more time to reply, which will use about 15s to reply this message to client.
maybe reply speed can be improved add sendBatch method to reduce small io interact with socket io by a big buffer.
I can't tell you if this is reasonable, the info given is very diffuse.
What I would like to see is a comparison of your workload in uWS against some other implementation where uWS is slower than the other implementation.
If you can provide that, I can look and see if your use hits some slow path. This also makes it easier for me to offload work to you, until you have given objective proof of something being wrong.
But I suspect you are simply shoving data faster than the kernel space consumes it, and so you end up hitting worst case path always. Sending and receiving in fast path is similar in cost.
the reason is message is very small,such as below 1kb . uwebsockets receive use 512kb buffer, which can receive many small message in one read, so the receive speed is very fast。 but for reply, every message will call send method, it will try interact socket io, which may lead to poor performance 。if we can merge many small message to one big buffer,the performance will have big improve。
this method can be achived by add sendBatch method,you can call sendBatch on on drain callback which will be faster。
`
SendStatus send(std::string_view message, OpCode opCode = OpCode::BINARY, int compress = false, bool fin = true) {
// each small message will have a try to interact socket io every times。
}
`
improve by add sendBatch method as the follow code
`
SendStatus sendBatch(std::vector<std::string_view>& messages, OpCode opCode = OpCode::BINARY) {
int compress = false;
bool fin = true;
WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL,
(us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this)
);
/* If we are subscribers and have messages to drain we need to drain them here to stay synced */
WebSocketData *webSocketData = (WebSocketData *) Super::getAsyncSocketData();
if (webSocketData->subscriber) {
/* This will call back into us, send. */
webSocketContextData->topicTree->drain(webSocketData->subscriber);
}
/* Get size, allocate size, write if needed */
size_t batchMessageFrameSize = 0;
for (int i=0; i < messages.size(); i++) {
std::string_view& message = messages[i];
batchMessageFrameSize += protocol::messageFrameSize(message.length());
}
auto [sendBuffer, sendBufferAttribute] = Super::getSendBuffer(batchMessageFrameSize);
char *batchBuffer = sendBuffer;
for (int i=0; i < messages.size(); i++) {
std::string_view& message = messages[i];
protocol::formatMessage<isServer>(batchBuffer, message.data(), message.length(), opCode, message.length(), compress, fin);
size_t messageFrameSize = protocol::messageFrameSize(message.length());
batchBuffer += messageFrameSize;
}
/* 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) {
/* Return false for failure, skipping to reset the timeout below */
return BACKPRESSURE;
}
} else if (sendBufferAttribute == SendBufferAttribute::NEEDS_UNCORK) {
/* Uncork if we came here uncorked */
auto [written, failed] = Super::uncork();
if (failed) {
return BACKPRESSURE;
}
}
/* Every successful send resets the timeout */
if (webSocketContextData->resetIdleTimeoutOnSend) {
Super::timeout(webSocketContextData->idleTimeoutComponents.first);
WebSocketData *webSocketData = (WebSocketData *) Super::getAsyncSocketData();
webSocketData->hasTimedOut = false;
}
/* Return success */
return SUCCESS;
}
`
`
bool canSendBatch() {
WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL,
(us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this)
);
if (webSocketContextData->maxBackpressure && webSocketContextData->maxBackpressure < getBufferedAmount()) {
return false;
}
return true;
}
`
if you all log you will see many zero written when pressure, which is unnecessary
` int written = us_socket_write(SSL, (us_socket_t *) this, asyncSocketData->buffer.data(), (int) asyncSocketData->buffer.length(), /*nextLength != 0 | */length); std::cout << "asyncSocketData->buffer socket write write: write " << written << std::endl;
`