From 2be613d40bf465df9fb6d61e5bd424ed88a75366 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 24 Jun 2018 15:38:24 +0200 Subject: [PATCH] For some reason std:: string/vector memory is much slower! --- main.cpp | 31 ++++++++++++++++++----------- src/App.h | 15 ++++++++------ src/HttpSocket.h | 52 ++++++++++++++++++++++++++++++++++++++++++++---- src/Loop.h | 6 +++++- uSockets | 2 +- 5 files changed, 82 insertions(+), 24 deletions(-) diff --git a/main.cpp b/main.cpp index 0de0a4a..a28fabc 100644 --- a/main.cpp +++ b/main.cpp @@ -1,12 +1,25 @@ -#include "uWS.h" +#include -std::string buffer; +char largeBuf[] = "HTTP/1.1 200 OK\r\nContent-Length: 52428800\r\n\r\n"; +int largeHttpBufSize = sizeof(largeBuf) + 52428800 - 1; +char *largeHttpBuf; + +#include "uWS.h" //#define USE_SSL int main() { - buffer = "Why hello there!"; + std::string buffer; + buffer.resize(largeHttpBufSize); + + std::vector vec; + vec.resize(largeHttpBufSize); + + // all STD-classes's memory cut throughput in half! + largeHttpBuf = (char *) buffer.data();//new char[largeHttpBufSize];//aligned_alloc(16, largeHttpBufSize);//std::aligned_alloc//vec.data();//malloc(largeHttpBufSize); + memcpy(largeHttpBuf, largeBuf, sizeof(largeBuf) - 1); + printf("%d\n", largeHttpBufSize); //uWS::init(); //uWS::Loop loop(); @@ -22,15 +35,9 @@ int main() { app.onGet("/", [](auto *s, auto *req, auto *args) { - // this one is simple and okay for small sends - /*s->writeStatus("200 OK") - ->writeHeader("Server", "uWebSockets") - ->end(buffer);*/ - - // for large sends you want a stream! - s->writeStatus("200 OK")->write([](int offset) { - return std::string_view(buffer.data() + offset, buffer.length() - offset); - }, buffer.length()); + s->/*writeStatus("200 OK")->*/write([](int offset) { + return std::string_view(largeHttpBuf + offset, largeHttpBufSize - offset); + }, largeHttpBufSize); }).onWebSocket("/wsApi", []() { diff --git a/src/App.h b/src/App.h index ad7d036..181a9cf 100644 --- a/src/App.h +++ b/src/App.h @@ -66,7 +66,7 @@ protected: static_dispatch(us_ssl_socket_context_on_open, us_socket_context_on_open)(httpServerContext, [](auto *s) { Data *data = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s)); - // here we need to construct a HTTP socket on the ext! + new (static_dispatch(us_ssl_socket_ext, us_socket_ext)(s)) typename HttpSocket::Data; if (!data->onHttpConnection) { return; @@ -78,6 +78,9 @@ protected: static_dispatch(us_ssl_socket_context_on_close, us_socket_context_on_close)(httpServerContext, [](auto *s) { Data *data = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s)); + // todo: run the destructor! + //((typename HttpSocket::Data *) static_dispatch(us_ssl_socket_ext, us_socket_ext)(s))->~(typename HttpSocket::Data)(); + if (!data->onHttpDisconnection) { return; } @@ -89,10 +92,6 @@ protected: Data *contextData = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s)); - // a HttpSocket is basically the Http state and everything needed for it, we use it to parse the data and it knows about its context - - // - HttpRequest req(data, length); if (req.isComplete()) { contextData->onHttpRequest((HttpSocket *) s, &req); @@ -100,13 +99,17 @@ protected: std::cout << "Got chunked HTTP headers!" << std::endl; } }); + + static_dispatch(us_ssl_socket_context_on_writable, us_socket_context_on_writable)(httpServerContext, [](auto *s) { + ((HttpSocket *) s)->onWritable(); + }); } public: // for server void listen(const char *host, int port, int options) { - static_dispatch(us_ssl_socket_context_listen, us_socket_context_listen)(httpServerContext, host, port, options, sizeof(HttpSocket)); + static_dispatch(us_ssl_socket_context_listen, us_socket_context_listen)(httpServerContext, host, port, options, sizeof(typename HttpSocket::Data)); } AppBase &onPost(std::string pattern, std::function *, HttpRequest *, std::vector *)> handler) { diff --git a/src/HttpSocket.h b/src/HttpSocket.h index f67a067..3febb83 100644 --- a/src/HttpSocket.h +++ b/src/HttpSocket.h @@ -10,6 +10,17 @@ template struct HttpSocket { + template + static constexpr typename std::conditional::type *static_dispatch(A *a, B *b) { + if constexpr(SSL) { + return a; + } else { + return b; + } + } + + typedef typename std::conditional::type SOCKET_TYPE; + // chunked response will be tricky with this buffering scheme // if we do not fit, we can always use the header buffer for this (both in and out!) // put first 8kb chunk in the http buffer, then from there it's the stream's job! @@ -21,7 +32,7 @@ struct HttpSocket { std::string headerBuffer; int offset = 0; - std::function(int)> outStream; + std::function outStream; }; // only this one should be used! @@ -51,13 +62,13 @@ struct HttpSocket { } // never rely on this one! - void writeToCorkBufferAndReset(const char *src, int length) { + void writeToCorkBufferAndReset(const char *src, int length, int contentLength) { uWS::Loop::Data *loopData = (uWS::Loop::Data *) us_loop_ext(us_socket_context_loop(us_socket_get_context((us_socket *) this))); memcpy(loopData->corkBuffer + loopData->corkOffset, "Content-Length: ", 16); loopData->corkOffset += 16; - loopData->corkOffset += u32toa_naive(length, loopData->corkBuffer + loopData->corkOffset); + loopData->corkOffset += u32toa_naive(contentLength, loopData->corkBuffer + loopData->corkOffset); memcpy(loopData->corkBuffer + loopData->corkOffset, "\r\n\r\n", 4); loopData->corkOffset += 4; @@ -97,9 +108,42 @@ struct HttpSocket { // stream out (todo: fix up large sends and benchmark it again) void write(std::function cb, int length) { + + // just assume this went fine + Data *httpData = (Data *) static_dispatch(us_ssl_socket_ext, us_socket_ext)((SOCKET_TYPE *) this); + + //httpData->offset = 0;//uWS::Loop::MAX_COPY_DISTANCE; + + // now we start streaming as much as possible in each call! std::string_view chunk = cb(0); - writeToCorkBufferAndReset(chunk.data(), chunk.length()); + // write that off! + if constexpr (SSL) { + + } else { + httpData->offset = us_socket_write((SOCKET_TYPE *) this, chunk.data(), chunk.length(), 0); + } + + // if offset is at the end, we are done + if (httpData->offset < length) { + httpData->outStream = cb; + } + } + + // this thing should only be reachable from App! + void onWritable() { + Data *httpData = (Data *) static_dispatch(us_ssl_socket_ext, us_socket_ext)((SOCKET_TYPE *) this); + + + // now we start streaming as much as possible in each call! + std::string_view chunk = httpData->outStream(httpData->offset); + + // write that off! + if constexpr (SSL) { + + } else { + httpData->offset += us_socket_write((SOCKET_TYPE *) this, chunk.data(), chunk.length(), 0); + } } // can't create this! diff --git a/src/Loop.h b/src/Loop.h index b7785bd..50aa7d5 100644 --- a/src/Loop.h +++ b/src/Loop.h @@ -6,14 +6,18 @@ #include #include #include +#include namespace uWS { struct Loop { us_loop *loop; + static const int CORK_BUFFER_SIZE = 16 * 1024; + static const int MAX_COPY_DISTANCE = 4 * 1024; + struct Data { - char *corkBuffer = new char[1024]; + char *corkBuffer = new char[CORK_BUFFER_SIZE]; int corkOffset = 0; Data() { diff --git a/uSockets b/uSockets index 834ac39..d8362a7 160000 --- a/uSockets +++ b/uSockets @@ -1 +1 @@ -Subproject commit 834ac39aa313efe9e6cefb5c90dca2cd0cc2d0e8 +Subproject commit d8362a74b3105a7f6e51302650ef7b6d6762e479