Browse Source

Lithium update. (#6129)

* Lithium update.

* Lithium update.

* Lithium update.

* Lithium update.
Matthieu Garrigues 4 years ago
parent
commit
68c13b5c94

+ 3 - 3
frameworks/C++/lithium/compile_clang-batch.sh → frameworks/C++/lithium/compile-batch.sh

@@ -1,7 +1,7 @@
 #! /bin/sh
 
 DB_FLAG=$1
-COMMIT=b3e48251b9c5f5baf0727347a80fbc7f8e67ef0f
+COMMIT=10b72ad8ce16528ff642f802e266a975964d06f9
 MONOTHREAD=$2
 
 if [ $DB_FLAG = "TFB_MYSQL" ]; then
@@ -17,9 +17,9 @@ fi
 
 wget https://raw.githubusercontent.com/matt-42/lithium/$COMMIT/single_headers/lithium_http_backend.hh
 
-clang++ -fprofile-instr-generate=./profile.prof -flto -DPROFILE_MODE -DN_SQL_CONNECTIONS=1  -DMONOTHREAD=$MONOTHREAD -DNDEBUG -D$DB_FLAG -O3 -march=native -std=c++17 ./lithium_pipeline.cc $CXX_FLAGS -lpthread -lboost_context -lssl -lcrypto -o /lithium_tbf
+clang++ -fprofile-instr-generate=./profile.prof -flto -DLITHIUM_SERVER_NAME=l -DPROFILE_MODE -DN_SQL_CONNECTIONS=1  -DMONOTHREAD=$MONOTHREAD -DNDEBUG -D$DB_FLAG -O3 -march=native -std=c++17 ./lithium_batch.cc $CXX_FLAGS -lpthread -lboost_context -lssl -lcrypto -o /lithium_tbf
 /lithium_tbf tfb-database 8081
 llvm-profdata-10 merge -output=./profile.pgo  ./profile.prof
-clang++ -fprofile-instr-use=./profile.pgo -flto -DNDEBUG -D$DB_FLAG -DN_SQL_CONNECTIONS=1  -DMONOTHREAD=$MONOTHREAD -O3 -march=native -std=c++17 ./lithium_pipeline.cc $CXX_FLAGS -lpthread -lboost_context -lssl -lcrypto -o /lithium_tbf
+clang++ -fprofile-instr-use=./profile.pgo -flto -DLITHIUM_SERVER_NAME=l -DNDEBUG -D$DB_FLAG -DN_SQL_CONNECTIONS=1  -DMONOTHREAD=$MONOTHREAD -O3 -march=native -std=c++17 ./lithium_batch.cc $CXX_FLAGS -lpthread -lboost_context -lssl -lcrypto -o /lithium_tbf
 
 /lithium_tbf tfb-database 8080

+ 27 - 0
frameworks/C++/lithium/compile.sh

@@ -0,0 +1,27 @@
+#! /bin/sh
+
+DB_FLAG=$1
+COMMIT=e199537f15abce7cf25c0b4de0f14461a1f426ec
+
+if [ $DB_FLAG = "TFB_MYSQL" ]; then
+  CXX_FLAGS="-I /usr/include/mariadb  -lmariadbclient "
+  wget https://raw.githubusercontent.com/matt-42/lithium/$COMMIT/single_headers/lithium_mysql.hh
+elif [ $DB_FLAG = "TFB_PGSQL" ]; then
+  CXX_FLAGS="-lpthread  -L/usr/lib -lpq -I/postgres-bab150045bd9766869f471ede88734ea0989261c/src/include"
+  wget https://raw.githubusercontent.com/matt-42/lithium/$COMMIT/single_headers/lithium_pgsql.hh
+fi
+
+COMPILER="clang++"
+
+wget https://raw.githubusercontent.com/matt-42/lithium/$COMMIT/single_headers/lithium_http_server.hh
+
+if [ $COMPILER = "clang++" ]; then
+  clang++ -fprofile-instr-generate=./profile.prof -flto -DLITHIUM_SERVER_NAME=l -DPROFILE_MODE -DNDEBUG -D$DB_FLAG -O3 -march=native -std=c++17 ./lithium.cc $CXX_FLAGS -lpthread -lboost_context -lssl -lcrypto -o /lithium_tbf
+  /lithium_tbf tfb-database 8081
+  llvm-profdata-10 merge -output=./profile.pgo  ./profile.prof
+  clang++ -fprofile-instr-use=./profile.pgo -flto -DLITHIUM_SERVER_NAME=l -DNDEBUG -D$DB_FLAG -O3 -march=native -std=c++17 ./lithium.cc $CXX_FLAGS -lpthread -lboost_context -lssl -lcrypto -o /lithium_tbf
+else
+  g++ -flto -DLITHIUM_SERVER_NAME=l -DNDEBUG -D$DB_FLAG -O3 -march=native -std=c++17 ./lithium.cc $CXX_FLAGS -lpthread -lboost_context -lssl -lcrypto -o /lithium_tbf
+fi
+
+/lithium_tbf tfb-database 8080

+ 0 - 22
frameworks/C++/lithium/compile_and_start_clang.sh

@@ -1,22 +0,0 @@
-#! /bin/sh
-
-DB_FLAG=$1
-COMMIT=6d2b6d06fc47d45a0451e99d470ce2bc6b26f0d7
-
-if [ $DB_FLAG = "TFB_MYSQL" ]; then
-  CXX_FLAGS="-I /usr/include/mariadb  -lmariadbclient "
-  wget https://raw.githubusercontent.com/matt-42/lithium/$COMMIT/single_headers/lithium_mysql.hh
-elif [ $DB_FLAG = "TFB_PGSQL" ]; then
-  CXX_FLAGS="-lpthread  -L/usr/lib -lpq -I/postgres-bab150045bd9766869f471ede88734ea0989261c/src/include"
-  wget https://raw.githubusercontent.com/matt-42/lithium/$COMMIT/single_headers/lithium_pgsql.hh
-fi
-
-
-wget https://raw.githubusercontent.com/matt-42/lithium/$COMMIT/single_headers/lithium_http_server.hh
-
-clang++ -fprofile-instr-generate=./profile.prof -flto -DPROFILE_MODE -DNDEBUG -D$DB_FLAG -O3 -march=native -std=c++17 ./lithium.cc $CXX_FLAGS -lpthread -lboost_context -lssl -lcrypto -o /lithium_tbf
-/lithium_tbf tfb-database 8081
-llvm-profdata-10 merge -output=./profile.pgo  ./profile.prof
-clang++ -fprofile-instr-use=./profile.pgo -flto -DNDEBUG -D$DB_FLAG -O3 -march=native -std=c++17 ./lithium.cc $CXX_FLAGS -lpthread -lboost_context -lssl -lcrypto -o /lithium_tbf
-
-/lithium_tbf tfb-database 8080

+ 0 - 19
frameworks/C++/lithium/compile_and_start_gcc.sh

@@ -1,19 +0,0 @@
-#! /bin/sh
-
-DB_FLAG=$1
-COMMIT=6d2b6d06fc47d45a0451e99d470ce2bc6b26f0d7
-
-if [ $DB_FLAG = "TFB_MYSQL" ]; then
-  CXX_FLAGS="-I /usr/include/mariadb  -lmariadbclient "
-  wget https://raw.githubusercontent.com/matt-42/lithium/$COMMIT/single_headers/lithium_mysql.hh
-elif [ $DB_FLAG = "TFB_PGSQL" ]; then
-  CXX_FLAGS="-I/usr/include/postgresql -I /usr/include/postgresql/12/server -lpthread -lpq"
-  wget https://raw.githubusercontent.com/matt-42/lithium/$COMMIT/single_headers/lithium_pgsql.hh
-fi
-
-
-wget https://raw.githubusercontent.com/matt-42/lithium/$COMMIT/single_headers/lithium_http_server.hh
-
-g++ -flto -DNDEBUG -D$DB_FLAG -O3 -march=native -std=c++17 ./lithium.cc $CXX_FLAGS -lpthread -lboost_context -lssl -lcrypto -o /lithium_tbf
-
-/lithium_tbf tfb-database 8080

+ 1 - 1
frameworks/C++/lithium/lithium-postgres-batch.dockerfile

@@ -9,4 +9,4 @@ COPY ./ ./
 RUN ./compile_libpq.sh batchmode
 ENV LD_LIBRARY_PATH=/usr/lib
 
-CMD ./compile_clang-batch.sh TFB_PGSQL 0
+CMD ./compile-batch.sh TFB_PGSQL 0

+ 1 - 1
frameworks/C++/lithium/lithium-postgres.dockerfile

@@ -9,4 +9,4 @@ COPY ./ ./
 RUN ./compile_libpq.sh
 ENV LD_LIBRARY_PATH=/usr/lib
 
-CMD ./compile_and_start_clang.sh TFB_PGSQL
+CMD ./compile.sh TFB_PGSQL

+ 9 - 20
frameworks/C++/lithium/lithium.cc

@@ -58,14 +58,13 @@ void siege(int port) {
 template <typename T>
 struct cache {
 
-  void insert(T o) { 
-    buffer.push_back(o);
+  void insert(T o) {
+    if (buffer.size() <= o.id) buffer.resize(o.id+1);
+    buffer[o.id] = o;
   }
 
-  std::vector<const T*> get_array(const std::vector<int>& ids) const {
-    std::vector<const T*> res;
-    for (int i = 0; i < ids.size(); i++) res.push_back(&buffer[ids[i]]);
-    return res;
+  const T& get(int id) const {
+    return buffer[id];
   }
 
   std::vector<T> buffer;
@@ -148,14 +147,8 @@ int main(int argc, char* argv[]) {
     
     N = std::max(1, std::min(N, 500));
     
-    std::vector<decltype(random_numbers.all_fields())> numbers(N);
-    {
-      auto c = random_numbers.connect(request.fiber);
-      for (int i = 0; i < N; i++)
-        numbers[i] = *c.find_one(s::id = 1 + rand() % 10000);
-    }
-
-    response.write_json(numbers);
+    auto c = random_numbers.connect(request.fiber);
+    response.write_json_generator(N, [&] { return *c.find_one(s::id = 1 + rand() % 10000); });
   };
 
   random_numbers.connect().forall([&] (const auto& number) {
@@ -165,13 +158,9 @@ int main(int argc, char* argv[]) {
   my_api.get("/cached-worlds") = [&](http_request& request, http_response& response) {
     std::string N_str = request.get_parameters(s::N = std::optional<std::string>()).N.value_or("1");
     int N = atoi(N_str.c_str());
-    
-    N = std::max(1, std::min(N, 500));
 
-    std::vector<int> ids(N);
-    for (int i = 0; i < N; i++)
-      ids[i] = 1 + rand() % 10000;
-    response.write_json(world_cache.get_array(ids));
+    response.write_json_generator(std::max(1, std::min(N, 500)), 
+      [&] { return world_cache.get(1 + rand() % 10000); });
   };
 
   my_api.get("/updates") = [&](http_request& request, http_response& response) {

+ 1 - 1
frameworks/C++/lithium/lithium.dockerfile

@@ -6,4 +6,4 @@ RUN apt-get install -yqq libmariadb-dev
 
 COPY ./ ./
 
-CMD ./compile_and_start_clang.sh TFB_MYSQL
+CMD ./compile.sh TFB_MYSQL

+ 194 - 0
frameworks/C++/lithium/lithium_batch.cc

@@ -0,0 +1,194 @@
+#include "lithium_http_backend.hh"
+
+#if TFB_MYSQL
+  #include "lithium_mysql.hh"
+#elif TFB_PGSQL
+  #include "lithium_pgsql.hh"
+#endif
+
+#include "symbols.hh"
+using namespace li;
+
+template <typename B>
+void escape_html_entities(B& buffer, const std::string_view& data)
+{
+  size_t pos = 0;
+  auto search_for_special = [&] () {
+    size_t start = pos;
+    size_t end = pos;
+    for(;pos != data.size(); ++pos) {
+      char c = data[pos];
+      if (c > '>' || (c != '&' && c != '\"' && c != '\'' && c != '<' && c == '>'))
+        end = pos + 1;
+      else break;
+    }
+
+    if (start != end)
+      buffer << std::string_view(data.data() + start, end - start);
+  };
+  
+    for(; pos != data.size(); ++pos) {
+      search_for_special();
+      if (pos >= data.size()) return;
+        switch(data[pos]) {
+            case '&':  buffer << "&amp;";       break;
+            case '\"': buffer << "&quot;";      break;
+            case '\'': buffer << "&apos;";      break;
+            case '<':  buffer << "&lt;";        break;
+            case '>':  buffer << "&gt;";        break;
+            default:   buffer << data[pos]; break;
+        }
+    }
+}
+
+#ifdef PROFILE_MODE
+void siege(int port) {
+  auto sockets = http_benchmark_connect(256, port);
+  http_benchmark(sockets, 1, 100, "GET /json HTTP/1.1\r\n\r\n");
+  http_benchmark(sockets, 1, 100, "GET /plaintext HTTP/1.1\r\n\r\n");
+  http_benchmark(sockets, 1, 100, "GET /db HTTP/1.1\r\n\r\n");
+  http_benchmark(sockets, 1, 100, "GET /queries?N=20 HTTP/1.1\r\n\r\n");
+  http_benchmark(sockets, 1, 100, "GET /fortunes HTTP/1.1\r\n\r\n");
+  // http_benchmark(sockets, 1, 100, "GET /updates?N=20 HTTP/1.1\r\n\r\n");
+  http_benchmark_close(sockets);
+}
+#endif
+
+int main(int argc, char* argv[]) {
+
+  if (argc != 3)
+  {
+    std::cerr << "Usage: " << argv[0] << " sql_host port" << std::endl;
+    return 1;
+  }
+
+  int port = atoi(argv[2]);
+
+  int nprocs = std::thread::hardware_concurrency();
+
+#if MONOTHREAD
+  int nthreads = 1;
+#else
+  int nthreads = nprocs;
+#endif
+
+#if TFB_MYSQL
+  auto sql_db = mysql_database(s::host = argv[1], s::database = "hello_world", s::user = "benchmarkdbuser",
+                s::password = "benchmarkdbpass", s::port = 3306, s::charset = "utf8");
+#elif TFB_PGSQL
+  auto sql_db = pgsql_database(s::host = argv[1], s::database = "hello_world", s::user = "benchmarkdbuser",
+                               s::password = "benchmarkdbpass", s::port = 5432, s::charset = "utf8", s::max_async_connections_per_thread = N_SQL_CONNECTIONS);
+#endif
+
+  std::cout << "Using " << sql_db.max_async_connections_per_thread_ << " sql connections x " << nthreads << " threads." << std::endl;
+  auto fortunes = sql_orm_schema(sql_db, "Fortune").fields(
+    s::id(s::auto_increment, s::primary_key) = int(),
+    s::message = std::string());
+
+  auto random_numbers = sql_orm_schema(sql_db, "World").fields(
+    s::id(s::auto_increment, s::primary_key) = int(),
+    s::randomNumber = int());
+
+
+
+  http_api my_api;
+
+  auto select_N_random_numbers = [] (auto& orm, int N) {
+    std::vector<decltype(random_numbers.all_fields())> numbers(N);
+    std::vector<decltype(orm.find_one(s::id = 1))> results;
+    
+    for (int i = 0; i < N; i++)
+      results.push_back(orm.find_one(s::id = 1 + (i*10000/N) + rand() % (10000/N)));
+    for (int i = 0; i < N; i++){
+      // println(" read result " , i);
+      numbers[i] = results[i]().value();
+    }
+    return numbers;
+  };
+
+  my_api.get("/plaintext") = [&](http_request& request, http_response& response) {
+    response.set_header("Content-Type", "text/plain");
+    response.write("Hello, World!");
+  };
+
+  my_api.get("/json") = [&](http_request& request, http_response& response) {
+    response.write_json(s::message = "Hello, World!");
+  };
+  my_api.get("/db") = [&](http_request& request, http_response& response) {
+    response.write_json(random_numbers.connect(request.fiber).find_one(s::id = 1 + rand() % 10000)());
+  };
+
+  my_api.get("/queries") = [&](http_request& request, http_response& response) {
+    std::string N_str = request.get_parameters(s::N = std::optional<std::string>()).N.value_or("1");
+    int N = atoi(N_str.c_str());
+    
+    N = std::max(1, std::min(N, 500));
+    
+    auto orm = random_numbers.connect(request.fiber);
+    std::vector<decltype(orm.find_one(s::id = 1))> results;
+    for (int i = 0; i < N; i++)
+      results.push_back(orm.find_one(s::id = 1 + (i*10000/N) + rand() % (10000/N)));
+    
+    int i = 0;
+    response.write_json_generator(N, [&] { return results[i++]().value(); });
+  };
+
+  my_api.get("/updates") = [&](http_request& request, http_response& response) {
+    std::string N_str = request.get_parameters(s::N = std::optional<std::string>()).N.value_or("1");
+    int N = atoi(N_str.c_str());
+    N = std::max(1, std::min(N, 500));
+
+    auto c = random_numbers.connect(request.fiber);
+    auto numbers = select_N_random_numbers(c, N);
+ 
+    for (int i = 0; i < N; i++)
+      numbers[i].randomNumber = 1 + rand() % 10000;
+
+    auto req = c.bulk_update(numbers);
+    if (N_SQL_CONNECTIONS * nthreads > 1) c.backend_connection().end_of_batch();
+    req.flush_results();
+    response.write_json(numbers);
+  };
+
+  my_api.get("/fortunes") = [&](http_request& request, http_response& response) {
+
+    typedef decltype(fortunes.all_fields()) fortune;
+    std::vector<fortune> table;
+
+    auto c = fortunes.connect(request.fiber);
+    c.forall([&] (const auto& f) { table.emplace_back(metamap_clone(f)); });
+    table.emplace_back(0, "Additional fortune added at request time.");
+
+    std::sort(table.begin(), table.end(),
+              [] (const fortune& a, const fortune& b) { return a.message < b.message; });
+
+    li::growing_output_buffer ss;
+ 
+    ss << "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>";
+    for(auto& f : table)
+    {
+      ss << "<tr><td>" << f.id << "</td><td>";
+      escape_html_entities(ss, f.message); 
+      ss << "</td></tr>";
+    }
+    ss << "</table></body></html>";
+
+    response.set_header("Content-Type", "text/html; charset=utf-8");
+    response.write(ss.to_string_view());
+  };
+
+#ifndef PROFILE_MODE
+  // Start the server for the Techempower benchmark.
+  http_serve(my_api, port, s::nthreads = nthreads);
+#else
+  std::thread server_thread([&] {
+    http_serve(my_api, port, s::nthreads = nprocs);
+  });
+  usleep(2e6);
+  siege(port);
+  li::quit_signal_catched = true;
+  server_thread.join();
+
+#endif
+  return 0;
+}