For some reason std:: string/vector memory is much slower!

This commit is contained in:
Alex Hultman
2018-06-24 15:40:12 +02:00
parent 377d1b3ee0
commit 2be613d40b
5 changed files with 82 additions and 24 deletions
+19 -12
View File
@@ -1,12 +1,25 @@
#include "uWS.h" #include <string>
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 //#define USE_SSL
int main() { int main() {
buffer = "Why hello there!"; std::string buffer;
buffer.resize(largeHttpBufSize);
std::vector<char> 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::init();
//uWS::Loop loop(); //uWS::Loop loop();
@@ -22,15 +35,9 @@ int main() {
app.onGet("/", [](auto *s, auto *req, auto *args) { app.onGet("/", [](auto *s, auto *req, auto *args) {
// this one is simple and okay for small sends s->/*writeStatus("200 OK")->*/write([](int offset) {
/*s->writeStatus("200 OK") return std::string_view(largeHttpBuf + offset, largeHttpBufSize - offset);
->writeHeader("Server", "uWebSockets") }, largeHttpBufSize);
->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());
}).onWebSocket("/wsApi", []() { }).onWebSocket("/wsApi", []() {
+9 -6
View File
@@ -66,7 +66,7 @@ protected:
static_dispatch(us_ssl_socket_context_on_open, us_socket_context_on_open)(httpServerContext, [](auto *s) { 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)); 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<SSL>::Data;
if (!data->onHttpConnection) { if (!data->onHttpConnection) {
return; return;
@@ -78,6 +78,9 @@ protected:
static_dispatch(us_ssl_socket_context_on_close, us_socket_context_on_close)(httpServerContext, [](auto *s) { 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)); 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<SSL>::Data *) static_dispatch(us_ssl_socket_ext, us_socket_ext)(s))->~(typename HttpSocket<SSL>::Data)();
if (!data->onHttpDisconnection) { if (!data->onHttpDisconnection) {
return; 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)); 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); HttpRequest req(data, length);
if (req.isComplete()) { if (req.isComplete()) {
contextData->onHttpRequest((HttpSocket<SSL> *) s, &req); contextData->onHttpRequest((HttpSocket<SSL> *) s, &req);
@@ -100,13 +99,17 @@ protected:
std::cout << "Got chunked HTTP headers!" << std::endl; 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<SSL> *) s)->onWritable();
});
} }
public: public:
// for server // for server
void listen(const char *host, int port, int options) { 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<SSL>)); static_dispatch(us_ssl_socket_context_listen, us_socket_context_listen)(httpServerContext, host, port, options, sizeof(typename HttpSocket<SSL>::Data));
} }
AppBase &onPost(std::string pattern, std::function<void(HttpSocket<SSL> *, HttpRequest *, std::vector<std::string_view> *)> handler) { AppBase &onPost(std::string pattern, std::function<void(HttpSocket<SSL> *, HttpRequest *, std::vector<std::string_view> *)> handler) {
+48 -4
View File
@@ -10,6 +10,17 @@
template <bool SSL> template <bool SSL>
struct HttpSocket { struct HttpSocket {
template <class A, class B>
static constexpr typename std::conditional<SSL, A, B>::type *static_dispatch(A *a, B *b) {
if constexpr(SSL) {
return a;
} else {
return b;
}
}
typedef typename std::conditional<SSL, us_ssl_socket, us_socket>::type SOCKET_TYPE;
// chunked response will be tricky with this buffering scheme // 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!) // 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! // 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; std::string headerBuffer;
int offset = 0; int offset = 0;
std::function<std::pair<const char *, int>(int)> outStream; std::function<std::string_view(int)> outStream;
}; };
// only this one should be used! // only this one should be used!
@@ -51,13 +62,13 @@ struct HttpSocket {
} }
// never rely on this one! // 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))); 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); memcpy(loopData->corkBuffer + loopData->corkOffset, "Content-Length: ", 16);
loopData->corkOffset += 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); memcpy(loopData->corkBuffer + loopData->corkOffset, "\r\n\r\n", 4);
loopData->corkOffset += 4; loopData->corkOffset += 4;
@@ -97,9 +108,42 @@ struct HttpSocket {
// stream out (todo: fix up large sends and benchmark it again) // stream out (todo: fix up large sends and benchmark it again)
void write(std::function<std::string_view(int)> cb, int length) { void write(std::function<std::string_view(int)> 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); 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! // can't create this!
+5 -1
View File
@@ -6,14 +6,18 @@
#include <new> #include <new>
#include <string_view> #include <string_view>
#include <iostream> #include <iostream>
#include <unistd.h>
namespace uWS { namespace uWS {
struct Loop { struct Loop {
us_loop *loop; us_loop *loop;
static const int CORK_BUFFER_SIZE = 16 * 1024;
static const int MAX_COPY_DISTANCE = 4 * 1024;
struct Data { struct Data {
char *corkBuffer = new char[1024]; char *corkBuffer = new char[CORK_BUFFER_SIZE];
int corkOffset = 0; int corkOffset = 0;
Data() { Data() {