Переглянути джерело

Merge remote-tracking branch 'upstream/master' into round-14

Damien Solimando 8 роки тому
батько
коміт
cf9085a9c8
100 змінених файлів з 1982 додано та 928 видалено
  1. 19 0
      .github/PULL_REQUEST_TEMPLATE.md
  2. 3 0
      .gitignore
  3. 4 7
      .travis.yml
  4. 0 2
      config/create-postgres-database.sql
  5. 0 29
      config/create-postgres-old.sql
  6. 0 29
      config/create-postgres-upper-quote.sql
  7. 4 0
      config/create-postgres.sql
  8. 2 53
      config/create.sql
  9. 1 1
      config/travis_setup.sh
  10. 19 0
      config/upstart.example/tfb.conf
  11. 9 9
      deployment/vagrant-aws/README.md
  12. 6 2
      deployment/vagrant-aws/setup_aws.py
  13. 13 9
      frameworks/C++/cpoll_cppsp/benchmark_config.json
  14. 67 6
      frameworks/C++/cutelyst/benchmark_config.json
  15. 2 1
      frameworks/C++/cutelyst/config/config_socket.ini
  16. 3 3
      frameworks/C++/cutelyst/nginx.conf
  17. 4 4
      frameworks/C++/cutelyst/setup.sh
  18. 21 0
      frameworks/C++/cutelyst/setup_thread.sh
  19. 1 3
      frameworks/C++/cutelyst/setup_uwsgi_nginx.sh
  20. 12 4
      frameworks/C++/cutelyst/src/cutelyst-benchmarks.cpp
  21. 6 6
      frameworks/C++/cutelyst/src/databaseupdatestest.cpp
  22. 2 2
      frameworks/C++/cutelyst/src/fortunetest.cpp
  23. 4 4
      frameworks/C++/cutelyst/src/multipledatabasequeriestest.cpp
  24. 4 4
      frameworks/C++/cutelyst/src/singledatabasequerytest.cpp
  25. 188 4
      frameworks/C++/ffead-cpp/benchmark_config.json
  26. 16 0
      frameworks/C++/ffead-cpp/setup-apache2-mysql.sh
  27. 16 0
      frameworks/C++/ffead-cpp/setup-apache2-postgresql.sh
  28. 16 0
      frameworks/C++/ffead-cpp/setup-apache2.sh
  29. 16 0
      frameworks/C++/ffead-cpp/setup-mysql.sh
  30. 16 0
      frameworks/C++/ffead-cpp/setup-nginx-mysql.sh
  31. 15 0
      frameworks/C++/ffead-cpp/setup-nginx-postgresql.sh
  32. 15 0
      frameworks/C++/ffead-cpp/setup-nginx.sh
  33. 16 0
      frameworks/C++/ffead-cpp/setup-postgresql.sh
  34. 4 0
      frameworks/C++/ffead-cpp/setup.sh
  35. 5 5
      frameworks/C++/poco/benchmark_config.json
  36. 1 1
      frameworks/C++/silicon/CMakeLists.txt
  37. 9 6
      frameworks/C++/silicon/benchmark_config.json
  38. 1 1
      frameworks/C++/silicon/setup_lwan_mysql.sh
  39. 1 1
      frameworks/C++/silicon/setup_mhd_epoll_mysql.sh
  40. 1 1
      frameworks/C++/silicon/setup_mhd_tpc_mysql.sh
  41. 8 6
      frameworks/C++/treefrog/benchmark_config.json
  42. 6 80
      frameworks/C++/ulib/benchmark_config.json
  43. 4 2
      frameworks/C++/ulib/setup_json.sh
  44. 8 4
      frameworks/C++/wt/benchmark_config.json
  45. 5 4
      frameworks/C/duda/benchmark_config.json
  46. 0 1
      frameworks/C/h2o/CMakeLists.txt
  47. 5 4
      frameworks/C/h2o/benchmark_config.json
  48. 1 1
      frameworks/C/h2o/setup.sh
  49. 4 5
      frameworks/C/h2o/src/database.c
  50. 11 14
      frameworks/C/h2o/src/event_loop.c
  51. 2 2
      frameworks/C/h2o/src/event_loop.h
  52. 25 40
      frameworks/C/h2o/src/fortune.c
  53. 25 10
      frameworks/C/h2o/src/main.c
  54. 38 53
      frameworks/C/h2o/src/request_handler.c
  55. 5 2
      frameworks/C/h2o/src/request_handler.h
  56. 8 2
      frameworks/C/h2o/src/template.c
  57. 49 31
      frameworks/C/h2o/src/thread.c
  58. 20 12
      frameworks/C/h2o/src/thread.h
  59. 3 3
      frameworks/C/h2o/src/tls.c
  60. 1 1
      frameworks/C/h2o/src/tls.h
  61. 6 3
      frameworks/C/h2o/src/utility.h
  62. 68 85
      frameworks/C/h2o/src/world.c
  63. 4 3
      frameworks/C/haywire/benchmark_config.json
  64. 3 2
      frameworks/C/libreactor/benchmark_config.json
  65. 12 9
      frameworks/C/lwan/benchmark_config.json
  66. 8 6
      frameworks/C/onion/benchmark_config.json
  67. 25 21
      frameworks/CSharp/HttpListener/benchmark_config.json
  68. 25 20
      frameworks/CSharp/aspnet-stripped/benchmark_config.json
  69. 98 80
      frameworks/CSharp/aspnet/benchmark_config.json
  70. 1 0
      frameworks/CSharp/aspnetcore/Benchmarks/Configuration/AppSettings.cs
  71. 12 0
      frameworks/CSharp/aspnetcore/Benchmarks/Configuration/DatabaseServer.cs
  72. 30 12
      frameworks/CSharp/aspnetcore/Benchmarks/Configuration/Scenarios.cs
  73. 11 51
      frameworks/CSharp/aspnetcore/Benchmarks/Controllers/FortunesController.cs
  74. 42 0
      frameworks/CSharp/aspnetcore/Benchmarks/Controllers/MultipleQueriesController.cs
  75. 42 0
      frameworks/CSharp/aspnetcore/Benchmarks/Controllers/MultipleUpdatesController.cs
  76. 41 0
      frameworks/CSharp/aspnetcore/Benchmarks/Controllers/SingleQueryController.cs
  77. 49 2
      frameworks/CSharp/aspnetcore/Benchmarks/Data/ApplicationDbContext.cs
  78. 24 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/BatchUpdateString.cs
  79. 50 12
      frameworks/CSharp/aspnetcore/Benchmarks/Data/DapperDb.cs
  80. 27 3
      frameworks/CSharp/aspnetcore/Benchmarks/Data/EfDb.cs
  81. 4 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/Fortune.cs
  82. 19 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/IDb.cs
  83. 43 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/NoTransactionSqlServerConnection.cs
  84. 6 3
      frameworks/CSharp/aspnetcore/Benchmarks/Data/Random.cs
  85. 89 39
      frameworks/CSharp/aspnetcore/Benchmarks/Data/RawDb.cs
  86. 5 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/World.cs
  87. 0 47
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/ErrorHandlerMiddleware.cs
  88. 5 5
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/FortunesEfMiddleware.cs
  89. 14 5
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MiddlewareHelpers.cs
  90. 5 5
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleQueriesEfMiddleware.cs
  91. 61 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleUpdatesDapperMiddleware.cs
  92. 61 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleUpdatesEfMiddleware.cs
  93. 61 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleUpdatesRawMiddleware.cs
  94. 5 5
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/SingleQueryEfMiddleware.cs
  95. 2 2
      frameworks/CSharp/aspnetcore/Benchmarks/NuGet.Config
  96. 41 8
      frameworks/CSharp/aspnetcore/Benchmarks/Startup.cs
  97. 0 3
      frameworks/CSharp/aspnetcore/Benchmarks/appsettings.json
  98. 4 0
      frameworks/CSharp/aspnetcore/Benchmarks/appsettings.postgresql.json
  99. 21 10
      frameworks/CSharp/aspnetcore/Benchmarks/project.json
  100. 188 13
      frameworks/CSharp/aspnetcore/benchmark_config.json

+ 19 - 0
.github/PULL_REQUEST_TEMPLATE.md

@@ -0,0 +1,19 @@
+<!--
+....................................
+
+MAKE SURE YOU ARE OPENING A PULL
+REQUEST AGAINST THE CORRECT BRANCH
+
+....................................
+
+master = currently not accepting pull
+requests as we prepare for our Round 13
+final release.
+
+round-14 = new features, frameworks, 
+tests, bug fixes, and any other 
+changes that you would like to see in 
+the next round.
+
+....................................
+-->

+ 3 - 0
.gitignore

@@ -83,3 +83,6 @@ nimcache
 # crystal
 .crystal
 *.out
+
+#don't ignore merge request templates
+!.github/

+ 4 - 7
.travis.yml

@@ -55,7 +55,6 @@ env:
     - "TESTDIR=Erlang/cowboy"
     - "TESTDIR=Erlang/elli"
     - "TESTDIR=Erlang/mochiweb"
-    - "TESTDIR=Erlang/misultin"
     - "TESTDIR=Go/beego"
     - "TESTDIR=Go/echo"
     - "TESTDIR=Go/falcore"
@@ -98,11 +97,9 @@ env:
     - "TESTDIR=Java/servlet"
     - "TESTDIR=Java/servlet3-cass"
     - "TESTDIR=Java/spark"
-    - "TESTDIR=Java/sabina"
     - "TESTDIR=Java/spring"
     - "TESTDIR=Java/tapestry"
     - "TESTDIR=Java/undertow"
-    - "TESTDIR=Java/undertow-edge"
     - "TESTDIR=Java/undertow-jersey-c3p0"
     - "TESTDIR=Java/undertow-jersey-hikaricp"
     - "TESTDIR=Java/vertx"
@@ -119,6 +116,7 @@ env:
     - "TESTDIR=JavaScript/sailsjs"
     - "TESTDIR=Kotlin/hexagon"
     - "TESTDIR=Lua/lapis"
+    - "TESTDIR=Lua/octopus"
     - "TESTDIR=Lua/openresty"
     - "TESTDIR=Nim/jester"
     - "TESTDIR=Nim/nawak"
@@ -130,7 +128,7 @@ env:
     - "TESTDIR=PHP/cakephp"
     - "TESTDIR=PHP/hhvm"
     - "TESTDIR=PHP/php"
-    - "TESTDIR=PHP/cygnite-php-framework"
+    - "TESTDIR=PHP/cygnite"
     - "TESTDIR=PHP/codeigniter"
     - "TESTDIR=PHP/clancats"
     - "TESTDIR=PHP/fat-free"
@@ -140,7 +138,6 @@ env:
     - "TESTDIR=PHP/limonade"
     - "TESTDIR=PHP/lithium"
     - "TESTDIR=PHP/lumen"
-    - "TESTDIR=PHP/micromvc"
     - "TESTDIR=PHP/phalcon"
     - "TESTDIR=PHP/phalcon-micro"
     - "TESTDIR=PHP/phpixie"
@@ -168,6 +165,7 @@ env:
     - "TESTDIR=Python/turbogears"
     - "TESTDIR=Python/uwsgi"
     - "TESTDIR=Python/web2py"
+    - "TESTDIR=Python/weppy"
     - "TESTDIR=Python/wheezyweb"
     - "TESTDIR=Python/wsgi"
     - "TESTDIR=Racket/racket-ws"
@@ -188,13 +186,11 @@ env:
     - "TESTDIR=Scala/finatra"
     - "TESTDIR=Scala/fintrospect"
     - "TESTDIR=Scala/lift-stateless"
-    - "TESTDIR=Scala/plain"
     - "TESTDIR=Scala/play2-scala"
     - "TESTDIR=Scala/scalatra"
     - "TESTDIR=Scala/scruffy"
     - "TESTDIR=Scala/spray"
     - "TESTDIR=Scala/s-server"
-    - "TESTDIR=Scala/spray-es"
     - "TESTDIR=Scala/unfiltered"
     - "TESTDIR=Scala/http4s"
     - "TESTDIR=Scala/finch"
@@ -212,6 +208,7 @@ before_install:
 services:
   - postgresql
   - redis-server
+  - mongodb
 
 addons:
   postgresql: "9.3"

+ 0 - 2
config/create-postgres-database.sql

@@ -2,5 +2,3 @@ CREATE USER benchmarkdbuser WITH PASSWORD 'benchmarkdbpass';
 
 DROP DATABASE IF EXISTS hello_world;
 CREATE DATABASE hello_world WITH ENCODING 'UTF8';
-
-GRANT ALL PRIVILEGES ON DATABASE hello_world to benchmarkdbuser;

+ 0 - 29
config/create-postgres-old.sql

@@ -1,29 +0,0 @@
-DROP TABLE IF EXISTS World;
-CREATE TABLE  "World" (
-  id integer NOT NULL,
-  randomNumber integer NOT NULL default 0,
-  PRIMARY KEY  (id)
-);
-
-INSERT INTO "World" (id, randomNumber)
-SELECT x.id, random() * 10000 + 1 FROM generate_series(1,10000) as x(id);
-
-DROP TABLE IF EXISTS Fortune;
-CREATE TABLE  "Fortune" (
-  id integer NOT NULL,
-  message varchar(2048) NOT NULL,
-  PRIMARY KEY  (id)
-);
-
-INSERT INTO fortune (id, message) VALUES (1, 'fortune: No such file or directory');
-INSERT INTO fortune (id, message) VALUES (2, 'A computer scientist is someone who fixes things that aren''t broken.');
-INSERT INTO fortune (id, message) VALUES (3, 'After enough decimal places, nobody gives a damn.');
-INSERT INTO fortune (id, message) VALUES (4, 'A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1');
-INSERT INTO fortune (id, message) VALUES (5, 'A computer program does what you tell it to do, not what you want it to do.');
-INSERT INTO fortune (id, message) VALUES (6, 'Emacs is a nice operating system, but I prefer UNIX. — Tom Christaensen');
-INSERT INTO fortune (id, message) VALUES (7, 'Any program that runs right is obsolete.');
-INSERT INTO fortune (id, message) VALUES (8, 'A list is only as strong as its weakest link. — Donald Knuth');
-INSERT INTO fortune (id, message) VALUES (9, 'Feature: A bug with seniority.');
-INSERT INTO fortune (id, message) VALUES (10, 'Computers make very fast, very accurate mistakes.');
-INSERT INTO fortune (id, message) VALUES (11, '<script>alert("This should not be displayed in a browser alert box.");</script>');
-INSERT INTO fortune (id, message) VALUES (12, 'フレームワークのベンチマーク');

+ 0 - 29
config/create-postgres-upper-quote.sql

@@ -1,29 +0,0 @@
-DROP TABLE IF EXISTS "World";
-CREATE TABLE  "World" (
-  id integer NOT NULL,
-  randomNumber integer NOT NULL default 0,
-  PRIMARY KEY  (id)
-);
-
-INSERT INTO "World" (id, randomNumber)
-SELECT x.id, random() * 10000 + 1 FROM generate_series(1,10000) as x(id);
-
-DROP TABLE IF EXISTS "Fortune";
-CREATE TABLE "Fortune" (
-  id integer NOT NULL,
-  message varchar(2048) NOT NULL,
-  PRIMARY KEY  (id)
-);
-
-INSERT INTO "Fortune" (id, message) VALUES (1, 'fortune: No such file or directory');
-INSERT INTO "Fortune" (id, message) VALUES (2, 'A computer scientist is someone who fixes things that aren''t broken.');
-INSERT INTO "Fortune" (id, message) VALUES (3, 'After enough decimal places, nobody gives a damn.');
-INSERT INTO "Fortune" (id, message) VALUES (4, 'A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1');
-INSERT INTO "Fortune" (id, message) VALUES (5, 'A computer program does what you tell it to do, not what you want it to do.');
-INSERT INTO "Fortune" (id, message) VALUES (6, 'Emacs is a nice operating system, but I prefer UNIX. — Tom Christaensen');
-INSERT INTO "Fortune" (id, message) VALUES (7, 'Any program that runs right is obsolete.');
-INSERT INTO "Fortune" (id, message) VALUES (8, 'A list is only as strong as its weakest link. — Donald Knuth');
-INSERT INTO "Fortune" (id, message) VALUES (9, 'Feature: A bug with seniority.');
-INSERT INTO "Fortune" (id, message) VALUES (10, 'Computers make very fast, very accurate mistakes.');
-INSERT INTO "Fortune" (id, message) VALUES (11, '<script>alert("This should not be displayed in a browser alert box.");</script>');
-INSERT INTO "Fortune" (id, message) VALUES (12, 'フレームワークのベンチマーク');

+ 4 - 0
config/create-postgres.sql

@@ -5,6 +5,7 @@ CREATE TABLE  World (
   randomNumber integer NOT NULL default 0,
   PRIMARY KEY  (id)
 );
+GRANT SELECT, UPDATE ON World to benchmarkdbuser;
 
 INSERT INTO World (id, randomnumber)
 SELECT x.id, random() * 10000 + 1 FROM generate_series(1,10000) as x(id);
@@ -15,6 +16,7 @@ CREATE TABLE Fortune (
   message varchar(2048) NOT NULL,
   PRIMARY KEY  (id)
 );
+GRANT SELECT ON Fortune to benchmarkdbuser;
 
 INSERT INTO Fortune (id, message) VALUES (1, 'fortune: No such file or directory');
 INSERT INTO Fortune (id, message) VALUES (2, 'A computer scientist is someone who fixes things that aren''t broken.');
@@ -36,6 +38,7 @@ CREATE TABLE  "World" (
   randomNumber integer NOT NULL default 0,
   PRIMARY KEY  (id)
 );
+GRANT SELECT, UPDATE ON "World" to benchmarkdbuser;
 
 INSERT INTO "World" (id, randomnumber)
 SELECT x.id, random() * 10000 + 1 FROM generate_series(1,10000) as x(id);
@@ -46,6 +49,7 @@ CREATE TABLE "Fortune" (
   message varchar(2048) NOT NULL,
   PRIMARY KEY  (id)
 );
+GRANT SELECT ON "Fortune" to benchmarkdbuser;
 
 INSERT INTO "Fortune" (id, message) VALUES (1, 'fortune: No such file or directory');
 INSERT INTO "Fortune" (id, message) VALUES (2, 'A computer scientist is someone who fixes things that aren''t broken.');

+ 2 - 53
config/create.sql

@@ -3,59 +3,6 @@ DROP DATABASE IF EXISTS hello_world;
 CREATE DATABASE hello_world;
 USE hello_world;
 
-DROP TABLE IF EXISTS World;
-CREATE TABLE  World (
-  id int(10) unsigned NOT NULL auto_increment,
-  randomNumber int NOT NULL default 0,
-  PRIMARY KEY  (id)
-)
-ENGINE=INNODB;
-GRANT ALL ON hello_world.world TO 'benchmarkdbuser'@'%' IDENTIFIED BY 'benchmarkdbpass';
-GRANT ALL ON hello_world.world TO 'benchmarkdbuser'@'localhost' IDENTIFIED BY 'benchmarkdbpass';
-
-DROP PROCEDURE IF EXISTS load_data;
-
-DELIMITER #
-CREATE PROCEDURE load_data()
-BEGIN
-
-declare v_max int unsigned default 10000;
-declare v_counter int unsigned default 0;
-
-  TRUNCATE TABLE World;
-  START TRANSACTION;
-  while v_counter < v_max do
-    INSERT INTO World (randomNumber) VALUES ( floor(0 + (rand() * 10000)) );
-    SET v_counter=v_counter+1;
-  end while;
-  commit;
-END #
-
-DELIMITER ;
-
-CALL load_data();
-
-DROP TABLE IF EXISTS Fortune;
-CREATE TABLE  Fortune (
-  id int(10) unsigned NOT NULL auto_increment,
-  message varchar(2048) CHARACTER SET 'utf8' NOT NULL,
-  PRIMARY KEY  (id)
-)
-ENGINE=INNODB;
-
-INSERT INTO Fortune (message) VALUES ('fortune: No such file or directory');
-INSERT INTO Fortune (message) VALUES ('A computer scientist is someone who fixes things that aren''t broken.');
-INSERT INTO Fortune (message) VALUES ('After enough decimal places, nobody gives a damn.');
-INSERT INTO Fortune (message) VALUES ('A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1');
-INSERT INTO Fortune (message) VALUES ('A computer program does what you tell it to do, not what you want it to do.');
-INSERT INTO Fortune (message) VALUES ('Emacs is a nice operating system, but I prefer UNIX. — Tom Christaensen');
-INSERT INTO Fortune (message) VALUES ('Any program that runs right is obsolete.');
-INSERT INTO Fortune (message) VALUES ('A list is only as strong as its weakest link. — Donald Knuth');
-INSERT INTO Fortune (message) VALUES ('Feature: A bug with seniority.');
-INSERT INTO Fortune (message) VALUES ('Computers make very fast, very accurate mistakes.');
-INSERT INTO Fortune (message) VALUES ('<script>alert("This should not be displayed in a browser alert box.");</script>');
-INSERT INTO Fortune (message) VALUES ('フレームワークのベンチマーク');
-
 DROP TABLE IF EXISTS world;
 CREATE TABLE  world (
   id int(10) unsigned NOT NULL auto_increment,
@@ -63,6 +10,8 @@ CREATE TABLE  world (
   PRIMARY KEY  (id)
 )
 ENGINE=INNODB;
+GRANT SELECT, UPDATE ON hello_world.world TO 'benchmarkdbuser'@'%' IDENTIFIED BY 'benchmarkdbpass';
+GRANT SELECT, UPDATE ON hello_world.world TO 'benchmarkdbuser'@'localhost' IDENTIFIED BY 'benchmarkdbpass';
 
 DROP PROCEDURE IF EXISTS load_data;
 

+ 1 - 1
config/travis_setup.sh

@@ -83,7 +83,7 @@ echo "Populating Postgres database"
 psql --version
 sudo useradd benchmarkdbuser -p benchmarkdbpass
 sudo -u postgres psql template1 < config/create-postgres-database.sql
-sudo -u benchmarkdbuser psql hello_world < config/create-postgres.sql
+sudo -u postgres psql hello_world < config/create-postgres.sql
 sudo sed -i "s|#listen_addresses = 'localhost'|listen_addresses = '*'|g" /etc/postgresql/9.3/main/postgresql.conf
 sudo sed -i 's|max_connections = 255|max_connections = 500|g' /etc/postgresql/9.3/main/postgresql.conf
 sudo service postgresql stop

+ 19 - 0
config/upstart.example/tfb.conf

@@ -0,0 +1,19 @@
+# /etc/init/tfb.conf
+env TFB_REPOPARENT="/private"
+env TFB_REPONAME="FrameworkBenchmarks"
+env TFB_REPOURI="https://github.com/ashawnbandy-te-tfb/FrameworkBenchmarks.git"
+env TFB_MAILINGLIST="[email protected]"
+env TFB_MAILING_FROM="[email protected]"
+env TFB_LOGSFOLDER="/private/logs"
+env TFB_REPOBRANCH="45216-continuousbenchmarking-20160609-asb-2"
+setuid techempower
+setgid techempower
+umask 0002
+respawn
+respawn limit 5 2
+script
+  if [ ! -d "$TFB_REPOPARENT/$TFB_REPONAME" ]; then
+    git clone -b $TFB_REPOBRANCH $TFB_REPOURI $TFB_REPOPARENT/$TFB_REPONAME
+  fi
+  exec /$TFB_REPOPARENT/$TFB_REPONAME/toolset/run-continuously.sh
+end script

+ 9 - 9
deployment/vagrant-aws/README.md

@@ -17,8 +17,7 @@ production setup costs about TODO.
 
 ## Prerequisites
 
-* **A recent version of Vagrant**, like 1.6.3 (NOTE: `apt-get` is 
-too old, download the newest `deb` directly). See 
+* **A recent version of Vagrant**, like 1.6.3. See 
 [here](https://www.vagrantup.com/downloads.html) for downloads
 
 * **Vagrant AWS Plugin** from [here](https://github.com/mitchellh/vagrant-aws)
@@ -29,12 +28,13 @@ too old, download the newest `deb` directly). See
 
 ## Using Vagrant to Run Amazon-powered Virtual Machine
 
-The high level steps are 
-1) clone this project 
-2) set environment variables allowing us to log into your amazon account
-3) Run amazon setup script to create network
-4) Run `vagrant up --provider=aws` to launch into amazon
-5) Run `vagrant ssh` to log into the application server
+The high level steps are:
+
+* clone this project 
+* set environment variables allowing us to log into your amazon account
+* Run amazon setup script to create network
+* Run `vagrant up --provider=aws` to launch into amazon
+* Run `vagrant ssh` to log into the application server
 
 By default, your local git clone of this project will not by synced with the 
 remote amazon machines.
@@ -189,7 +189,7 @@ simultaneous benchmarks, so the better approach is to just increase all
 the IP addresses by 3 and run the additional benchmarks in the same VPC. 
 
 
-**I'm getting an AuthFailure but my Credientials are Correct!**:
+**I'm getting an AuthFailure but my Credentials are Correct!**:
 
 This normally means the AMI has been moved from public to private. Ubuntu's 
 Cloud image team occasionally does this. Navigate [here](http://cloud-images.ubuntu.com/trusty/current/) and find a more current AMI. 

+ 6 - 2
deployment/vagrant-aws/setup_aws.py

@@ -101,8 +101,12 @@ def run_aws(command, prefix=True, load=True):
   log.debug("Request : %s", command)
   result = subprocess.check_output(command, shell=True)
   log.debug("Response: %s", result)
-  if load:
-    return json.loads(result)
+  if load and result != '':
+    try:
+      return json.loads(result)
+    except ValueError:
+      log.error("Could not parse result '%s' as JSON for command '%s'", result, command)
+      raise
   else:
     return result
 

+ 13 - 9
frameworks/C++/cpoll_cppsp/benchmark_config.json

@@ -7,18 +7,19 @@
       "plaintext_url": "/plaintext",
       "port": 16969,
       "approach": "Realistic",
-      "classification": "Micro",
+      "classification": "Platform",
       "database": "None",
       "framework": "cpoll-cppsp",
       "language": "C++",
+      "flavor": "None",
       "orm": "Raw",
-      "platform": "CPoll",
+      "platform": "None",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "cpoll-cppsp",
       "notes": "",
-      "versus": ""
+      "versus": "cpoll_cppsp"
     },
     "raw": {
       "setup_file": "setup",
@@ -32,14 +33,15 @@
       "database": "MySQL",
       "framework": "cpoll-cppsp",
       "language": "C++",
+      "flavor": "None",
       "orm": "Raw",
-      "platform": "CPoll",
+      "platform": "None",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "cpoll-cppsp-raw",
       "notes": "",
-      "versus": ""
+      "versus": "cpoll_cppsp"
     },
     "postgres-raw": {
       "setup_file": "setup",
@@ -51,14 +53,15 @@
       "database": "Postgres",
       "framework": "cpoll-cppsp",
       "language": "C++",
+      "flavor": "None",
       "orm": "Raw",
-      "platform": "CPoll",
+      "platform": "None",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "cpoll-cppsp-raw",
       "notes": "",
-      "versus": ""
+      "versus": "cpoll_cppsp"
     },
     "postgres-raw-threadpool": {
       "setup_file": "setup",
@@ -70,14 +73,15 @@
       "database": "Postgres",
       "framework": "cpoll-cppsp",
       "language": "C++",
+      "flavor": "None",
       "orm": "Raw",
-      "platform": "CPoll",
+      "platform": "None",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "cpoll-pool",
       "notes": "Threadpool",
-      "versus": ""
+      "versus": "cpoll_cppsp"
     }
   }]
 }

+ 67 - 6
frameworks/C++/cutelyst/benchmark_config.json

@@ -16,7 +16,7 @@
                 "webserver": "None",
                 "os": "Linux",
                 "database_os": "Linux",
-                "display_name": "cutelyst-uwsgi",
+                "display_name": "cutelyst-pf",
                 "notes": "",
                 "versus": ""
             },
@@ -37,7 +37,7 @@
                 "webserver": "None",
                 "os": "Linux",
                 "database_os": "Linux",
-                "display_name": "cutelyst-uwsgi-pg-raw",
+                "display_name": "cutelyst-pf-pg-raw",
                 "notes": "",
                 "versus": ""
             },
@@ -58,7 +58,68 @@
                 "webserver": "None",
                 "os": "Linux",
                 "database_os": "Linux",
-                "display_name": "cutelyst-uwsgi-mysql-raw",
+                "display_name": "cutelyst-pf-mysql-raw",
+                "notes": "",
+                "versus": ""
+            },
+            "thread": {
+                "setup_file": "setup_thread",
+                "json_url": "/json",
+                "plaintext_url": "/plaintext",
+                "port": 8080,
+                "approach": "Realistic",
+                "classification": "Fullstack",
+                "database": "None",
+                "framework": "cutelyst",
+                "language": "C++",
+                "orm": "Raw",
+                "platform": "Qt",
+                "webserver": "None",
+                "os": "Linux",
+                "database_os": "Linux",
+                "display_name": "cutelyst-thread",
+                "notes": "",
+                "versus": ""
+            },
+            "thread-postgres-raw": {
+                "setup_file": "setup_thread",
+                "db_url": "/db_postgres",
+                "query_url": "/query_postgres?queries=",
+                "update_url": "/updates_postgres?queries=",
+                "fortune_url": "/fortunes_raw_postgres",
+                "port": 8080,
+                "approach": "Realistic",
+                "classification": "Platform",
+                "database": "Postgres",
+                "framework": "cutelyst",
+                "language": "C++",
+                "orm": "Raw",
+                "platform": "Qt",
+                "webserver": "None",
+                "os": "Linux",
+                "database_os": "Linux",
+                "display_name": "cutelyst-thread-pg-raw",
+                "notes": "",
+                "versus": ""
+            },
+            "thread-mysql-raw": {
+                "setup_file": "setup_thread",
+                "db_url": "/db_mysql",
+                "query_url": "/query_mysql?queries=",
+                "update_url": "/updates_mysql?queries=",
+                "fortune_url": "/fortunes_raw_mysql",
+                "port": 8080,
+                "approach": "Realistic",
+                "classification": "Platform",
+                "database": "MySQL",
+                "framework": "cutelyst",
+                "language": "C++",
+                "orm": "Raw",
+                "platform": "Qt",
+                "webserver": "None",
+                "os": "Linux",
+                "database_os": "Linux",
+                "display_name": "cutelyst-thread-mysql-raw",
                 "notes": "",
                 "versus": ""
             },
@@ -74,7 +135,7 @@
                 "language": "C++",
                 "orm": "Raw",
                 "platform": "Qt",
-                "webserver": "None",
+                "webserver": "nginx",
                 "os": "Linux",
                 "database_os": "Linux",
                 "display_name": "cutelyst-uwsgi-nginx",
@@ -95,7 +156,7 @@
                 "language": "C++",
                 "orm": "Raw",
                 "platform": "Qt",
-                "webserver": "None",
+                "webserver": "nginx",
                 "os": "Linux",
                 "database_os": "Linux",
                 "display_name": "cutelyst-uwsgi-nginx-pg-raw",
@@ -116,7 +177,7 @@
                 "language": "C++",
                 "orm": "Raw",
                 "platform": "Qt",
-                "webserver": "None",
+                "webserver": "nginx",
                 "os": "Linux",
                 "database_os": "Linux",
                 "display_name": "cutelyst-uwsgi-nginx-mysql-raw",

+ 2 - 1
frameworks/C++/cutelyst/config/config_socket.ini

@@ -3,9 +3,10 @@ master
 ; Increase listen queue used for nginx connecting to uWSGI. This matches
 ; net.ipv4.tcp_max_syn_backlog and net.core.somaxconn.
 ; for performance
+listen = 65535
 disable-logging
 ; use UNIX sockets instead of TCP loopback for performance
-socket = /tmp/uwsgi.sock
+socket = /var/tmp/uwsgi.sock
 ; allow nginx to access the UNIX socket
 chmod-socket = 666
 ; Avoid thundering herd problem http://uwsgi-docs.readthedocs.org/en/latest/articles/SerializingAccept.html .

+ 3 - 3
frameworks/C++/cutelyst/nginx.conf

@@ -1,6 +1,6 @@
 # This file is based on /usr/local/nginx/conf/nginx.conf.default.
 
-# One worker process per core
+worker_processes auto;
 error_log stderr error;
 
 events {
@@ -27,7 +27,7 @@ http {
     # some worker processes to be handling too connections relative to the
     # other workers based on an initial imbalance, so this is disabled for
     # now.
-#    keepalive_requests 1000;
+    keepalive_requests 1000;
 
     #keepalive_timeout  0;
     keepalive_timeout  65;
@@ -41,7 +41,7 @@ http {
         server_name  localhost;
 
         location / {
-            uwsgi_pass unix:/tmp/uwsgi.sock;
+            uwsgi_pass unix:/var/tmp/uwsgi.sock;
             include /usr/local/nginx/conf/uwsgi_params;
         }
     }

+ 4 - 4
frameworks/C++/cutelyst/setup.sh

@@ -3,20 +3,20 @@
 fw_depends cutelyst
 
 sed -i 's|DatabaseHostName=.*|DatabaseHostName='"$DBHOST"'|g' config/config.ini
-sed -i 's|SendDate=.*|SendDate=true|g' config/config.ini
+sed -i 's|SendDate=.*|SendDate=false|g' config/config.ini
 
 cd $IROOT
 mkdir cutelyst-benchmarks || true
 cd cutelyst-benchmarks
-rm -rf *
 
 QT_VERSION_MM=56
 export CMAKE_PREFIX_PATH=/opt/qt${QT_VERSION_MM}:${IROOT}
 
 cmake $TROOT -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$IROOT
 
-make clean
 make -j $MAX_THREADS
 
 export LD_LIBRARY_PATH=/opt/qt${QT_VERSION_MM}/lib:${IROOT}/lib/x86_64-linux-gnu/
-uwsgi --ini ${TROOT}/config/config.ini --cutelyst-app ${IROOT}/cutelyst-benchmarks/src/libcutelyst_benchmarks.so -p $(( $MAX_THREADS * 2 )) &
+export CUTELYST_CONFIG=${TROOT}/config/config.ini
+
+${IROOT}/bin/cutelyst-wsgi --http-socket :8080 -a ${IROOT}/cutelyst-benchmarks/src/libcutelyst_benchmarks.so -p $MAX_THREADS &

+ 21 - 0
frameworks/C++/cutelyst/setup_thread.sh

@@ -0,0 +1,21 @@
+#!/bin/bash
+
+fw_depends cutelyst
+
+sed -i 's|DatabaseHostName=.*|DatabaseHostName='"$DBHOST"'|g' config/config.ini
+sed -i 's|SendDate=.*|SendDate=false|g' config/config.ini
+
+cd $IROOT
+mkdir cutelyst-benchmarks || true
+cd cutelyst-benchmarks
+
+QT_VERSION_MM=56
+export CMAKE_PREFIX_PATH=/opt/qt${QT_VERSION_MM}:${IROOT}
+
+cmake $TROOT -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$IROOT
+
+make -j $MAX_THREADS
+
+export LD_LIBRARY_PATH=/opt/qt${QT_VERSION_MM}/lib:${IROOT}/lib/x86_64-linux-gnu/
+
+${IROOT}/bin/cutelyst-wsgi --ini ${TROOT}/config/config.ini --http-socket :8080 -a ${IROOT}/cutelyst-benchmarks/src/libcutelyst_benchmarks.so -t $MAX_THREADS &

+ 1 - 3
frameworks/C++/cutelyst/setup_uwsgi_nginx.sh

@@ -9,17 +9,15 @@ sed -i 's|include .*/conf/uwsgi_params;|include '"${NGINX_HOME}"'/conf/uwsgi_par
 cd $IROOT
 mkdir cutelyst-benchmarks || true
 cd cutelyst-benchmarks
-rm -rf *
 
 QT_VERSION_MM=56
 export CMAKE_PREFIX_PATH=/opt/qt${QT_VERSION_MM}:${IROOT}
 
 cmake $TROOT -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$IROOT
 
-make clean
 make -j $MAX_THREADS
 
 nginx -c $TROOT/nginx.conf
 
 export LD_LIBRARY_PATH=/opt/qt${QT_VERSION_MM}/lib:${IROOT}/lib/x86_64-linux-gnu/
-uwsgi --ini ${TROOT}/config/config_socket.ini --cutelyst-app ${IROOT}/cutelyst-benchmarks/src/libcutelyst_benchmarks.so -p $(( $MAX_THREADS * 2 )) &
+uwsgi --ini ${TROOT}/config/config_socket.ini --cutelyst-app ${IROOT}/cutelyst-benchmarks/src/libcutelyst_benchmarks.so -p $MAX_THREADS &

+ 12 - 4
frameworks/C++/cutelyst/src/cutelyst-benchmarks.cpp

@@ -4,7 +4,10 @@
 
 #include <QtSql/QSqlDatabase>
 #include <QtSql/QSqlError>
+#include <QCoreApplication>
+#include <QThread>
 #include <QDebug>
+#include <QMutexLocker>
 
 #include "root.h"
 #include "jsontest.h"
@@ -16,6 +19,8 @@
 
 using namespace Cutelyst;
 
+static QMutex mutex;
+
 cutelyst_benchmarks::cutelyst_benchmarks(QObject *parent) : Application(parent)
 {
 }
@@ -46,27 +51,30 @@ bool cutelyst_benchmarks::init()
 
 bool cutelyst_benchmarks::postFork()
 {
+    QMutexLocker locker(&mutex);
+
     QSqlDatabase db;
-    db = QSqlDatabase::addDatabase(QLatin1String("QPSQL"), QLatin1String("postgres"));
+    db = QSqlDatabase::addDatabase(QLatin1String("QPSQL"), QLatin1String("postgres-") + QThread::currentThread()->objectName());
     db.setDatabaseName(QLatin1String("hello_world"));
     db.setUserName(QLatin1String("benchmarkdbuser"));
     db.setPassword(QLatin1String("benchmarkdbpass"));
     db.setHostName(config(QLatin1String("DatabaseHostName")).toString());
     if (!db.open()) {
-        qDebug() << "Error opening db:" << db << db.lastError().databaseText();
+        qDebug() << "Error opening PostgreSQL db:" << db << db.connectionName() << db.lastError().databaseText();
         return false;
     }
 
-    db = QSqlDatabase::addDatabase(QLatin1String("QMYSQL"), QLatin1String("mysql"));
+    db = QSqlDatabase::addDatabase(QLatin1String("QMYSQL"), QLatin1String("mysql-") + QThread::currentThread()->objectName());
     db.setDatabaseName(QLatin1String("hello_world"));
     db.setUserName(QLatin1String("benchmarkdbuser"));
     db.setPassword(QLatin1String("benchmarkdbpass"));
     db.setHostName(config(QLatin1String("DatabaseHostName")).toString());
     if (!db.open()) {
-        qDebug() << "Error opening db:" << db << db.lastError().databaseText();
+        qDebug() << "Error opening MySQL db:" << db << db.connectionName() << db.lastError().databaseText();
         return false;
     }
 
+    qDebug() << "Connections" << QCoreApplication::applicationPid() << QThread::currentThread() << QSqlDatabase::connectionNames();
 //    db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), QLatin1String("sqlite"));
 //    if (!db.open()) {
 //        qDebug() << "Error opening db:" << db << db.lastError().databaseText();

+ 6 - 6
frameworks/C++/cutelyst/src/databaseupdatestest.cpp

@@ -18,10 +18,10 @@ void DatabaseUpdatesTest::updates_postgres(Context *c)
 {
     QSqlQuery query = CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT randomNumber FROM world WHERE id = :id"),
-                QSqlDatabase::database(QLatin1String("postgres")));
+                QSqlDatabase::database(QLatin1String("postgres-") + QThread::currentThread()->objectName()));
     QSqlQuery updateQuery = CPreparedSqlQueryForDatabase(
                 QLatin1String("UPDATE world SET randomNumber = :randomNumber WHERE id = :id"),
-                QSqlDatabase::database(QLatin1String("postgres")));
+                QSqlDatabase::database(QLatin1String("postgres-") + QThread::currentThread()->objectName()));
     processQuery(c, query, updateQuery);
 }
 
@@ -29,10 +29,10 @@ void DatabaseUpdatesTest::updates_mysql(Context *c)
 {
     QSqlQuery query = CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT randomNumber FROM world WHERE id = :id"),
-                QSqlDatabase::database(QLatin1String("mysql")));
+                QSqlDatabase::database(QLatin1String("mysql-") + QThread::currentThread()->objectName()));
     QSqlQuery updateQuery = CPreparedSqlQueryForDatabase(
                 QLatin1String("UPDATE world SET randomNumber = :randomNumber WHERE id = :id"),
-                QSqlDatabase::database(QLatin1String("mysql")));
+                QSqlDatabase::database(QLatin1String("mysql-") + QThread::currentThread()->objectName()));
     processQuery(c, query, updateQuery);
 }
 
@@ -48,7 +48,7 @@ void DatabaseUpdatesTest::processQuery(Context *c, QSqlQuery &query, QSqlQuery &
     }
 
     for (int i = 0; i < queries; ++i) {
-        int id = (qrand() % 9999) + 1;
+        int id = (qrand() % 10000) + 1;
 
         query.bindValue(QStringLiteral(":id"), id);
         if (!query.exec() || !query.next()) {
@@ -56,7 +56,7 @@ void DatabaseUpdatesTest::processQuery(Context *c, QSqlQuery &query, QSqlQuery &
             return;
         }
 
-        int randomNumber = (qrand() % 9999) + 1;
+        int randomNumber = (qrand() % 10000) + 1;
         updateQuery.bindValue(QStringLiteral(":id"), id);
         updateQuery.bindValue(QStringLiteral(":randomNumber"), randomNumber);
         if (!updateQuery.exec()) {

+ 2 - 2
frameworks/C++/cutelyst/src/fortunetest.cpp

@@ -34,14 +34,14 @@ QSqlQuery FortuneTest::postgresQuery()
 {
     return CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT id, message FROM fortune"),
-                QSqlDatabase::database(QLatin1String("postgres")));
+                QSqlDatabase::database(QLatin1String("postgres-") + QThread::currentThread()->objectName()));
 }
 
 QSqlQuery FortuneTest::mysqlQuery()
 {
     return CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT id, message FROM fortune"),
-                QSqlDatabase::database(QLatin1String("mysql")));
+                QSqlDatabase::database(QLatin1String("mysql-") + QThread::currentThread()->objectName()));
 }
 
 static bool caseSensitiveLessThan(const Fortune &a1, const Fortune &a2)

+ 4 - 4
frameworks/C++/cutelyst/src/multipledatabasequeriestest.cpp

@@ -18,7 +18,7 @@ void MultipleDatabaseQueriesTest::query_postgres(Context *c)
 {
     QSqlQuery query = CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT id, randomNumber FROM world WHERE id = :id"),
-                QSqlDatabase::database(QLatin1String("postgres")));
+                QSqlDatabase::database(QLatin1String("postgres-") + QThread::currentThread()->objectName()));
     processQuery(c, query);
 }
 
@@ -26,7 +26,7 @@ void MultipleDatabaseQueriesTest::query_mysql(Context *c)
 {
     QSqlQuery query = CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT id, randomNumber FROM world WHERE id = :id"),
-                QSqlDatabase::database(QLatin1String("mysql")));
+                QSqlDatabase::database(QLatin1String("mysql-") + QThread::currentThread()->objectName()));
     processQuery(c, query);
 }
 
@@ -42,9 +42,9 @@ void MultipleDatabaseQueriesTest::processQuery(Context *c, QSqlQuery &query)
     }
 
     for (int i = 0; i < queries; ++i) {
-        int id = qrand() % 9999;
+        int id = (qrand() % 10000) + 1;
 
-        query.bindValue(QStringLiteral(":id"), id + 1);
+        query.bindValue(QStringLiteral(":id"), id);
         if (!query.exec() || !query.next()) {
             c->res()->setStatus(Response::InternalServerError);
             return;

+ 4 - 4
frameworks/C++/cutelyst/src/singledatabasequerytest.cpp

@@ -17,7 +17,7 @@ void SingleDatabaseQueryTest::db_postgres(Context *c)
 {
     QSqlQuery query = CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT id, randomNumber FROM world WHERE id = :id"),
-                QSqlDatabase::database(QLatin1String("postgres")));
+                QSqlDatabase::database(QLatin1String("postgres-") + QThread::currentThread()->objectName()));
     processQuery(c, query);
 }
 
@@ -25,15 +25,15 @@ void SingleDatabaseQueryTest::db_mysql(Context *c)
 {
     QSqlQuery query = CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT id, randomNumber FROM world WHERE id = :id"),
-                QSqlDatabase::database(QLatin1String("mysql")));
+                QSqlDatabase::database(QLatin1String("mysql-") + QThread::currentThread()->objectName()));
     processQuery(c, query);
 }
 
 void SingleDatabaseQueryTest::processQuery(Context *c, QSqlQuery &query)
 {
-    int id = qrand() % 9999;
+    int id = (qrand() % 10000) + 1;
 
-    query.bindValue(QStringLiteral(":id"), id + 1);
+    query.bindValue(QStringLiteral(":id"), id);
     if (!query.exec() || !query.next()) {
         c->res()->setStatus(Response::InternalServerError);
         return;

+ 188 - 4
frameworks/C++/ffead-cpp/benchmark_config.json

@@ -11,19 +11,203 @@
       "update_url": "/te-benchmark/updates?queries=",
       "port": 8080,
       "approach": "Realistic",
-      "classification": "Fullstack",
+      "classification": "micro",
       "database": "MongoDB",
       "framework": "ffead-cpp",
       "language": "C++",
+      "flavor": "None",
+      "orm": "Full",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ffead-cpp-mongo",
+      "notes": "",
+      "versus": ""
+    },
+	"mysql": {
+      "setup_file": "setup-mysql",
+      "json_url": "/te-benchmark/json",
+      "plaintext_url": "/te-benchmark/plaintext",
+      "db_url": "/te-benchmark/db",
+      "query_url": "/te-benchmark/queries?queries=",
+      "fortune_url": "/te-benchmark/fortunes",
+      "update_url": "/te-benchmark/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "mysql",
+      "framework": "ffead-cpp",
+      "language": "C++",
+      "orm": "Full",
+      "platform": "ffead-cpp",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ffead-cpp-mysql",
+      "notes": "",
+      "versus": ""
+    },
+	"postgresql": {
+      "setup_file": "setup-postgresql",
+      "json_url": "/te-benchmark/json",
+      "plaintext_url": "/te-benchmark/plaintext",
+      "db_url": "/te-benchmark/db",
+      "query_url": "/te-benchmark/queries?queries=",
+      "fortune_url": "/te-benchmark/fortunes",
+      "update_url": "/te-benchmark/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "postgres",
+      "framework": "ffead-cpp",
+      "language": "C++",
       "orm": "Full",
       "platform": "ffead-cpp",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "ffead-cpp",
+      "display_name": "ffead-cpp-postgresql",
       "notes": "",
       "versus": ""
+    },
+    "apache2-mongo": {
+      "setup_file": "setup-apache2",
+      "json_url": "/te-benchmark/json",
+      "plaintext_url": "/te-benchmark/plaintext",
+      "db_url": "/te-benchmark/db",
+      "query_url": "/te-benchmark/queries?queries=",
+      "fortune_url": "/te-benchmark/fortunes",
+      "update_url": "/te-benchmark/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "mongodb",
+      "framework": "ffead-cpp",
+      "language": "C++",
+      "orm": "Full",
+      "platform": "ffead-cpp-apache2",
+      "webserver": "apache2",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ffead-cpp-apache2-mongo",
+      "notes": "",
+      "versus": ""
+    },
+	"apache2-mysql": {
+      "setup_file": "setup-apache2-mysql",
+      "json_url": "/te-benchmark/json",
+      "plaintext_url": "/te-benchmark/plaintext",
+      "db_url": "/te-benchmark/db",
+      "query_url": "/te-benchmark/queries?queries=",
+      "fortune_url": "/te-benchmark/fortunes",
+      "update_url": "/te-benchmark/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "mysql",
+      "framework": "ffead-cpp",
+      "language": "C++",
+      "orm": "Full",
+      "platform": "ffead-cpp-apache2",
+      "webserver": "apache2",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ffead-cpp-apache2-mysql",
+      "notes": "",
+      "versus": ""
+    },
+	"apache2-postgresql": {
+      "setup_file": "setup-apache2-postgresql",
+      "json_url": "/te-benchmark/json",
+      "plaintext_url": "/te-benchmark/plaintext",
+      "db_url": "/te-benchmark/db",
+      "query_url": "/te-benchmark/queries?queries=",
+      "fortune_url": "/te-benchmark/fortunes",
+      "update_url": "/te-benchmark/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "postgres",
+      "framework": "ffead-cpp",
+      "language": "C++",
+      "orm": "Full",
+      "platform": "ffead-cpp-apache2",
+      "webserver": "apache2",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ffead-cpp-apache2-postgresql",
+      "notes": "",
+      "versus": ""
+    },
+    "nginx-mongo": {
+      "setup_file": "setup-nginx",
+      "json_url": "/te-benchmark/json",
+      "plaintext_url": "/te-benchmark/plaintext",
+      "db_url": "/te-benchmark/db",
+      "query_url": "/te-benchmark/queries?queries=",
+      "fortune_url": "/te-benchmark/fortunes",
+      "update_url": "/te-benchmark/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "mongodb",
+      "framework": "ffead-cpp",
+      "language": "C++",
+      "orm": "Full",
+      "platform": "ffead-cpp-nginx",
+      "webserver": "nginx",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ffead-cpp-nginx-mongo",
+      "notes": "",
+      "versus": ""
+    },
+	"nginx-mysql": {
+      "setup_file": "setup-nginx-mysql",
+      "json_url": "/te-benchmark/json",
+      "plaintext_url": "/te-benchmark/plaintext",
+      "db_url": "/te-benchmark/db",
+      "query_url": "/te-benchmark/queries?queries=",
+      "fortune_url": "/te-benchmark/fortunes",
+      "update_url": "/te-benchmark/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "mysql",
+      "framework": "ffead-cpp",
+      "language": "C++",
+      "orm": "Full",
+      "platform": "ffead-cpp-nginx",
+      "webserver": "nginx",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ffead-cpp-nginx-mysql",
+      "notes": "",
+      "versus": ""
+    },
+	"nginx-postgresql": {
+      "setup_file": "setup-nginx-postgresql",
+      "json_url": "/te-benchmark/json",
+      "plaintext_url": "/te-benchmark/plaintext",
+      "db_url": "/te-benchmark/db",
+      "query_url": "/te-benchmark/queries?queries=",
+      "fortune_url": "/te-benchmark/fortunes",
+      "update_url": "/te-benchmark/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "postgres",
+      "framework": "ffead-cpp",
+      "language": "C++",
+      "orm": "Full",
+      "platform": "ffead-cpp-nginx",
+      "webserver": "nginx",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ffead-cpp-nginx-postgresql",
+      "notes": "",
+      "versus": "ffead-cpp"
     }
   }]
-}
-
+}

+ 16 - 0
frameworks/C++/ffead-cpp/setup-apache2-mysql.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+
+fw_depends apache
+fw_depends ffead-cpp-apache
+
+export FFEAD_CPP_PATH=/var/www/ffead-cpp-2.0
+export LD_LIBRARY_PATH=$IROOT:$FFEAD_CPP_PATH/lib:$LD_LIBRARY_PATH
+echo $FFEAD_CPP_PATH
+echo $LD_LIBRARY_PATH
+sudo rm -f $FFEAD_CPP_PATH/*.cntrl
+sudo rm -f $FFEAD_CPP_PATH/tmp/*.sess
+sudo cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormmysql.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+sudo rm -rf $FFEAD_CPP_PATH/lib
+sudo cp -Rf $FFEAD_CPP_PATH/libsql $FFEAD_CPP_PATH/lib
+sudo cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldsql.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
+sudo /etc/init.d/apache2 restart > ffead.log 2>&1

+ 16 - 0
frameworks/C++/ffead-cpp/setup-apache2-postgresql.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+
+fw_depends apache
+fw_depends ffead-cpp-apache
+
+export FFEAD_CPP_PATH=/var/www/ffead-cpp-2.0
+export LD_LIBRARY_PATH=$IROOT:$FFEAD_CPP_PATH/lib:$LD_LIBRARY_PATH
+echo $FFEAD_CPP_PATH
+echo $LD_LIBRARY_PATH
+sudo rm -f $FFEAD_CPP_PATH/*.cntrl
+sudo rm -f $FFEAD_CPP_PATH/tmp/*.sess
+sudo cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormpostgresql.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+sudo rm -rf $FFEAD_CPP_PATH/lib
+sudo cp -Rf $FFEAD_CPP_PATH/libsql $FFEAD_CPP_PATH/lib
+sudo cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldsql.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
+sudo /etc/init.d/apache2 restart > ffead.log 2>&1

+ 16 - 0
frameworks/C++/ffead-cpp/setup-apache2.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+
+fw_depends apache
+fw_depends ffead-cpp-apache
+
+export FFEAD_CPP_PATH=/var/www/ffead-cpp-2.0
+export LD_LIBRARY_PATH=$IROOT:$FFEAD_CPP_PATH/lib:$LD_LIBRARY_PATH
+echo $FFEAD_CPP_PATH
+echo $LD_LIBRARY_PATH
+sudo rm -f $FFEAD_CPP_PATH/*.cntrl
+sudo rm -f $FFEAD_CPP_PATH/tmp/*.sess
+sudo cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormmongo.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+sudo rm -rf $FFEAD_CPP_PATH/lib
+sudo cp -Rf $FFEAD_CPP_PATH/libmongo $FFEAD_CPP_PATH/lib
+sudo cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldmongo.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
+sudo /etc/init.d/apache2 restart > ffead.log 2>&1

+ 16 - 0
frameworks/C++/ffead-cpp/setup-mysql.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+
+fw_depends ffead-cpp
+
+export FFEAD_CPP_PATH=$TROOT/ffead-cpp-2.0
+export LD_LIBRARY_PATH=$IROOT:$FFEAD_CPP_PATH/lib:$LD_LIBRARY_PATH
+echo $FFEAD_CPP_PATH
+echo $LD_LIBRARY_PATH
+rm -f $FFEAD_CPP_PATH/*.cntrl
+rm -f $FFEAD_CPP_PATH/tmp/*.sess
+cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormmysql.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+rm -rf $FFEAD_CPP_PATH/lib
+cp -Rf $FFEAD_CPP_PATH/libsql $FFEAD_CPP_PATH/lib
+cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldsql.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
+$TROOT/ffead-cpp-2.0/CHS $FFEAD_CPP_PATH > ffead.log 2>&1
+

+ 16 - 0
frameworks/C++/ffead-cpp/setup-nginx-mysql.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+
+fw_depends ffead-cpp-nginx
+
+export FFEAD_CPP_PATH=$TROOT/ffead-cpp-2.0
+export LD_LIBRARY_PATH=$IROOT:$FFEAD_CPP_PATH/lib:$LD_LIBRARY_PATH
+echo $FFEAD_CPP_PATH
+echo $LD_LIBRARY_PATH
+rm -f $FFEAD_CPP_PATH/*.cntrl
+rm -f $FFEAD_CPP_PATH/tmp/*.sess
+cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormmysql.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+rm -rf $FFEAD_CPP_PATH/lib
+cp -Rf $FFEAD_CPP_PATH/libsql $FFEAD_CPP_PATH/lib
+cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldsql.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
+$IROOT/nginxfc/sbin/nginx > ffead.log 2>&1
+

+ 15 - 0
frameworks/C++/ffead-cpp/setup-nginx-postgresql.sh

@@ -0,0 +1,15 @@
+#!/bin/bash
+
+fw_depends ffead-cpp-nginx
+
+export FFEAD_CPP_PATH=$TROOT/ffead-cpp-2.0
+export LD_LIBRARY_PATH=$IROOT:$FFEAD_CPP_PATH/lib:$LD_LIBRARY_PATH
+echo $FFEAD_CPP_PATH
+echo $LD_LIBRARY_PATH
+rm -f $FFEAD_CPP_PATH/*.cntrl
+rm -f $FFEAD_CPP_PATH/tmp/*.sess
+cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormpostgresql.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+rm -rf $FFEAD_CPP_PATH/lib
+cp -Rf $FFEAD_CPP_PATH/libsql $FFEAD_CPP_PATH/lib
+cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldsql.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
+$IROOT/nginxfc/sbin/nginx > ffead.log 2>&1

+ 15 - 0
frameworks/C++/ffead-cpp/setup-nginx.sh

@@ -0,0 +1,15 @@
+#!/bin/bash
+
+fw_depends ffead-cpp-nginx
+
+export FFEAD_CPP_PATH=$TROOT/ffead-cpp-2.0
+export LD_LIBRARY_PATH=$IROOT:$FFEAD_CPP_PATH/lib:$LD_LIBRARY_PATH
+echo $FFEAD_CPP_PATH
+echo $LD_LIBRARY_PATH
+rm -f $FFEAD_CPP_PATH/*.cntrl
+rm -f $FFEAD_CPP_PATH/tmp/*.sess
+cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormmongo.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+rm -rf $FFEAD_CPP_PATH/lib
+cp -Rf $FFEAD_CPP_PATH/libmongo $FFEAD_CPP_PATH/lib
+cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldmongo.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
+$IROOT/nginxfc/sbin/nginx > ffead.log 2>&1

+ 16 - 0
frameworks/C++/ffead-cpp/setup-postgresql.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+
+fw_depends ffead-cpp
+
+export FFEAD_CPP_PATH=$TROOT/ffead-cpp-2.0
+export LD_LIBRARY_PATH=$IROOT:$FFEAD_CPP_PATH/lib:$LD_LIBRARY_PATH
+echo $FFEAD_CPP_PATH
+echo $LD_LIBRARY_PATH
+rm -f $FFEAD_CPP_PATH/*.cntrl
+rm -f $FFEAD_CPP_PATH/tmp/*.sess
+cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormpostgresql.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+rm -rf $FFEAD_CPP_PATH/lib
+cp -Rf $FFEAD_CPP_PATH/libsql $FFEAD_CPP_PATH/lib
+cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldsql.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
+$TROOT/ffead-cpp-2.0/CHS $FFEAD_CPP_PATH > ffead.log 2>&1
+

+ 4 - 0
frameworks/C++/ffead-cpp/setup.sh

@@ -8,5 +8,9 @@ echo $FFEAD_CPP_PATH
 echo $LD_LIBRARY_PATH
 rm -f $FFEAD_CPP_PATH/*.cntrl
 rm -f $FFEAD_CPP_PATH/tmp/*.sess
+cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormmongo.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+rm -rf $FFEAD_CPP_PATH/lib
+cp -Rf $FFEAD_CPP_PATH/libmongo $FFEAD_CPP_PATH/lib
+cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldmongo.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
 $TROOT/ffead-cpp-2.0/CHS $FFEAD_CPP_PATH > ffead.log 2>&1
 

+ 5 - 5
frameworks/C++/poco/benchmark_config.json

@@ -8,17 +8,17 @@
       "approach": "Realistic",
       "classification": "Platform",
       "database": "none",
-      "framework": "POCO",
+      "framework": "None",
       "language": "C++",
+      "flavor": "None",
       "orm": "Raw",
-      "platform": "POCO",
-      "webserver": "poco",
+      "platform": "None",
+      "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "POCO",
       "notes": "",
-      "versus": ""
+      "versus": "poco"
     }
   }]
 }
-

+ 1 - 1
frameworks/C++/silicon/CMakeLists.txt

@@ -16,4 +16,4 @@ set_target_properties(silicon_epoll_mysql PROPERTIES COMPILE_FLAGS "-DTFB_USE_EP
 target_link_libraries(silicon_epoll_mysql microhttpd mysqlclient)
 
 add_executable(silicon_lwan_mysql techempower_lwan.cc)
-target_link_libraries(silicon_lwan_mysql mysqlclient lwan-common curl z pthread dl)
+target_link_libraries(silicon_lwan_mysql mysqlclient lwan curl z pthread dl luajit-5.1)

+ 9 - 6
frameworks/C++/silicon/benchmark_config.json

@@ -15,14 +15,15 @@
       "database": "MySQL",
       "framework": "silicon",
       "language": "C++",
+      "flavor": "None",
       "orm": "Full",
-      "platform": "Silicon",
+      "platform": "None",
       "webserver": "microhttpd",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "silicon-tpc-mysql",
       "notes": "",
-      "versus": ""
+      "versus": "silicon"
     },
     "epoll-mysql": {
       "setup_file": "setup_mhd_epoll_mysql",
@@ -38,14 +39,15 @@
       "database": "MySQL",
       "framework": "silicon",
       "language": "C++",
+      "flavor": "None",
       "orm": "Full",
-      "platform": "Silicon",
+      "platform": "None",
       "webserver": "microhttpd",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "silicon-epoll-mysql",
       "notes": "",
-      "versus": ""
+      "versus": "silicon"
     },
     "lwan-mysql": {
       "setup_file": "setup_lwan_mysql",
@@ -61,14 +63,15 @@
       "database": "MySQL",
       "framework": "silicon",
       "language": "C++",
+      "flavor": "None",
       "orm": "Full",
-      "platform": "Silicon",
+      "platform": "None",
       "webserver": "Lwan",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "silicon-lwan-mysql",
       "notes": "",
-      "versus": ""
+      "versus": "silicon"
     }
     
   }]

+ 1 - 1
frameworks/C++/silicon/setup_lwan_mysql.sh

@@ -5,7 +5,7 @@ fw_depends silicon lwan
 rm -rf build
 mkdir build
 cd build
-cmake .. -DCMAKE_CXX_COMPILER=clang++-3.5
+cmake .. -DCMAKE_CXX_COMPILER=clang++-3.8
 make silicon_lwan_mysql
 
 $TROOT/build/silicon_lwan_mysql ${DBHOST} 8080 &

+ 1 - 1
frameworks/C++/silicon/setup_mhd_epoll_mysql.sh

@@ -5,7 +5,7 @@ fw_depends silicon microhttpd
 rm -rf build
 mkdir build
 cd build
-cmake .. -DCMAKE_CXX_COMPILER=clang++-3.5
+cmake .. -DCMAKE_CXX_COMPILER=clang++-3.8
 make silicon_epoll_mysql
 
 $TROOT/build/silicon_epoll_mysql ${DBHOST} 8080 ${MAX_THREADS} &

+ 1 - 1
frameworks/C++/silicon/setup_mhd_tpc_mysql.sh

@@ -5,7 +5,7 @@ fw_depends silicon microhttpd
 rm -rf build
 mkdir build
 cd build
-cmake .. -DCMAKE_CXX_COMPILER=clang++-3.5
+cmake .. -DCMAKE_CXX_COMPILER=clang++-3.8
 make silicon_tpc_mysql
 
 $TROOT/build/silicon_tpc_mysql ${DBHOST} 8080 ${MAX_THREADS} &

+ 8 - 6
frameworks/C++/treefrog/benchmark_config.json

@@ -16,7 +16,7 @@
       "framework": "treefrog",
       "language": "C++",
       "orm": "Micro",
-      "platform": "Treefrog",
+      "platform": "None",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
@@ -38,14 +38,15 @@
       "database": "MySQL",
       "framework": "treefrog",
       "language": "C++",
+      "flavor": "None",
       "orm": "Micro",
-      "platform": "Treefrog",
+      "platform": "None",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "treefrog-hybrid",
       "notes": "",
-      "versus": ""
+      "versus": "treefrog"
     },
     "postgres": {
       "setup_file": "setup-postgres",
@@ -61,8 +62,9 @@
       "database": "Postgres",
       "framework": "treefrog",
       "language": "C++",
+      "flavor": "None",
       "orm": "Micro",
-      "platform": "Treefrog",
+      "platform": "None",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
@@ -85,13 +87,13 @@
       "framework": "treefrog",
       "language": "C++",
       "orm": "Micro",
-      "platform": "Treefrog",
+      "platform": "None",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "treefrog-mongo",
       "notes": "",
-      "versus": ""
+      "versus": "treefrog"
     }
   }]
 }

+ 6 - 80
frameworks/C++/ulib/benchmark_config.json

@@ -11,7 +11,7 @@
       "framework": "ULib",
       "language": "C++",
       "orm": "Micro",
-      "platform": "ULib",
+      "platform": "None",
       "webserver": "ULib",
       "os": "Linux",
       "database_os": "Linux",
@@ -19,7 +19,7 @@
       "notes": "",
       "versus": ""
     },
-    "json_normal": {
+    "json": {
       "setup_file": "setup_json",
       "json_url": "/json",
       "port": 8080,
@@ -29,61 +29,7 @@
       "framework": "ULib",
       "language": "C++",
       "orm": "Micro",
-      "platform": "ULib",
-      "webserver": "ULib",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ULib",
-      "notes": "",
-      "versus": ""
-    },
-    "json_medium": {
-      "setup_file": "setup_json_medium",
-      "json_url": "/json",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "None",
-      "framework": "ULib",
-      "language": "C++",
-      "orm": "Micro",
-      "platform": "ULib",
-      "webserver": "ULib",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ULib",
-      "notes": "",
-      "versus": ""
-    },
-    "json_large": {
-      "setup_file": "setup_json_large",
-      "json_url": "/json",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "None",
-      "framework": "ULib",
-      "language": "C++",
-      "orm": "Micro",
-      "platform": "ULib",
-      "webserver": "ULib",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ULib",
-      "notes": "",
-      "versus": ""
-    },
-    "json_extra": {
-      "setup_file": "setup_json_extra",
-      "json_url": "/json",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "None",
-      "framework": "ULib",
-      "language": "C++",
-      "orm": "Micro",
-      "platform": "ULib",
+      "platform": "None",
       "webserver": "ULib",
       "os": "Linux",
       "database_os": "Linux",
@@ -104,7 +50,7 @@
       "framework": "ULib",
       "language": "C++",
       "orm": "Micro",
-      "platform": "ULib",
+      "platform": "None",
       "webserver": "ULib",
       "os": "Linux",
       "database_os": "Linux",
@@ -125,7 +71,7 @@
       "framework": "ULib",
       "language": "C++",
       "orm": "Micro",
-      "platform": "ULib",
+      "platform": "None",
       "webserver": "ULib",
       "os": "Linux",
       "database_os": "Linux",
@@ -133,26 +79,6 @@
       "notes": "",
       "versus": ""
     },
-    "sqlite": {
-      "setup_file": "setup_sqlite",
-      "db_url": "/db",
-      "query_url": "/query?queries=",
-      "fortune_url": "/fortune",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "SQLite",
-      "framework": "ULib",
-      "language": "C++",
-      "orm": "Micro",
-      "platform": "ULib",
-      "webserver": "ULib",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ULib-sqlite",
-      "notes": "",
-      "versus": ""
-    },
     "mongodb": {
       "setup_file": "setup_mongodb",
       "db_url": "/mdb",
@@ -166,7 +92,7 @@
       "framework": "ULib",
       "language": "C++",
       "orm": "Micro",
-      "platform": "ULib",
+      "platform": "None",
       "webserver": "ULib",
       "os": "Linux",
       "database_os": "Linux",

+ 4 - 2
frameworks/C++/ulib/setup_json.sh

@@ -2,8 +2,10 @@
 
 fw_depends ulib
 
-# Travis is really broken!!
-if [ "$TRAVIS" == "true" ]; then
+# Travis is broken
+if [ "$TRAVIS" != "true" ]; then
+MAX_THREADS=$(( 3 * $MAX_THREADS / 2 ))
+else
 MAX_THREADS=$(( 2 * $MAX_THREADS ))
 fi
 

+ 8 - 4
frameworks/C++/wt/benchmark_config.json

@@ -15,13 +15,15 @@
      	"database": "MySQL",
      	"framework": "wt",
      	"language": "C++",
+          "flavor": "None",
      	"orm": "Full",
-     	"platform": "Wt",
+     	"platform": "None",
      	"webserver": "None",
      	"os": "Linux",
      	"database_os": "Linux",
      	"display_name": "wt",
-     	"notes": ""
+     	"notes": "",
+          "versus": "wt"
      },
      "postgres": {
      	"setup_file": "setup_postgres",
@@ -35,13 +37,15 @@
      	"database": "Postgres",
      	"framework": "wt",
      	"language": "C++",
+          "flavor": "None",
      	"orm": "Full",
-     	"platform": "Wt",
+     	"platform": "None",
      	"webserver": "None",
      	"os": "Linux",
      	"database_os": "Linux",
      	"display_name": "wt-postgres",
-     	"notes": ""
+     	"notes": "",
+          "versus": "wt"
      }
   }]
 }

+ 5 - 4
frameworks/C/duda/benchmark_config.json

@@ -8,17 +8,18 @@
       "port": 2001,
       "approach": "Realistic",
       "classification": "Platform",
-      "database": "none",
-      "framework": "Duda",
+      "database": "None",
+      "framework": "None",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
-      "platform": "Duda I/O",
+      "platform": "duda",
       "webserver": "Monkey",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "Duda I/O",
       "notes": "",
-      "versus": ""
+      "versus": "duda"
     }
   }]
 }

+ 0 - 1
frameworks/C/h2o/CMakeLists.txt

@@ -8,7 +8,6 @@ find_path(MUSTACHE_C_INCLUDE mustache.h)
 find_path(YAJL_INCLUDE yajl/yajl_gen.h)
 set(COMMON_OPTIONS -flto -pthread)
 add_compile_options(-std=gnu11 -pedantic -Wall -Wextra ${COMMON_OPTIONS})
-set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address")
 set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fstack-protector-all -D_FORTIFY_SOURCE=2")
 set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Ofast")
 set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -Ofast")

+ 5 - 4
frameworks/C/h2o/benchmark_config.json

@@ -13,14 +13,15 @@
       "approach": "Realistic",
       "classification": "Platform",
       "database": "Postgres",
-      "framework": "H2O",
+      "framework": "None",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
-      "platform": "H2O",
-      "webserver": "H2O",
+      "platform": "None",
+      "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "h2o",
+      "display_name": "H2O",
       "notes": ""
     }
   }]

+ 1 - 1
frameworks/C/h2o/setup.sh

@@ -25,7 +25,7 @@ run_curl()
 
 run_h2o_app()
 {
-	"$1/h2o_app" -a2 -f "$2/template/fortunes.mustache" -m2 "$3" "$4" \
+	"$1/h2o_app" -a1 -f "$2/template/fortunes.mustache" -m5 "$3" "$4" \
 		-d "host=$DBHOST dbname=hello_world user=benchmarkdbuser password=benchmarkdbpass" &
 }
 

+ 4 - 5
frameworks/C/h2o/src/database.c

@@ -353,8 +353,7 @@ static void start_database_connect(thread_context_t *ctx, db_conn_t *db_conn)
 			goto error;
 		}
 
-		const char * const conninfo =
-			ctx->global_data->config->db_host ? ctx->global_data->config->db_host : "";
+		const char * const conninfo = ctx->config->db_host ? ctx->config->db_host : "";
 
 		db_conn->conn = PQconnectStart(conninfo);
 
@@ -441,7 +440,7 @@ static void stop_database_write_polling(db_conn_t *db_conn)
 
 void connect_to_database(thread_context_t *ctx)
 {
-	for (size_t i = ctx->db_state.db_conn_num; i < ctx->global_data->config->max_db_conn_num; i++)
+	for (size_t i = ctx->db_state.db_conn_num; i < ctx->config->max_db_conn_num; i++)
 		start_database_connect(ctx, NULL);
 }
 
@@ -475,13 +474,13 @@ int execute_query(thread_context_t *ctx, db_query_param_t *param)
 		db_conn->param = param;
 		do_execute_query(db_conn);
 	}
-	else if (ctx->db_state.query_num < ctx->global_data->config->max_query_num) {
+	else if (ctx->db_state.query_num < ctx->config->max_query_num) {
 		param->l.next = NULL;
 		*ctx->db_state.queries.tail = &param->l;
 		ctx->db_state.queries.tail = &param->l.next;
 		ctx->db_state.query_num++;
 
-		if (ctx->db_state.db_conn_num < ctx->global_data->config->max_db_conn_num)
+		if (ctx->db_state.db_conn_num < ctx->config->max_db_conn_num)
 			start_database_connect(ctx, NULL);
 	}
 	else

+ 11 - 14
frameworks/C/h2o/src/event_loop.c

@@ -24,7 +24,6 @@
 #include <string.h>
 #include <unistd.h>
 #include <sys/epoll.h>
-#include <sys/syscall.h>
 
 #include "error.h"
 #include "event_loop.h"
@@ -57,7 +56,7 @@ static void accept_connection(h2o_socket_t *listener, const char *err)
 				sock->on_close.cb = on_close_connection;
 				sock->on_close.data = &ctx->event_loop.conn_num;
 				h2o_accept(&ctx->event_loop.h2o_accept_ctx, sock);
-			} while (++accepted < ctx->global_data->config->max_accept);
+			} while (++accepted < ctx->config->max_accept);
 		}
 	}
 }
@@ -96,11 +95,11 @@ static void process_messages(h2o_multithread_receiver_t *receiver, h2o_linklist_
 {
 	IGNORE_FUNCTION_PARAMETER(messages);
 
-	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
-	                                                      event_loop.h2o_receiver,
-	                                                      receiver);
+	global_thread_data_t * const global_thread_data = H2O_STRUCT_FROM_MEMBER(global_thread_data_t,
+	                                                                         h2o_receiver,
+	                                                                         receiver);
 
-	h2o_socket_read_stop(ctx->event_loop.h2o_socket);
+	h2o_socket_read_stop(global_thread_data->ctx->event_loop.h2o_socket);
 }
 
 static void shutdown_server(h2o_socket_t *listener, const char *err)
@@ -113,23 +112,20 @@ static void shutdown_server(h2o_socket_t *listener, const char *err)
 		ctx->global_data->shutdown = true;
 		h2o_socket_read_stop(ctx->event_loop.h2o_socket);
 
-		for (size_t i = 1; i < ctx->global_data->config->thread_num; i++)
-			h2o_multithread_send_message(&ctx[i].event_loop.h2o_receiver, NULL);
+		for (size_t i = 1; i < ctx->config->thread_num; i++)
+			h2o_multithread_send_message(&ctx->global_thread_data[i].h2o_receiver, NULL);
 	}
 }
 
 void event_loop(thread_context_t *ctx)
 {
-	ctx->tid = syscall(SYS_gettid);
-	ctx->random_seed = ctx->tid;
-
 	while (!ctx->global_data->shutdown || ctx->event_loop.conn_num)
 		h2o_evloop_run(ctx->event_loop.h2o_ctx.loop);
 }
 
-void free_event_loop(event_loop_t *event_loop)
+void free_event_loop(event_loop_t *event_loop, h2o_multithread_receiver_t *h2o_receiver)
 {
-	h2o_multithread_unregister_receiver(event_loop->h2o_ctx.queue, &event_loop->h2o_receiver);
+	h2o_multithread_unregister_receiver(event_loop->h2o_ctx.queue, h2o_receiver);
 	h2o_socket_close(event_loop->h2o_socket);
 	h2o_socket_close(event_loop->epoll_socket);
 	h2o_context_dispose(&event_loop->h2o_ctx);
@@ -137,6 +133,7 @@ void free_event_loop(event_loop_t *event_loop)
 
 void initialize_event_loop(bool is_main_thread,
                            global_data_t *global_data,
+                           h2o_multithread_receiver_t *h2o_receiver,
                            event_loop_t *loop)
 {
 	memset(loop, 0, sizeof(*loop));
@@ -165,7 +162,7 @@ void initialize_event_loop(bool is_main_thread,
 	loop->h2o_socket->data = loop;
 	h2o_socket_read_start(loop->h2o_socket, accept_connection);
 	h2o_multithread_register_receiver(loop->h2o_ctx.queue,
-	                                  &loop->h2o_receiver,
+	                                  h2o_receiver,
 	                                  process_messages);
 	// libh2o's event loop does not support write polling unless it
 	// controls sending the data as well, so do read polling on the

+ 2 - 2
frameworks/C/h2o/src/event_loop.h

@@ -35,13 +35,13 @@ typedef struct {
 	int epoll_fd;
 	h2o_accept_ctx_t h2o_accept_ctx;
 	h2o_context_t h2o_ctx;
-	h2o_multithread_receiver_t h2o_receiver;
 } event_loop_t;
 
 void event_loop(thread_context_t *ctx);
-void free_event_loop(event_loop_t *event_loop);
+void free_event_loop(event_loop_t *event_loop, h2o_multithread_receiver_t *h2o_receiver);
 void initialize_event_loop(bool is_main_thread,
                            global_data_t *global_data,
+                           h2o_multithread_receiver_t *h2o_receiver,
                            event_loop_t *loop);
 int start_write_polling(int fd,
                         void (**on_write_ready)(void *),

+ 25 - 40
frameworks/C/h2o/src/fortune.c

@@ -49,6 +49,7 @@ typedef struct {
 	iovec_list_t *iovec_list_iter;
 	h2o_req_t *req;
 	list_t *result;
+	size_t content_length;
 	size_t num_result;
 	db_query_param_t param;
 	h2o_generator_t generator;
@@ -56,9 +57,9 @@ typedef struct {
 
 static uintmax_t add_iovec(mustache_api_t *api,
                            void *userdata,
-                           char *buffer,
+                           const char *buffer,
                            uintmax_t buffer_size);
-static void cleanup_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req);
+static void cleanup_fortunes(void *data);
 static int compare_fortunes(const list_t *x, const list_t *y);
 static void complete_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req);
 static list_t *get_sorted_sublist(list_t *head);
@@ -76,7 +77,7 @@ static list_t *sort_fortunes(list_t *head);
 
 static uintmax_t add_iovec(mustache_api_t *api,
                            void *userdata,
-                           char *buffer,
+                           const char *buffer,
                            uintmax_t buffer_size)
 {
 	IGNORE_FUNCTION_PARAMETER(api);
@@ -101,20 +102,17 @@ static uintmax_t add_iovec(mustache_api_t *api,
 	}
 
 	if (ret) {
-		iovec_list->iov[iovec_list->iovcnt].base = buffer;
+		iovec_list->iov[iovec_list->iovcnt].base = (char *) buffer;
 		iovec_list->iov[iovec_list->iovcnt++].len = buffer_size;
+		fortune_ctx->content_length += buffer_size;
 	}
 
 	return ret;
 }
 
-static void cleanup_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req)
+static void cleanup_fortunes(void *data)
 {
-	IGNORE_FUNCTION_PARAMETER(req);
-
-	fortune_ctx_t * const fortune_ctx = H2O_STRUCT_FROM_MEMBER(fortune_ctx_t,
-	                                                           generator,
-	                                                           self);
+	fortune_ctx_t * const fortune_ctx = data;
 	const list_t *iter = fortune_ctx->result;
 
 	if (iter)
@@ -146,19 +144,12 @@ static void complete_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req)
 	fortune_ctx_t * const fortune_ctx = H2O_STRUCT_FROM_MEMBER(fortune_ctx_t,
 	                                                           generator,
 	                                                           self);
+	iovec_list_t * const iovec_list = H2O_STRUCT_FROM_MEMBER(iovec_list_t,
+	                                                         l,
+	                                                         fortune_ctx->iovec_list);
 
-	if (fortune_ctx->iovec_list) {
-		iovec_list_t * const iovec_list = H2O_STRUCT_FROM_MEMBER(iovec_list_t,
-		                                                         l,
-		                                                         fortune_ctx->iovec_list);
-
-		fortune_ctx->iovec_list = iovec_list->l.next;
-		h2o_send(fortune_ctx->req, iovec_list->iov, iovec_list->iovcnt, false);
-	}
-	else {
-		h2o_send(req, NULL, 0, true);
-		cleanup_fortunes(self, req);
-	}
+	fortune_ctx->iovec_list = iovec_list->l.next;
+	h2o_send(req, iovec_list->iov, iovec_list->iovcnt, !fortune_ctx->iovec_list);
 }
 
 static list_t *get_sorted_sublist(list_t *head)
@@ -168,12 +159,7 @@ static list_t *get_sorted_sublist(list_t *head)
 	if (head) {
 		head = head->next;
 
-		while (head && compare_fortunes(tail, head) < 0) {
-			tail = head;
-			head = head->next;
-		}
-
-		while (head && !compare_fortunes(tail, head)) {
+		while (head && compare_fortunes(tail, head) <= 0) {
 			tail = head;
 			head = head->next;
 		}
@@ -218,7 +204,6 @@ static void on_fortune_error(db_query_param_t *param, const char *error_string)
 	                                                           param,
 	                                                           param);
 
-	cleanup_fortunes(&fortune_ctx->generator, fortune_ctx->req);
 	send_error(BAD_GATEWAY, error_string, fortune_ctx->req);
 }
 
@@ -254,7 +239,6 @@ static result_return_t on_fortune_result(db_query_param_t *param, PGresult *resu
 					fortune->data = result;
 			}
 			else {
-				cleanup_fortunes(&fortune_ctx->generator, fortune_ctx->req);
 				send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, fortune_ctx->req);
 				ret = DONE;
 
@@ -265,8 +249,10 @@ static result_return_t on_fortune_result(db_query_param_t *param, PGresult *resu
 			}
 		}
 	}
-	else if (result)
+	else if (result) {
 		PQclear(result);
+		send_error(BAD_GATEWAY, PQresultErrorMessage(result), fortune_ctx->req);
+	}
 	else {
 		mustache_api_t api = {.sectget = on_fortune_section,
 		                      .varget = on_fortune_variable,
@@ -286,18 +272,15 @@ static result_return_t on_fortune_result(db_query_param_t *param, PGresult *resu
 
 		if (mustache_render(&api, fortune_ctx, ctx->global_data->fortunes_template)) {
 			fortune_ctx->iovec_list = iovec_list->l.next;
-			set_default_response_param(HTML, fortune_ctx->req);
+			set_default_response_param(HTML, fortune_ctx->content_length, fortune_ctx->req);
 			h2o_start_response(fortune_ctx->req, &fortune_ctx->generator);
 			h2o_send(fortune_ctx->req,
 			         iovec_list->iov,
 			         iovec_list->iovcnt,
-			         false);
+			         !fortune_ctx->iovec_list);
 		}
-		else {
-			cleanup_fortunes(&fortune_ctx->generator, fortune_ctx->req);
+		else
 			send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, fortune_ctx->req);
-			ret = DONE;
-		}
 	}
 
 	return ret;
@@ -335,7 +318,6 @@ static void on_fortune_timeout(db_query_param_t *param)
 	                                                           param,
 	                                                           param);
 
-	cleanup_fortunes(&fortune_ctx->generator, fortune_ctx->req);
 	send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, fortune_ctx->req);
 }
 
@@ -388,7 +370,9 @@ int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req)
 	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
 	                                                      event_loop.h2o_ctx,
 	                                                      req->conn->ctx);
-	fortune_ctx_t * const fortune_ctx = h2o_mem_alloc_pool(&req->pool, sizeof(*fortune_ctx));
+	fortune_ctx_t * const fortune_ctx = h2o_mem_alloc_shared(&req->pool,
+	                                                         sizeof(*fortune_ctx),
+	                                                         cleanup_fortunes);
 
 	if (fortune_ctx) {
 		fortune_t * const fortune = h2o_mem_alloc_pool(&req->pool, sizeof(*fortune));
@@ -401,7 +385,6 @@ int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req)
 			fortune->message.len = sizeof(NEW_FORTUNE_MESSAGE) - 1;
 			memset(fortune_ctx, 0, sizeof(*fortune_ctx));
 			fortune_ctx->generator.proceed = complete_fortunes;
-			fortune_ctx->generator.stop = cleanup_fortunes;
 			fortune_ctx->num_result = 1;
 			fortune_ctx->param.command = FORTUNE_TABLE_NAME;
 			fortune_ctx->param.on_error = on_fortune_error;
@@ -414,6 +397,8 @@ int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req)
 			if (execute_query(ctx, &fortune_ctx->param))
 				send_service_unavailable_error(DB_REQ_ERROR, req);
 		}
+		else
+			send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 	}
 	else
 		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);

+ 25 - 10
frameworks/C/h2o/src/main.c

@@ -24,6 +24,7 @@
 #include <netdb.h>
 #include <signal.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -44,6 +45,7 @@
 #include "tls.h"
 #include "utility.h"
 
+#define DEFAULT_CACHE_LINE_SIZE 128
 #define DEFAULT_TCP_FASTOPEN_QUEUE_LEN 4096
 #define USAGE_MESSAGE \
 	"Usage:\n%s [-a <max connections accepted simultaneously>] [-b <bind address>] " \
@@ -63,8 +65,12 @@ static void setup_process(void);
 
 static void free_global_data(global_data_t *global_data)
 {
-	if (global_data->ctx)
-		free_thread_contexts(global_data);
+	if (global_data->global_thread_data) {
+		for (size_t i = 1; i < global_data->global_thread_data->config->thread_num; i++)
+			CHECK_ERROR(pthread_join, global_data->global_thread_data[i].thread, NULL);
+
+		free(global_data->global_thread_data);
+	}
 
 	if (global_data->file_logger)
 		global_data->file_logger->dispose(global_data->file_logger);
@@ -177,9 +183,7 @@ static int initialize_global_data(const config_t *config, global_data_t *global_
 	sigset_t signals;
 
 	memset(global_data, 0, sizeof(*global_data));
-	global_data->config = config;
 	global_data->memory_alignment = get_maximum_cache_line_size();
-	assert(global_data->memory_alignment <= DEFAULT_CACHE_LINE_SIZE);
 	CHECK_ERRNO(sigemptyset, &signals);
 #ifdef NDEBUG
 	CHECK_ERRNO(sigaddset, &signals, SIGINT);
@@ -194,7 +198,7 @@ static int initialize_global_data(const config_t *config, global_data_t *global_
 		goto error;
 
 	if (config->cert && config->key)
-		initialize_openssl(global_data);
+		initialize_openssl(config, global_data);
 
 	const h2o_iovec_t host = h2o_iovec_init(H2O_STRLIT("default"));
 	h2o_hostconf_t * const hostconf = h2o_config_register_host(&global_data->h2o_config,
@@ -220,10 +224,14 @@ static int initialize_global_data(const config_t *config, global_data_t *global_
 			global_data->file_logger = h2o_access_log_register(pathconf, log_handle);
 	}
 
-	global_data->ctx = initialize_thread_contexts(global_data);
+	global_data->global_thread_data = initialize_global_thread_data(config, global_data);
 
-	if (global_data->ctx)
+	if (global_data->global_thread_data) {
+		printf("Number of processors: %zu\nMaximum cache line size: %zu\n",
+		       h2o_numproc(),
+		       global_data->memory_alignment);
 		return EXIT_SUCCESS;
+	}
 
 error:
 	free_global_data(global_data);
@@ -353,10 +361,17 @@ int main(int argc, char *argv[])
 		global_data_t global_data;
 
 		if (initialize_global_data(&config, &global_data) == EXIT_SUCCESS) {
+			thread_context_t ctx;
+
 			setup_process();
-			start_threads(global_data.ctx);
-			connect_to_database(global_data.ctx);
-			event_loop(global_data.ctx);
+			start_threads(global_data.global_thread_data);
+			initialize_thread_context(global_data.global_thread_data, true, &ctx);
+			connect_to_database(&ctx);
+			event_loop(&ctx);
+			// Even though this is global data, we need to close
+			// it before the associated event loop is cleaned up.
+			h2o_socket_close(global_data.signals);
+			free_thread_context(&ctx);
 			free_global_data(&global_data);
 			rc = EXIT_SUCCESS;
 		}

+ 38 - 53
frameworks/C/h2o/src/request_handler.c

@@ -20,6 +20,8 @@
 #include <assert.h>
 #include <h2o.h>
 #include <stdalign.h>
+#include <stdbool.h>
+#include <stdint.h>
 #include <string.h>
 #include <yajl/yajl_gen.h>
 
@@ -29,56 +31,31 @@
 #include "utility.h"
 #include "world.h"
 
-typedef struct {
-	yajl_gen gen;
-	h2o_generator_t h2o_generator;
-} json_ctx_t;
+#define HELLO_RESPONSE "Hello, World!"
 
-static void cleanup_json_response(struct st_h2o_generator_t *self, h2o_req_t *req);
-static void complete_json_response(struct st_h2o_generator_t *self, h2o_req_t *req);
 static int json_serializer(struct st_h2o_handler_t *self, h2o_req_t *req);
 static int plaintext(struct st_h2o_handler_t *self, h2o_req_t *req);
 static const char *status_code_to_string(http_status_code_t status_code);
 
-static void cleanup_json_response(struct st_h2o_generator_t *self, h2o_req_t *req)
-{
-	IGNORE_FUNCTION_PARAMETER(req);
-
-	json_ctx_t * const json_ctx = H2O_STRUCT_FROM_MEMBER(json_ctx_t, h2o_generator, self);
-
-	yajl_gen_free(json_ctx->gen);
-}
-
-static void complete_json_response(struct st_h2o_generator_t *self, h2o_req_t *req)
-{
-	h2o_send(req, NULL, 0, true);
-	cleanup_json_response(self, req);
-}
-
 static int json_serializer(struct st_h2o_handler_t *self, h2o_req_t *req)
 {
 	IGNORE_FUNCTION_PARAMETER(self);
 
-	json_ctx_t * const json_ctx = h2o_mem_alloc_pool(&req->pool, sizeof(*json_ctx));
-
-	if (json_ctx) {
-		memset(json_ctx, 0, sizeof(*json_ctx));
-		json_ctx->gen = get_json_generator(&req->pool);
-		json_ctx->h2o_generator.proceed = complete_json_response;
-		json_ctx->h2o_generator.stop = cleanup_json_response;
+	const yajl_gen gen = get_json_generator(&req->pool);
 
-		if (json_ctx->gen) {
-			CHECK_YAJL_STATUS(yajl_gen_map_open, json_ctx->gen);
-			CHECK_YAJL_STATUS(yajl_gen_string, json_ctx->gen, YAJL_STRLIT("message"));
-			CHECK_YAJL_STATUS(yajl_gen_string, json_ctx->gen, YAJL_STRLIT("Hello, World!"));
-			CHECK_YAJL_STATUS(yajl_gen_map_close, json_ctx->gen);
+	if (gen) {
+		CHECK_YAJL_STATUS(yajl_gen_map_open, gen);
+		CHECK_YAJL_STATUS(yajl_gen_string, gen, YAJL_STRLIT("message"));
+		CHECK_YAJL_STATUS(yajl_gen_string, gen, YAJL_STRLIT(HELLO_RESPONSE));
+		CHECK_YAJL_STATUS(yajl_gen_map_close, gen);
 
-			if (!send_json_response(json_ctx->gen, &json_ctx->h2o_generator, req))
-				return 0;
+		// The response is small enough, so that it is simpler to copy it
+		// instead of doing a delayed deallocation of the JSON generator.
+		if (!send_json_response(gen, true, req))
+			return 0;
 
 error_yajl:
-			yajl_gen_free(json_ctx->gen);
-		}
+		yajl_gen_free(gen);
 	}
 
 	send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
@@ -88,8 +65,8 @@ error_yajl:
 static int plaintext(struct st_h2o_handler_t *self, h2o_req_t *req)
 {
 	IGNORE_FUNCTION_PARAMETER(self);
-	set_default_response_param(PLAIN, req);
-	h2o_send_inline(req, H2O_STRLIT("Hello, World!"));
+	set_default_response_param(PLAIN, sizeof(HELLO_RESPONSE) - 1, req);
+	h2o_send_inline(req, H2O_STRLIT(HELLO_RESPONSE));
 	return 0;
 }
 
@@ -195,22 +172,29 @@ void send_error(http_status_code_t status_code, const char *body, h2o_req_t *req
 	h2o_send_error_generic(req, status_code, status_code_to_string(status_code), body, 0);
 }
 
-int send_json_response(yajl_gen gen, h2o_generator_t *h2o_generator, h2o_req_t *req)
+int send_json_response(yajl_gen gen, bool free_gen, h2o_req_t *req)
 {
-	h2o_iovec_t h2o_iovec = {.len = 0};
+	const unsigned char *buf;
+	size_t len;
 	int ret = EXIT_FAILURE;
 
-	static_assert(sizeof(h2o_iovec.base) == sizeof(const unsigned char *) &&
-	              alignof(h2o_iovec.base) == alignof(const unsigned char *),
-	              "Types must be compatible.");
-
-	if (yajl_gen_get_buf(gen,
-	                     (const unsigned char **) &h2o_iovec.base,
-	                     &h2o_iovec.len) == yajl_gen_status_ok) {
-		set_default_response_param(JSON, req);
-		h2o_start_response(req, h2o_generator);
-		h2o_send(req, &h2o_iovec, 1, false);
-		ret = EXIT_SUCCESS;
+	if (yajl_gen_get_buf(gen, &buf, &len) == yajl_gen_status_ok) {
+		if (free_gen) {
+			char * const body = h2o_mem_alloc_pool(&req->pool, len);
+
+			if (body) {
+				memcpy(body, buf, len);
+				yajl_gen_free(gen);
+				set_default_response_param(JSON, len, req);
+				h2o_send_inline(req, body, len);
+				ret = EXIT_SUCCESS;
+			}
+		}
+		else {
+			set_default_response_param(JSON, len, req);
+			h2o_send_inline(req, (char *) buf, len);
+			ret = EXIT_SUCCESS;
+		}
 	}
 
 	return ret;
@@ -228,8 +212,9 @@ void send_service_unavailable_error(const char *body, h2o_req_t *req)
 	                   H2O_SEND_ERROR_KEEP_HEADERS);
 }
 
-void set_default_response_param(content_type_t content_type, h2o_req_t *req)
+void set_default_response_param(content_type_t content_type, size_t content_length, h2o_req_t *req)
 {
+	req->res.content_length = content_length;
 	req->res.status = OK;
 	req->res.reason = status_code_to_string(req->res.status);
 

+ 5 - 2
frameworks/C/h2o/src/request_handler.h

@@ -22,6 +22,7 @@
 #define REQUEST_H_
 
 #include <h2o.h>
+#include <stdbool.h>
 #include <yajl/yajl_gen.h>
 
 typedef enum {
@@ -44,8 +45,10 @@ const char *get_query_param(const char *query,
                             size_t param_len);
 void register_request_handlers(h2o_hostconf_t *hostconf, h2o_access_log_filehandle_t *log_handle);
 void send_error(http_status_code_t status_code, const char *body, h2o_req_t *req);
-int send_json_response(yajl_gen gen, h2o_generator_t *h2o_generator, h2o_req_t *req);
+int send_json_response(yajl_gen gen, bool free_gen, h2o_req_t *req);
 void send_service_unavailable_error(const char *body, h2o_req_t *req);
-void set_default_response_param(content_type_t content_type, h2o_req_t *req);
+void set_default_response_param(content_type_t content_type,
+                                size_t content_length,
+                                h2o_req_t *req);
 
 #endif // REQUEST_H_

+ 8 - 2
frameworks/C/h2o/src/template.c

@@ -46,7 +46,10 @@ static uintmax_t read_template(mustache_api_t *api,
                                void *userdata,
                                char *buffer,
                                uintmax_t buffer_size);
-static void template_error(mustache_api_t *api, void *userdata, uintmax_t lineno, char *error);
+static void template_error(mustache_api_t *api,
+                           void *userdata,
+                           uintmax_t lineno,
+                           const char *error);
 
 static uintmax_t prerender_section(mustache_api_t *api,
                                    void *userdata,
@@ -101,7 +104,10 @@ static uintmax_t read_template(mustache_api_t *api,
 	return fread(buffer, sizeof(*buffer), buffer_size, template_input->input);
 }
 
-static void template_error(mustache_api_t *api, void *userdata, uintmax_t lineno, char *error)
+static void template_error(mustache_api_t *api,
+                           void *userdata,
+                           uintmax_t lineno,
+                           const char *error)
 {
 	IGNORE_FUNCTION_PARAMETER(api);
 

+ 49 - 31
frameworks/C/h2o/src/thread.c

@@ -22,9 +22,12 @@
 #include <errno.h>
 #include <h2o.h>
 #include <pthread.h>
+#include <stdbool.h>
 #include <stdlib.h>
+#include <string.h>
 #include <h2o/serverutil.h>
 #include <sys/epoll.h>
+#include <sys/syscall.h>
 
 #include "database.h"
 #include "error.h"
@@ -35,71 +38,86 @@ static void *run_thread(void *arg);
 
 static void *run_thread(void *arg)
 {
-	connect_to_database(arg);
-	event_loop(arg);
+	thread_context_t ctx;
+
+	initialize_thread_context(arg, false, &ctx);
+	connect_to_database(&ctx);
+	event_loop(&ctx);
+	free_thread_context(&ctx);
 	pthread_exit(NULL);
 }
 
-void free_thread_contexts(global_data_t *global_data)
+void free_thread_context(thread_context_t *ctx)
 {
-	thread_context_t * const ctx = global_data->ctx;
-
-	for (size_t i = 0; i < ctx->global_data->config->thread_num; i++) {
-		if (i)
-			CHECK_ERROR(pthread_join, ctx[i].thread, NULL);
-		else
-			// Even though this is global data, we need to close
-			// it before the associated event loop is cleaned up.
-			h2o_socket_close(global_data->signals);
-
-		free_database_state(ctx[i].event_loop.h2o_ctx.loop, &ctx[i].db_state);
-		free_event_loop(&ctx[i].event_loop);
-	}
-
-	free(ctx);
+	free_database_state(ctx->event_loop.h2o_ctx.loop, &ctx->db_state);
+	free_event_loop(&ctx->event_loop, &ctx->global_thread_data->h2o_receiver);
 }
 
-thread_context_t *initialize_thread_contexts(global_data_t *global_data)
+global_thread_data_t *initialize_global_thread_data(const config_t *config,
+                                                    global_data_t *global_data)
 {
-	const size_t sz = global_data->config->thread_num * sizeof(thread_context_t);
-	thread_context_t * const ret = aligned_alloc(global_data->memory_alignment, sz);
+	const size_t sz = config->thread_num * sizeof(thread_context_t);
+	// The global thread data is modified only at program initialization and termination,
+	// and is not accessed by performance-sensitive code, so false sharing is not a concern.
+	global_thread_data_t * const ret = aligned_alloc(global_data->memory_alignment, sz);
 
 	if (ret) {
 		memset(ret, 0, sz);
 
-		for (size_t i = 0; i < global_data->config->thread_num; i++) {
+		for (size_t i = 0; i < config->thread_num; i++) {
+			ret[i].config = config;
 			ret[i].global_data = global_data;
-			initialize_event_loop(!i, global_data, &ret[i].event_loop);
-			initialize_database_state(ret[i].event_loop.h2o_ctx.loop, &ret[i].db_state);
 		}
 	}
 
 	return ret;
 }
 
-void start_threads(thread_context_t *ctx)
+void initialize_thread_context(global_thread_data_t *global_thread_data,
+                               bool is_main_thread,
+                               thread_context_t *ctx)
+{
+	memset(ctx, 0, sizeof(*ctx));
+	ctx->config = global_thread_data->config;
+	ctx->global_data = global_thread_data->global_data;
+	ctx->global_thread_data = global_thread_data;
+	ctx->tid = syscall(SYS_gettid);
+	ctx->random_seed = ctx->tid;
+	initialize_event_loop(is_main_thread,
+	                      global_thread_data->global_data,
+	                      &global_thread_data->h2o_receiver,
+	                      &ctx->event_loop);
+	initialize_database_state(ctx->event_loop.h2o_ctx.loop, &ctx->db_state);
+	global_thread_data->ctx = ctx;
+}
+
+void start_threads(global_thread_data_t *global_thread_data)
 {
 	const size_t num_cpus = h2o_numproc();
 
 	// The first thread context is used by the main thread.
-	ctx->thread = pthread_self();
+	global_thread_data->thread = pthread_self();
 
-	for (size_t i = 1; i < ctx->global_data->config->thread_num; i++)
-		CHECK_ERROR(pthread_create, &ctx[i].thread, NULL, run_thread, ctx + i);
+	for (size_t i = 1; i < global_thread_data->config->thread_num; i++)
+		CHECK_ERROR(pthread_create,
+		            &global_thread_data[i].thread,
+		            NULL,
+		            run_thread,
+		            global_thread_data + i);
 
 	// If the number of threads is not equal to the number of processors, then let the scheduler
 	// decide how to balance the load.
-	if (ctx->global_data->config->thread_num == num_cpus) {
+	if (global_thread_data->config->thread_num == num_cpus) {
 		const size_t cpusetsize = CPU_ALLOC_SIZE(num_cpus);
 		cpu_set_t * const cpuset = CPU_ALLOC(num_cpus);
 
 		if (!cpuset)
 			abort();
 
-		for (size_t i = 0; i < ctx->global_data->config->thread_num; i++) {
+		for (size_t i = 0; i < global_thread_data->config->thread_num; i++) {
 			CPU_ZERO_S(cpusetsize, cpuset);
 			CPU_SET_S(i, cpusetsize, cpuset);
-			CHECK_ERROR(pthread_setaffinity_np, ctx[i].thread, cpusetsize, cpuset);
+			CHECK_ERROR(pthread_setaffinity_np, global_thread_data[i].thread, cpusetsize, cpuset);
 		}
 
 		CPU_FREE(cpuset);

+ 20 - 12
frameworks/C/h2o/src/thread.h

@@ -24,33 +24,41 @@
 #include <assert.h>
 #include <h2o.h>
 #include <pthread.h>
+#include <stdbool.h>
 #include <sys/types.h>
 
 #include "database.h"
 #include "event_loop.h"
 #include "utility.h"
 
-#define DEFAULT_CACHE_LINE_SIZE 64
-
 typedef struct thread_context_t thread_context_t;
 
+typedef struct global_thread_data_t {
+	const config_t *config;
+	thread_context_t *ctx;
+	global_data_t *global_data;
+	h2o_multithread_receiver_t h2o_receiver;
+	pthread_t thread;
+} global_thread_data_t;
+
 struct thread_context_t {
+	const config_t *config;
 	global_data_t *global_data;
+	// global_thread_data contains config and global_data as well,
+	// but keep copies here to avoid some pointer chasing.
+	global_thread_data_t *global_thread_data;
 	unsigned random_seed;
 	pid_t tid;
 	db_state_t db_state;
 	event_loop_t event_loop;
-	pthread_t thread;
-	// Align on the cache line size to prevent false sharing.
-	char padding[49];
 };
 
-static_assert(!(sizeof(thread_context_t) % DEFAULT_CACHE_LINE_SIZE),
-              "The size of the thread_context_t structure must be a "
-              "multiple of the cache line size.");
-
-void free_thread_contexts(global_data_t *global_data);
-thread_context_t *initialize_thread_contexts(global_data_t *global_data);
-void start_threads(thread_context_t *ctx);
+void free_thread_context(thread_context_t *ctx);
+global_thread_data_t *initialize_global_thread_data(const config_t *config,
+                                                    global_data_t *global_data);
+void initialize_thread_context(global_thread_data_t *global_thread_data,
+                               bool is_main_thread,
+                               thread_context_t *ctx);
+void start_threads(global_thread_data_t *global_thread_data);
 
 #endif // THREAD_H_

+ 3 - 3
frameworks/C/h2o/src/tls.c

@@ -136,7 +136,7 @@ void cleanup_openssl(global_data_t *global_data)
 	CHECK_ERROR(pthread_mutexattr_destroy, &openssl_global_data.lock_attr);
 }
 
-void initialize_openssl(global_data_t *global_data)
+void initialize_openssl(const config_t *config, global_data_t *global_data)
 {
 	SSL_library_init();
 	SSL_load_error_strings();
@@ -160,10 +160,10 @@ void initialize_openssl(global_data_t *global_data)
 	global_data->ssl_ctx = SSL_CTX_new(TLSv1_2_server_method());
 	CHECK_OPENSSL_ERROR(SSL_CTX_use_certificate_file,
 	                    global_data->ssl_ctx,
-	                    global_data->config->cert,
+	                    config->cert,
 	                    SSL_FILETYPE_PEM);
 	CHECK_OPENSSL_ERROR(SSL_CTX_use_PrivateKey_file,
 	                    global_data->ssl_ctx,
-	                    global_data->config->key,
+	                    config->key,
 	                    SSL_FILETYPE_PEM);
 }

+ 1 - 1
frameworks/C/h2o/src/tls.h

@@ -24,6 +24,6 @@
 #include "utility.h"
 
 void cleanup_openssl(global_data_t *global_data);
-void initialize_openssl(global_data_t *global_data);
+void initialize_openssl(const config_t *config, global_data_t *global_data);
 
 #endif // TLS_H_

+ 6 - 3
frameworks/C/h2o/src/utility.h

@@ -36,7 +36,7 @@
 #define TOSTRING(x) # x
 #define YAJL_STRLIT(s) (const unsigned char *) (s), sizeof(s) - 1
 
-typedef struct thread_context_t thread_context_t;
+typedef struct global_thread_data_t global_thread_data_t;
 
 typedef struct {
 	const char *bind_address;
@@ -54,12 +54,11 @@ typedef struct {
 } config_t;
 
 typedef struct {
-	const config_t *config;
-	thread_context_t *ctx;
 	h2o_logger_t *file_logger;
 	mustache_template_t *fortunes_template;
 	h2o_socket_t *signals;
 	SSL_CTX *ssl_ctx;
+	global_thread_data_t *global_thread_data;
 	size_t memory_alignment;
 	int listener_sd;
 	int signal_fd;
@@ -67,7 +66,11 @@ typedef struct {
 	h2o_globalconf_t h2o_config;
 } global_data_t;
 
+// Call yajl_gen_free() on the result, even though the JSON generator
+// uses a memory pool; in this way the code remains correct if the
+// underlying memory allocator is changed (e.g. for debugging purposes).
 yajl_gen get_json_generator(h2o_mem_pool_t *pool);
+
 uint32_t get_random_number(uint32_t max_rand, unsigned int *seed);
 
 #endif // UTILITY_H_

+ 68 - 85
frameworks/C/h2o/src/world.c

@@ -53,8 +53,6 @@ typedef struct {
 
 typedef struct {
 	db_query_param_t param;
-	yajl_gen gen;
-	h2o_generator_t h2o_generator;
 	const char *id_pointer;
 	h2o_req_t *req;
 	uint32_t id;
@@ -64,18 +62,18 @@ typedef struct {
 
 typedef struct {
 	single_query_ctx_t single;
+	yajl_gen gen;
 	size_t num_query;
 	size_t num_result;
 	update_state_t update_state;
 	query_result_t res[];
 } multiple_query_ctx_t;
 
-static void complete_request(struct st_h2o_generator_t *self, h2o_req_t *req);
-static void cleanup_request(struct st_h2o_generator_t *self, h2o_req_t *req);
+static void cleanup_request(void *data);
 static int do_multiple_queries(update_state_t update_state, h2o_req_t *req);
-static int initialize_single_query_context(h2o_req_t *req,
-                                           on_result_t on_result,
-                                           single_query_ctx_t *query_ctx);
+static void initialize_single_query_context(h2o_req_t *req,
+                                            on_result_t on_result,
+                                            single_query_ctx_t *query_ctx);
 static void on_database_error(db_query_param_t *param, const char *error_string);
 static void on_database_timeout(db_query_param_t *param);
 static result_return_t on_multiple_query_result(db_query_param_t *param, PGresult *result);
@@ -85,25 +83,15 @@ static int on_update_write_ready(db_query_param_t *param, PGconn *db_conn);
 static int serialize_item(uint32_t id, uint32_t random_number, yajl_gen gen);
 static void serialize_items(const query_result_t *res,
                             size_t num_result,
-                            h2o_generator_t *h2o_generator,
                             yajl_gen gen,
                             h2o_req_t *req);
 
-static void cleanup_request(struct st_h2o_generator_t *self, h2o_req_t *req)
+static void cleanup_request(void *data)
 {
-	IGNORE_FUNCTION_PARAMETER(req);
-
-	single_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(single_query_ctx_t,
-	                                                              h2o_generator,
-	                                                              self);
+	const multiple_query_ctx_t * const query_ctx = data;
 
-	yajl_gen_free(query_ctx->gen);
-}
-
-static void complete_request(struct st_h2o_generator_t *self, h2o_req_t *req)
-{
-	h2o_send(req, NULL, 0, true);
-	cleanup_request(self, req);
+	if (query_ctx->gen)
+		yajl_gen_free(query_ctx->gen);
 }
 
 static int do_multiple_queries(update_state_t update_state, h2o_req_t *req)
@@ -129,16 +117,13 @@ static int do_multiple_queries(update_state_t update_state, h2o_req_t *req)
 		num_query = MAX_QUERIES;
 
 	const size_t sz = offsetof(multiple_query_ctx_t, res) + num_query * sizeof(query_result_t);
-	multiple_query_ctx_t * const query_ctx = h2o_mem_alloc_pool(&req->pool, sz);
+	multiple_query_ctx_t * const query_ctx = h2o_mem_alloc_shared(&req->pool, sz, cleanup_request);
 
-	if (!query_ctx || initialize_single_query_context(req,
-	                                                  on_multiple_query_result,
-	                                                  &query_ctx->single))
-		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
-	else {
+	if (query_ctx) {
 		// MAX_ID is a relatively small number, so allocate on the stack.
 		DEFINE_BITSET(bitset, MAX_ID);
 
+		initialize_single_query_context(req, on_multiple_query_result, &query_ctx->single);
 		memset(&query_ctx->single + 1,
 		       0,
 		       offsetof(multiple_query_ctx_t, res) - sizeof(query_ctx->single));
@@ -159,45 +144,37 @@ static int do_multiple_queries(update_state_t update_state, h2o_req_t *req)
 
 		query_ctx->single.id = htonl(query_ctx->res->id);
 
-		if (execute_query(ctx, &query_ctx->single.param)) {
-			yajl_gen_free(query_ctx->single.gen);
+		if (execute_query(ctx, &query_ctx->single.param))
 			send_service_unavailable_error(DB_REQ_ERROR, req);
-		}
+		else
+			// Create a JSON generator while the query is processed.
+			query_ctx->gen = get_json_generator(&req->pool);
 	}
+	else
+		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 
 	return 0;
 }
 
-static int initialize_single_query_context(h2o_req_t *req,
-                                           on_result_t on_result,
-                                           single_query_ctx_t *query_ctx)
+static void initialize_single_query_context(h2o_req_t *req,
+                                            on_result_t on_result,
+                                            single_query_ctx_t *query_ctx)
 {
-	int ret = EXIT_FAILURE;
-
 	memset(query_ctx, 0, sizeof(*query_ctx));
-	query_ctx->gen = get_json_generator(&req->pool);
-
-	if (query_ctx->gen) {
-		query_ctx->h2o_generator.proceed = complete_request;
-		query_ctx->h2o_generator.stop = cleanup_request;
-		query_ctx->id_format = 1;
-		query_ctx->id_len = sizeof(query_ctx->id);
-		query_ctx->id_pointer = (const char *) &query_ctx->id;
-		query_ctx->param.command = WORLD_TABLE_NAME;
-		query_ctx->param.nParams = 1;
-		query_ctx->param.on_error = on_database_error;
-		query_ctx->param.on_result = on_result;
-		query_ctx->param.on_timeout = on_database_timeout;
-		query_ctx->param.paramFormats = &query_ctx->id_format;
-		query_ctx->param.paramLengths = &query_ctx->id_len;
-		query_ctx->param.paramValues = &query_ctx->id_pointer;
-		query_ctx->param.flags = IS_PREPARED;
-		query_ctx->param.resultFormat = 1;
-		query_ctx->req = req;
-		ret = EXIT_SUCCESS;
-	}
-
-	return ret;
+	query_ctx->id_format = 1;
+	query_ctx->id_len = sizeof(query_ctx->id);
+	query_ctx->id_pointer = (const char *) &query_ctx->id;
+	query_ctx->param.command = WORLD_TABLE_NAME;
+	query_ctx->param.nParams = 1;
+	query_ctx->param.on_error = on_database_error;
+	query_ctx->param.on_result = on_result;
+	query_ctx->param.on_timeout = on_database_timeout;
+	query_ctx->param.paramFormats = &query_ctx->id_format;
+	query_ctx->param.paramLengths = &query_ctx->id_len;
+	query_ctx->param.paramValues = &query_ctx->id_pointer;
+	query_ctx->param.flags = IS_PREPARED;
+	query_ctx->param.resultFormat = 1;
+	query_ctx->req = req;
 }
 
 static void on_database_error(db_query_param_t *param, const char *error_string)
@@ -206,7 +183,6 @@ static void on_database_error(db_query_param_t *param, const char *error_string)
 	                                                              param,
 	                                                              param);
 
-	yajl_gen_free(query_ctx->gen);
 	send_error(BAD_GATEWAY, error_string, query_ctx->req);
 }
 
@@ -216,7 +192,6 @@ static void on_database_timeout(db_query_param_t *param)
 	                                                              param,
 	                                                              param);
 
-	yajl_gen_free(query_ctx->gen);
 	send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, query_ctx->req);
 }
 
@@ -251,8 +226,7 @@ static result_return_t on_multiple_query_result(db_query_param_t *param, PGresul
 		else if (query_ctx->update_state == NO_UPDATE) {
 			serialize_items(query_ctx->res,
 			                query_ctx->num_result,
-			                &query_ctx->single.h2o_generator,
-			                query_ctx->single.gen,
+			                query_ctx->gen,
 			                query_ctx->single.req);
 			return DONE;
 		}
@@ -277,7 +251,6 @@ static result_return_t on_multiple_query_result(db_query_param_t *param, PGresul
 		PQclear(result);
 	}
 
-	yajl_gen_free(query_ctx->single.gen);
 	return DONE;
 }
 
@@ -299,9 +272,17 @@ static result_return_t on_single_query_result(db_query_param_t *param, PGresult
 		random_number = ntohl(random_number);
 		PQclear(result);
 
-		if (!serialize_item(ntohl(query_ctx->id), random_number, query_ctx->gen) &&
-		    !send_json_response(query_ctx->gen, &query_ctx->h2o_generator, query_ctx->req))
-			return DONE;
+		const yajl_gen gen = get_json_generator(&query_ctx->req->pool);
+
+		if (gen) {
+			// The response is small enough, so that it is simpler to copy it
+			// instead of doing a delayed deallocation of the JSON generator.
+			if (!serialize_item(ntohl(query_ctx->id), random_number, gen) &&
+			    !send_json_response(gen, true, query_ctx->req))
+				return DONE;
+
+			yajl_gen_free(gen);
+		}
 
 		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, query_ctx->req);
 	}
@@ -310,7 +291,6 @@ static result_return_t on_single_query_result(db_query_param_t *param, PGresult
 		PQclear(result);
 	}
 
-	yajl_gen_free(query_ctx->gen);
 	return DONE;
 }
 
@@ -342,8 +322,7 @@ static result_return_t on_update_result(db_query_param_t *param, PGresult *resul
 
 			serialize_items(query_ctx->res,
 			                query_ctx->num_result,
-			                &query_ctx->single.h2o_generator,
-			                query_ctx->single.gen,
+			                query_ctx->gen,
 			                query_ctx->single.req);
 			ret = DONE;
 			break;
@@ -440,21 +419,28 @@ error_yajl:
 
 static void serialize_items(const query_result_t *res,
                             size_t num_result,
-                            h2o_generator_t *h2o_generator,
                             yajl_gen gen,
                             h2o_req_t *req)
 {
-	CHECK_YAJL_STATUS(yajl_gen_array_open, gen);
+	// In principle the JSON generator can be created here, but we do it earlier,
+	// so that it happens in parallel with the database query; we assume that the
+	// allocation will rarely fail, so that the delayed error handling is not
+	// problematic.
+	if (gen) {
+		CHECK_YAJL_STATUS(yajl_gen_array_open, gen);
 
-	for (size_t i = 0; i < num_result; i++)
-		if (serialize_item(res[i].id, res[i].random_number, gen))
-			goto error_yajl;
+		for (size_t i = 0; i < num_result; i++)
+			if (serialize_item(res[i].id, res[i].random_number, gen))
+				goto error_yajl;
 
-	CHECK_YAJL_STATUS(yajl_gen_array_close, gen);
+		CHECK_YAJL_STATUS(yajl_gen_array_close, gen);
+
+		if (!send_json_response(gen, false, req))
+			return;
+	}
 
-	if (send_json_response(gen, h2o_generator, req))
 error_yajl:
-		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
+	send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 }
 
 int multiple_queries(struct st_h2o_handler_t *self, h2o_req_t *req)
@@ -473,18 +459,15 @@ int single_query(struct st_h2o_handler_t *self, h2o_req_t *req)
 	                                                      req->conn->ctx);
 	single_query_ctx_t * const query_ctx = h2o_mem_alloc_pool(&req->pool, sizeof(*query_ctx));
 
-	if (!query_ctx || initialize_single_query_context(req,
-	                                                  on_single_query_result,
-	                                                  query_ctx))
-		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
-	else {
+	if (query_ctx) {
+		initialize_single_query_context(req, on_single_query_result, query_ctx);
 		query_ctx->id = htonl(get_random_number(MAX_ID, &ctx->random_seed) + 1);
 
-		if (execute_query(ctx, &query_ctx->param)) {
-			yajl_gen_free(query_ctx->gen);
+		if (execute_query(ctx, &query_ctx->param))
 			send_service_unavailable_error(DB_REQ_ERROR, req);
-		}
 	}
+	else
+		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 
 	return 0;
 }

+ 4 - 3
frameworks/C/haywire/benchmark_config.json

@@ -6,17 +6,18 @@
       "plaintext_url": "/plaintext",
       "port": 8000,
       "approach": "Realistic",
-      "classification": "Micro",
+      "classification": "Platform",
       "database": "None",
-      "framework": "haywire",
+      "framework": "None",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
       "platform": "haywire",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "notes": "",
-      "versus": ""
+      "versus": "haywire"
     }
   }]
 }

+ 3 - 2
frameworks/C/libreactor/benchmark_config.json

@@ -9,15 +9,16 @@
       "approach": "Realistic",
       "classification": "Platform",
       "database": "None",
-      "framework": "libreactor",
+      "framework": "None",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
       "platform": "libreactor",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "notes": "",
-      "versus": ""
+      "versus": "libreactor"
     }
   }]
 }

+ 12 - 9
frameworks/C/lwan/benchmark_config.json

@@ -9,16 +9,17 @@
       "approach": "Realistic",
       "classification": "Platform",
       "database": "None",
-      "framework": "lwan",
+      "framework": "None",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
       "platform": "Lwan",
-      "webserver": "Lwan",
+      "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "Lwan",
       "notes": "",
-      "versus": ""
+      "versus": "lwan"
     },
     "sqlite": {
       "setup_file": "setup",
@@ -29,16 +30,17 @@
       "approach": "Realistic",
       "classification": "Platform",
       "database": "SQLite",
-      "framework": "lwan",
+      "framework": "None",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
       "platform": "Lwan",
-      "webserver": "Lwan",
+      "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "Lwan",
       "notes": "",
-      "versus": ""
+      "versus": "lwan"
     },
     "mysql": {
       "setup_file": "setup-mysql",
@@ -49,16 +51,17 @@
       "approach": "Realistic",
       "classification": "Platform",
       "database": "MySQL",
-      "framework": "lwan",
+      "framework": "None",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
       "platform": "Lwan",
-      "webserver": "Lwan",
+      "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "Lwan",
       "notes": "",
-      "versus": ""
+      "versus": "lwan"
     }
   }]
 }

+ 8 - 6
frameworks/C/onion/benchmark_config.json

@@ -8,16 +8,17 @@
       "approach": "Realistic",
       "classification": "Platform",
       "database": "None",
-      "framework": "onion",
+      "framework": "None",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
-      "platform": "Onion",
+      "platform": "None",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "onion",
       "notes": "",
-      "versus": ""
+      "versus": "onion"
     },
     "raw": {
       "setup_file": "setup",
@@ -28,16 +29,17 @@
       "approach": "Realistic",
       "classification": "Platform",
       "database": "MySQL",
-      "framework": "onion",
+      "framework": "None",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
-      "platform": "Onion",
+      "platform": "None",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "onion",
       "notes": "",
-      "versus": ""
+      "versus": "onion"
     }
   }]
 }

+ 25 - 21
frameworks/CSharp/HttpListener/benchmark_config.json

@@ -6,19 +6,20 @@
       "json_url": "/json",
       "plaintext_url": "/plaintext",
       "port": 8080,
-      "approach": "Realistic",
+      "approach": "Stripped",
       "classification": "Platform",
       "database": "None",
-      "framework": "http-listener",
+      "framework": "None",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
-      "platform": "NET",
-      "webserver": "HTTP.sys",
+      "platform": "None",
+      "webserver": "None",
       "os": "Windows",
       "database_os": "Linux",
       "display_name": "http-listener",
       "notes": "",
-      "versus": ""
+      "versus": "HttpListener"
     },
     "mysql-raw": {
       "setup_file": "setup",
@@ -27,14 +28,15 @@
       "fortune_url": "/fortunes?provider=mysql",
       "update_url": "/updates?provider=mysql&queries=",
       "port": 8080,
-      "approach": "Realistic",
+      "approach": "Stripped",
       "classification": "Platform",
       "database": "MySQL",
-      "framework": "http-listener",
+      "framework": "None",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
-      "platform": "NET",
-      "webserver": "HTTP.sys",
+      "platform": "None",
+      "webserver": "None",
       "os": "Windows",
       "database_os": "Linux",
       "display_name": "http-listener",
@@ -48,14 +50,14 @@
       "fortune_url": "/fortunes?provider=postgresql",
       "update_url": "/updates?provider=postgresql&queries=",
       "port": 8080,
-      "approach": "Realistic",
+      "approach": "Stripped",
       "classification": "Platform",
       "database": "Postgres",
-      "framework": "http-listener",
+      "framework": "None",
       "language": "C#",
       "orm": "Raw",
-      "platform": "NET",
-      "webserver": "HTTP.sys",
+      "platform": "None",
+      "webserver": "None",
       "os": "Windows",
       "database_os": "Linux",
       "display_name": "http-listener",
@@ -69,14 +71,15 @@
       "fortune_url": "/mongodbfortunes",
       "update_url": "/mongodbupdates?queries=",
       "port": 8080,
-      "approach": "Realistic",
+      "approach": "Stripped",
       "classification": "Platform",
       "database": "MongoDB",
-      "framework": "http-listener",
+      "framework": "None",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
-      "platform": "NET",
-      "webserver": "HTTP.sys",
+      "platform": "None",
+      "webserver": "None",
       "os": "Windows",
       "database_os": "Linux",
       "display_name": "http-listener",
@@ -90,14 +93,15 @@
       "fortune_url": "/fortunes?provider=sqlserver",
       "update_url": "/updates?provider=sqlserver&queries=",
       "port": 8080,
-      "approach": "Realistic",
+      "approach": "Stripped",
       "classification": "Platform",
       "database": "SQLServer",
-      "framework": "http-listener",
+      "framework": "None",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
-      "platform": "NET",
-      "webserver": "HTTP.sys",
+      "platform": "None",
+      "webserver": "None",
       "os": "Windows",
       "database_os": "Windows",
       "display_name": "http-listener",

+ 25 - 20
frameworks/CSharp/aspnet-stripped/benchmark_config.json

@@ -9,16 +9,17 @@
       "approach": "Stripped",
       "classification": "Fullstack",
       "database": "None",
-      "framework": "aspnet",
+      "framework": "ASP.NET",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "os": "Windows",
       "database_os": "Linux",
-      "display_name": "aspnet-stripped",
+      "display_name": "asp.net-stripped",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet-stripped"
     },
     "mysql-raw": {
       "setup_file": "setup_iis",
@@ -30,16 +31,17 @@
       "approach": "Stripped",
       "classification": "Fullstack",
       "database": "MySQL",
-      "framework": "aspnet",
+      "framework": "ASP.NET",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "os": "Windows",
       "database_os": "Linux",
-      "display_name": "aspnet-stripped-raw",
+      "display_name": "asp.net-stripped-raw",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet-stripped"
     },
     "postgresql-raw": {
       "setup_file": "setup_iis",
@@ -51,16 +53,17 @@
       "approach": "Stripped",
       "classification": "Fullstack",
       "database": "Postgres",
-      "framework": "aspnet",
+      "framework": "ASP.NET",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "os": "Windows",
       "database_os": "Linux",
-      "display_name": "aspnet-stripped-raw",
+      "display_name": "asp.net-stripped-raw",
       "notes": "",
-      "versus": ""
+      "versus": "asnet-stripped"
     },
     "mongodb-raw": {
       "setup_file": "setup_iis",
@@ -72,16 +75,17 @@
       "approach": "Stripped",
       "classification": "Fullstack",
       "database": "MongoDB",
-      "framework": "aspnet",
+      "framework": "ASP.NET",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "os": "Windows",
       "database_os": "Linux",
-      "display_name": "aspnet-stripped-raw",
+      "display_name": "asp.net-stripped-raw",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet-stripped"
     },
     "sqlserver-raw": {
       "setup_file": "setup_iis",
@@ -93,16 +97,17 @@
       "approach": "Stripped",
       "classification": "Fullstack",
       "database": "SQLServer",
-      "framework": "aspnet",
+      "framework": "ASP.NET",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "os": "Windows",
       "database_os": "Windows",
-      "display_name": "aspnet-stripped-raw",
+      "display_name": "asp.net-stripped-raw",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet-stripped"
     }
   }]
 }

+ 98 - 80
frameworks/CSharp/aspnet/benchmark_config.json

@@ -9,52 +9,55 @@
       "approach": "Realistic",
       "classification": "Fullstack",
       "database": "None",
-      "framework": "aspnet-mvc",
+      "framework": "asp.net",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "os": "Windows",
       "database_os": "Linux",
-      "display_name": "aspnet",
+      "display_name": "asp.net",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     "jsonnet": {
       "setup_file": "setup_iis",
       "json_url": "/json/jsonnet",
       "port": 8080,
       "approach": "Realistic",
-      "classification": "Platform",
+      "classification": "Fullstack",
       "database": "None",
-      "framework": "aspnet",
+      "framework": "asp.net",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "os": "Windows",
       "database_os": "Linux",
-      "display_name": "aspnet-jsonnet",
+      "display_name": "jsonnet",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     "servicestack": {
       "setup_file": "setup_iis",
       "json_url": "/json/servicestack",
       "port": 8080,
       "approach": "Realistic",
-      "classification": "Platform",
+      "classification": "Fullstack",
       "database": "None",
-      "framework": "aspnet",
+      "framework": "asp.net",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "os": "Windows",
       "database_os": "Linux",
-      "display_name": "aspnet-svcstk",
+      "display_name": "servicestack",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     "mysql-raw": {
       "setup_file": "setup_iis",
@@ -66,16 +69,17 @@
       "approach": "Realistic",
       "classification": "Fullstack",
       "database": "MySQL",
-      "framework": "aspnet-mvc",
+      "framework": "asp.net",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "os": "Windows",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-raw",
+      "display_name": "asp.net-raw",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     "postgresql-raw": {
       "setup_file": "setup_iis",
@@ -87,16 +91,17 @@
       "approach": "Realistic",
       "classification": "Fullstack",
       "database": "Postgres",
-      "framework": "aspnet-mvc",
+      "framework": "asp.net",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "os": "Windows",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-raw",
+      "display_name": "asp.net-raw",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     "mongodb-raw": {
       "setup_file": "setup_iis",
@@ -108,16 +113,17 @@
       "approach": "Realistic",
       "classification": "Fullstack",
       "database": "MongoDB",
-      "framework": "aspnet-mvc",
+      "framework": "asp.net",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "os": "Windows",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-raw",
+      "display_name": "asp.net-raw",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     "sqlserver-raw": {
       "setup_file": "setup_iis",
@@ -129,16 +135,17 @@
       "approach": "Realistic",
       "classification": "Fullstack",
       "database": "SQLServer",
-      "framework": "aspnet-mvc",
+      "framework": "asp.net-mvc",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "os": "Windows",
       "database_os": "Windows",
-      "display_name": "aspnet-mvc-raw",
+      "display_name": "aspnet-raw",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     "mysql-entityframework": {
       "setup_file": "setup_iis",
@@ -150,16 +157,17 @@
       "approach": "Realistic",
       "classification": "Fullstack",
       "database": "MySQL",
-      "framework": "aspnet-mvc",
+      "framework": "EntityFramework",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Full",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "os": "Windows",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc",
+      "display_name": "EntityFramework",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     "postgresql-entityframework": {
       "setup_file": "setup_iis",
@@ -171,16 +179,17 @@
       "approach": "Realistic",
       "classification": "Fullstack",
       "database": "Postgres",
-      "framework": "aspnet-mvc",
+      "framework": "EntityFramework",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Full",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "os": "Windows",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc",
+      "display_name": "EntityFramework",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     "sqlserver-entityframework": {
       "setup_file": "setup_iis",
@@ -192,16 +201,17 @@
       "approach": "Realistic",
       "classification": "Fullstack",
       "database": "SQLServer",
-      "framework": "aspnet-mvc",
+      "framework": "EntityFramework",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Full",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "os": "Windows",
       "database_os": "Windows",
-      "display_name": "aspnet-mvc",
+      "display_name": "EntityFramework",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     "mono": {
       "setup_file": "setup_nginx",
@@ -209,54 +219,57 @@
       "plaintext_url": "/plaintext",
       "port": 8080,
       "approach": "Realistic",
-      "classification": "Fullstack",
+      "classification": "Platform",
       "database": "None",
-      "framework": "aspnet-mvc",
+      "framework": "None",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Raw",
-      "platform": "Mono",
+      "platform": "ASP.NET",
       "webserver": "nginx",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-mono",
+      "display_name": "ASP.NET",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet-mono"
     },
     "mono-jsonnet": {
       "setup_file": "setup_nginx",
       "json_url": "/json/jsonnet",
       "port": 8080,
       "approach": "Realistic",
-      "classification": "Platform",
+      "classification": "Fullstack",
       "database": "None",
-      "framework": "aspnet",
+      "framework": "JSONNet",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Raw",
-      "platform": "Mono",
+      "platform": "ASP.NET",
       "webserver": "nginx",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-jsonnet-mono",
+      "display_name": "JSONNet",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet-mono"
     },
     "mono-servicestack": {
       "setup_file": "setup_nginx",
       "json_url": "/json/servicestack",
       "port": 8080,
       "approach": "Realistic",
-      "classification": "Platform",
+      "classification": "Fullstack",
       "database": "None",
-      "framework": "aspnet",
+      "framework": "ServiceStack",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Raw",
-      "platform": "Mono",
+      "platform": "ASP.NET",
       "webserver": "nginx",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-svcstk-mono",
+      "display_name": "ServiceStack",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet-mono"
     },
     "mono-mysql-raw": {
       "setup_file": "setup_nginx",
@@ -268,18 +281,19 @@
       "update_url": "/ado/mysql/update?queries=",
       "port": 8080,
       "approach": "Realistic",
-      "classification": "Fullstack",
+      "classification": "Platform",
       "database": "MySQL",
-      "framework": "aspnet-mvc",
+      "framework": "None",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Raw",
-      "platform": "Mono",
+      "platform": "ASP.NET",
       "webserver": "nginx",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-mono-raw",
+      "display_name": "ASP.NET-raw",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     "mono-postgresql-raw": {
       "setup_file": "setup_nginx",
@@ -289,18 +303,19 @@
       "update_url": "/ado/postgresql/update?queries=",
       "port": 8080,
       "approach": "Realistic",
-      "classification": "Fullstack",
+      "classification": "Platform",
       "database": "Postgres",
-      "framework": "aspnet-mvc",
+      "framework": "None",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Raw",
-      "platform": "Mono",
+      "platform": "ASP.NET",
       "webserver": "nginx",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-mono-raw",
+      "display_name": "ASP.NET-raw",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     "mono-mongodb-raw": {
       "setup_file": "setup_nginx",
@@ -310,18 +325,19 @@
       "update_url": "/mongodb/update?queries=",
       "port": 8080,
       "approach": "Realistic",
-      "classification": "Fullstack",
+      "classification": "Platform",
       "database": "MongoDB",
-      "framework": "aspnet-mvc",
+      "framework": "None",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Raw",
-      "platform": "Mono",
+      "platform": "ASP.NET",
       "webserver": "nginx",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-mono-raw",
+      "display_name": "aspnet-raw",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     "mono-mysql-entityframework": {
       "setup_file": "setup_nginx",
@@ -333,16 +349,17 @@
       "approach": "Realistic",
       "classification": "Fullstack",
       "database": "MySQL",
-      "framework": "aspnet-mvc",
+      "framework": "EntityFramework",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Full",
-      "platform": "Mono",
+      "platform": "ASP.NET",
       "webserver": "nginx",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-mono",
+      "display_name": "EntityFramework",
       "notes": "Entity framework",
-      "versus": ""
+      "versus": "aspnet"
     },
     "mono-postgresql-entityframework": {
       "setup_file": "setup_nginx",
@@ -354,16 +371,17 @@
       "approach": "Realistic",
       "classification": "Fullstack",
       "database": "Postgres",
-      "framework": "aspnet-mvc",
+      "framework": "EntityFramework",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Full",
-      "platform": "Mono",
+      "platform": "ASP.NET",
       "webserver": "nginx",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-mono",
+      "display_name": "EntityFramework",
       "notes": "Entity framework",
-      "versus": ""
+      "versus": "aspnet"
     }
   }]
 }

+ 1 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Configuration/AppSettings.cs

@@ -7,5 +7,6 @@ namespace Benchmarks.Configuration
     public class AppSettings
     {
         public string ConnectionString { get; set; }
+        public DatabaseServer Database { get; set; } = DatabaseServer.SqlServer;
     }
 }

+ 12 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Configuration/DatabaseServer.cs

@@ -0,0 +1,12 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+
+namespace Benchmarks.Configuration
+{
+    public enum DatabaseServer
+    {
+        SqlServer,
+        PostgreSql
+    }
+}

+ 30 - 12
frameworks/CSharp/aspnetcore/Benchmarks/Configuration/Scenarios.cs

@@ -44,6 +44,15 @@ namespace Benchmarks.Configuration
         [ScenarioPath("/queries/dapper")]
         public bool DbMultiQueryDapper { get; set; }
 
+        [ScenarioPath("/updates/raw")]
+        public bool DbMultiUpdateRaw { get; set; }
+
+        [ScenarioPath("/updates/ef")]
+        public bool DbMultiUpdateEf { get; set; }
+
+        [ScenarioPath("/updates/dapper")]
+        public bool DbMultiUpdateDapper { get; set; }
+
         [ScenarioPath("/fortunes/raw")]
         public bool DbFortunesRaw { get; set; }
 
@@ -62,23 +71,32 @@ namespace Benchmarks.Configuration
         [ScenarioPath("/mvc/view")]
         public bool MvcViews { get; set; }
 
-        //[ScenarioPath("/mvc/db/raw")]
-        //public bool MvcDbSingleQueryRaw { get; set; }
+        [ScenarioPath("/mvc/db/raw")]
+        public bool MvcDbSingleQueryRaw { get; set; }
+
+        [ScenarioPath("/mvc/db/dapper")]
+        public bool MvcDbSingleQueryDapper { get; set; }
+
+        [ScenarioPath("/mvc/db/ef")]
+        public bool MvcDbSingleQueryEf { get; set; }
+
+        [ScenarioPath("/mvc/queries/raw")]
+        public bool MvcDbMultiQueryRaw { get; set; }
 
-        //[ScenarioPath("/mvc/db/dapper")]
-        //public bool MvcDbSingleQueryDapper { get; set; }
+        [ScenarioPath("/mvc/queries/dapper")]
+        public bool MvcDbMultiQueryDapper { get; set; }
 
-        //[ScenarioPath("/mvc/db/ef")]
-        //public bool MvcDbSingleQueryEf { get; set; }
+        [ScenarioPath("/mvc/queries/ef")]
+        public bool MvcDbMultiQueryEf { get; set; }
 
-        //[ScenarioPath("/mvc/queries/raw")]
-        //public bool MvcDbMultiQueryRaw { get; set; }
+        [ScenarioPath("/mvc/updates/raw")]
+        public bool MvcDbMultiUpdateRaw { get; set; }
 
-        //[ScenarioPath("/mvc/queries/dapper")]
-        //public bool MvcDbMultiQueryDapper { get; set; }
+        [ScenarioPath("/mvc/updates/dapper")]
+        public bool MvcDbMultiUpdateDapper { get; set; }
 
-        //[ScenarioPath("/mvc/queries/ef")]
-        //public bool MvcDbMultiQueryEf { get; set; }
+        [ScenarioPath("/mvc/updates/ef")]
+        public bool MvcDbMultiUpdateEf { get; set; }
 
         [ScenarioPath("/mvc/fortunes/raw")]
         public bool MvcDbFortunesRaw { get; set; }

+ 11 - 51
frameworks/CSharp/aspnetcore/Benchmarks/Controllers/FortunesController.cs

@@ -8,68 +8,28 @@ using Microsoft.Extensions.DependencyInjection;
 
 namespace Benchmarks.Controllers
 {
-    [Route("mvc")]
+    [Route("mvc/fortunes")]
     public class FortunesController : Controller
     {
-        private RawDb _rawDb;
-        private DapperDb _dapperDb;
-        private EfDb _efDb;
-
-        private RawDb RawDb
-        {
-            get
-            {
-                if (_rawDb == null)
-                {
-                    _rawDb = HttpContext.RequestServices.GetRequiredService<RawDb>();
-                }
-
-                return _rawDb;
-            }
-        }
-
-        private DapperDb DapperDb
-        {
-            get
-            {
-                if (_dapperDb == null)
-                {
-                    _dapperDb = HttpContext.RequestServices.GetRequiredService<DapperDb>();
-                }
-
-                return _dapperDb;
-            }
-        }
-
-        private EfDb EfDb
-        {
-            get
-            {
-                if (_efDb == null)
-                {
-                    _efDb = HttpContext.RequestServices.GetRequiredService<EfDb>();
-                }
-
-                return _efDb;
-            }
-        }
-
-        [HttpGet("fortunes/raw")]
+        [HttpGet("raw")]
         public async Task<IActionResult> Raw()
-        {
-            return View("Fortunes", await RawDb.LoadFortunesRows());
+        {   
+            var db = HttpContext.RequestServices.GetRequiredService<RawDb>();
+            return View("Fortunes", await db.LoadFortunesRows());
         }
 
-        [HttpGet("fortunes/dapper")]
+        [HttpGet("dapper")]
         public async Task<IActionResult> Dapper()
         {
-            return View("Fortunes", await DapperDb.LoadFortunesRows());
+            var db = HttpContext.RequestServices.GetRequiredService<DapperDb>();
+            return View("Fortunes", await db.LoadFortunesRows());
         }
 
-        [HttpGet("fortunes/ef")]
+        [HttpGet("ef")]
         public async Task<IActionResult> Ef()
         {
-            return View("Fortunes", await EfDb.LoadFortunesRows());
+            var db = HttpContext.RequestServices.GetRequiredService<EfDb>();
+            return View("Fortunes", await db.LoadFortunesRows());
         }
     }
 }

+ 42 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Controllers/MultipleQueriesController.cs

@@ -0,0 +1,42 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading.Tasks;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Benchmarks.Controllers
+{
+    [Route("mvc/queries")]
+    public class MultipleQueriesController : Controller
+    {
+        [HttpGet("raw")]
+        [Produces("application/json")]
+        public Task<World[]> Raw(int queries = 1)
+        {
+            return ExecuteQuery<RawDb>(queries);
+        }
+
+        [HttpGet("dapper")]
+        [Produces("application/json")]
+        public Task<World[]> Dapper(int queries = 1)
+        {
+            return ExecuteQuery<DapperDb>(queries);
+        }
+
+        [HttpGet("ef")]
+        [Produces("application/json")]
+        public Task<World[]> Ef(int queries = 1)
+        {
+            return ExecuteQuery<EfDb>(queries);
+        }
+
+        private Task<World[]> ExecuteQuery<T>(int queries) where T : IDb
+        {
+            queries = queries < 1 ? 1 : queries > 500 ? 500 : queries;
+            var db = HttpContext.RequestServices.GetRequiredService<T>();
+            return db.LoadMultipleQueriesRows(queries);
+        }
+    }
+}

+ 42 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Controllers/MultipleUpdatesController.cs

@@ -0,0 +1,42 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading.Tasks;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Benchmarks.Controllers
+{
+    [Route("mvc/updates")]
+    public class MultipleUpdatesController : Controller
+    {
+        [HttpGet("raw")]
+        [Produces("application/json")]
+        public Task<World[]> Raw(int queries = 1)
+        {
+            return ExecuteQuery<RawDb>(queries);
+        }
+
+        [HttpGet("dapper")]
+        [Produces("application/json")]
+        public Task<World[]> Dapper(int queries = 1)
+        {
+            return ExecuteQuery<DapperDb>(queries);
+        }
+
+        [HttpGet("ef")]
+        [Produces("application/json")]
+        public Task<World[]> Ef(int queries = 1)
+        {
+            return ExecuteQuery<EfDb>(queries);
+        }
+
+        private Task<World[]> ExecuteQuery<T>(int queries) where T : IDb
+        {
+            queries = queries < 1 ? 1 : queries > 500 ? 500 : queries;
+            var db = HttpContext.RequestServices.GetRequiredService<T>();
+            return db.LoadMultipleUpdatesRows(queries);
+        }
+    }
+}

+ 41 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Controllers/SingleQueryController.cs

@@ -0,0 +1,41 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading.Tasks;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Benchmarks.Controllers
+{
+    [Route("mvc/db")]
+    public class SingleQueryController : Controller
+    {
+        [HttpGet("raw")]
+        [Produces("application/json")]
+        public Task<World> Raw()
+        {
+            return ExecuteQuery<RawDb>();
+        }
+
+        [HttpGet("dapper")]
+        [Produces("application/json")]
+        public Task<World> Dapper()
+        {
+            return ExecuteQuery<DapperDb>();
+        }
+
+        [HttpGet("ef")]
+        [Produces("application/json")]
+        public Task<World> Ef()
+        {
+            return ExecuteQuery<EfDb>();
+        }
+
+        private Task<World> ExecuteQuery<T>() where T : IDb
+        {
+            var db = HttpContext.RequestServices.GetRequiredService<T>();
+            return db.LoadSingleQueryRow();
+        }
+    }
+}

+ 49 - 2
frameworks/CSharp/aspnetcore/Benchmarks/Data/ApplicationDbContext.cs

@@ -1,9 +1,14 @@
 // Copyright (c) .NET Foundation. All rights reserved. 
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
+using System.Linq;
 using Benchmarks.Configuration;
 using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage.Internal;
+using Microsoft.EntityFrameworkCore.Infrastructure.Internal;
 using Microsoft.Extensions.Options;
+using Microsoft.Extensions.DependencyInjection;
 
 namespace Benchmarks.Data
 {
@@ -20,9 +25,51 @@ namespace Benchmarks.Data
 
         public DbSet<Fortune> Fortune { get; set; }
 
+        public bool UseBatchUpdate 
+        { 
+            get
+            {
+                return _appSettings.Database != DatabaseServer.PostgreSql;
+            }
+        } 
+
         protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
         {
-            optionsBuilder.UseSqlServer(_appSettings.ConnectionString);
+            if (_appSettings.Database == DatabaseServer.PostgreSql)
+            {
+                optionsBuilder.UseNpgsql(_appSettings.ConnectionString);
+            }
+            else
+            {
+                var extension = GetOrCreateExtension(optionsBuilder);
+                extension.ConnectionString = _appSettings.ConnectionString;
+                ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
+            }
+        }
+
+        private static SqlServerOptionsExtension GetOrCreateExtension(DbContextOptionsBuilder optionsBuilder)
+        {
+            var existing = optionsBuilder.Options.FindExtension<NoTxSqlServerOptionsExtension>();
+            return existing != null
+                ? new NoTxSqlServerOptionsExtension(existing)
+                : new NoTxSqlServerOptionsExtension();
+        }
+
+        private class NoTxSqlServerOptionsExtension : SqlServerOptionsExtension
+        {
+            public NoTxSqlServerOptionsExtension()
+            {
+            }
+
+            public NoTxSqlServerOptionsExtension(NoTxSqlServerOptionsExtension copyFrom) : base(copyFrom)
+            {
+            }
+            public override void ApplyServices(IServiceCollection services)
+            {
+                base.ApplyServices(services);
+                services.Remove(services.First((sd) => sd.ServiceType == typeof(ISqlServerConnection)));
+                services.AddScoped<ISqlServerConnection, NoTransactionSqlServerConnection>();
+            }
         }
     }
-}
+}

+ 24 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Data/BatchUpdateString.cs

@@ -0,0 +1,24 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Benchmarks.Data
+{
+    internal class BatchUpdateString
+    {
+        public static IList<BatchUpdateString> Strings { get;} = 
+            Enumerable.Range(0, 500)
+                      .Select(i => new BatchUpdateString
+                      {
+                          Id = $"Id_{i}",
+                          Random = $"Random_{i}",
+                          UpdateQuery = $"UPDATE world SET randomnumber = @Random_{i} WHERE id = @Id_{i};"
+                      }).ToArray();
+                        
+        public string Id { get; set; }
+        public string Random { get; set; }
+        public string UpdateQuery { get; set; }
+    }
+}

+ 50 - 12
frameworks/CSharp/aspnetcore/Benchmarks/Data/DapperDb.cs

@@ -1,8 +1,11 @@
 // Copyright (c) .NET Foundation. All rights reserved. 
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
+using System;
 using System.Collections.Generic;
 using System.Data.Common;
+using System.Dynamic;
+using System.Text;
 using System.Threading.Tasks;
 using Benchmarks.Configuration;
 using Dapper;
@@ -10,7 +13,7 @@ using Microsoft.Extensions.Options;
 
 namespace Benchmarks.Data
 {
-    public class DapperDb
+    public class DapperDb : IDb
     {
         private readonly IRandom _random;
         private readonly DbProviderFactory _dbProviderFactory;
@@ -30,15 +33,39 @@ namespace Benchmarks.Data
                 db.ConnectionString = _connectionString;
 
                 // Note: Don't need to open connection if only doing one thing; let dapper do it
-                return await db.QueryFirstOrDefaultAsync<World>(
-                    "SELECT [Id], [RandomNumber] FROM [World] WHERE [Id] = @Id",
-                    new { Id = _random.Next(1, 10001) });
+                return await ReadSingleRow(db);
             }
         }
 
+        async Task<World> ReadSingleRow(DbConnection db)
+        {
+            return await db.QueryFirstOrDefaultAsync<World>(
+                    "SELECT id, randomnumber FROM world WHERE id = @Id",
+                    new { Id = _random.Next(1, 10001) });
+        }
+
         public async Task<World[]> LoadMultipleQueriesRows(int count)
         {
-            var result = new World[count];
+            var results = new World[count];
+            using (var db = _dbProviderFactory.CreateConnection())
+            {
+                db.ConnectionString = _connectionString;
+                await db.OpenAsync();
+
+                for (int i = 0; i < count; i++)
+                {
+                    results[i] = await ReadSingleRow(db);
+                }
+            }
+
+            return results;
+        }
+
+        public async Task<World[]> LoadMultipleUpdatesRows(int count)
+        {
+            var results = new World[count];
+            IDictionary<string, object> parameters = new ExpandoObject();
+            var updateCommand = new StringBuilder(count);
 
             using (var db = _dbProviderFactory.CreateConnection())
             {
@@ -47,15 +74,26 @@ namespace Benchmarks.Data
 
                 for (int i = 0; i < count; i++)
                 {
-                    result[i] = await db.QueryFirstOrDefaultAsync<World>(
-                        "SELECT [Id], [RandomNumber] FROM [World] WHERE [Id] = @Id",
-                        new { Id = _random.Next(1, 10001) });
+                    results[i] = await ReadSingleRow(db);
+                }
+
+                // postgres has problems with deadlocks when these aren't sorted
+                Array.Sort<World>(results, (a, b) => a.Id.CompareTo(b.Id));
+
+                for (int i = 0; i < count; i++)
+                {
+                    var randomNumber = _random.Next(1, 10001);
+                    parameters[BatchUpdateString.Strings[i].Random] = randomNumber;
+                    parameters[BatchUpdateString.Strings[i].Id] = results[i].Id;
+
+                    results[i].RandomNumber = randomNumber;
+                    updateCommand.Append(BatchUpdateString.Strings[i].UpdateQuery);
                 }
 
-                db.Close();
+                await db.ExecuteAsync(updateCommand.ToString(), parameters);
             }
 
-            return result;
+            return results;
         }
 
         public async Task<IEnumerable<Fortune>> LoadFortunesRows()
@@ -67,7 +105,7 @@ namespace Benchmarks.Data
                 db.ConnectionString = _connectionString;
 
                 // Note: don't need to open connection if only doing one thing; let dapper do it
-                result = (await db.QueryAsync<Fortune>("SELECT [Id], [Message] FROM [Fortune]")).AsList();
+                result = (await db.QueryAsync<Fortune>("SELECT id, message FROM fortune")).AsList();
             }
 
             result.Add(new Fortune { Message = "Additional fortune added at request time." });
@@ -76,4 +114,4 @@ namespace Benchmarks.Data
             return result;
         }
     }
-}
+}

+ 27 - 3
frameworks/CSharp/aspnetcore/Benchmarks/Data/EfDb.cs

@@ -7,7 +7,7 @@ using Microsoft.EntityFrameworkCore;
 
 namespace Benchmarks.Data
 {
-    public class EfDb
+    public class EfDb : IDb
     {
         private readonly IRandom _random;
         private readonly ApplicationDbContext _dbContext;
@@ -22,7 +22,6 @@ namespace Benchmarks.Data
         public Task<World> LoadSingleQueryRow()
         {
             var id = _random.Next(1, 10001);
-            
             return _dbContext.World.FirstAsync(w => w.Id == id);
         }
 
@@ -39,6 +38,31 @@ namespace Benchmarks.Data
             return result;
         }
 
+        public async Task<World[]> LoadMultipleUpdatesRows(int count)
+        {
+            var results = new World[count];
+
+            for (int i = 0; i < count; i++)
+            {
+                var id = _random.Next(1, 10001);
+                var result = await _dbContext.World.AsTracking().FirstAsync(w => w.Id == id);
+
+                result.RandomNumber = _random.Next(1, 10001);
+                results[i] = result;
+                if(!_dbContext.UseBatchUpdate)
+                {
+                    await _dbContext.SaveChangesAsync();
+                }
+            }
+            
+            if(_dbContext.UseBatchUpdate)
+            {
+                await _dbContext.SaveChangesAsync();
+            }
+
+            return results;
+        }
+
         public async Task<IEnumerable<Fortune>> LoadFortunesRows()
         {
             var result = await _dbContext.Fortune.ToListAsync();
@@ -49,4 +73,4 @@ namespace Benchmarks.Data
             return result;
         }
     }
-}
+}

+ 4 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Data/Fortune.cs

@@ -3,13 +3,17 @@
 
 using System;
 using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
 
 namespace Benchmarks.Data
 {
+    [Table("fortune")]
     public class Fortune : IComparable<Fortune>, IComparable
     {
+        [Column("id")]
         public int Id { get; set; }
 
+        [Column("message")]
         [StringLength(2048)]
         public string Message { get; set; }
         

+ 19 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Data/IDb.cs

@@ -0,0 +1,19 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Benchmarks.Data
+{
+    public interface IDb
+    {
+        Task<World> LoadSingleQueryRow();
+
+        Task<World[]> LoadMultipleQueriesRows(int count);
+
+        Task<World[]> LoadMultipleUpdatesRows(int count);
+
+        Task<IEnumerable<Fortune>> LoadFortunesRows();
+    }
+}

+ 43 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Data/NoTransactionSqlServerConnection.cs

@@ -0,0 +1,43 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System.Data;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.Storage.Internal;
+using Microsoft.Extensions.Logging;
+
+namespace Benchmarks.Data
+{
+    class NoTransactionSqlServerConnection : SqlServerConnection
+    {
+        public NoTransactionSqlServerConnection(IDbContextOptions options, ILogger<SqlServerConnection> logger)
+            : base(options, logger)
+        {
+        }
+
+        public override Task<IDbContextTransaction> BeginTransactionAsync(
+            IsolationLevel isolationLevel, CancellationToken cancellationToken = new CancellationToken())
+            => Task.FromResult<IDbContextTransaction>(new FakeTransaction());
+
+        public override IDbContextTransaction BeginTransaction(IsolationLevel isolationLevel) 
+            => new FakeTransaction();
+
+        private class FakeTransaction : IDbContextTransaction
+        {
+            public void Dispose()
+            {
+            }
+
+            public void Commit()
+            {
+            }
+
+            public void Rollback()
+            {
+            }
+        }
+    }
+}

+ 6 - 3
frameworks/CSharp/aspnetcore/Benchmarks/Data/Random.cs

@@ -2,16 +2,19 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
 using System;
+using System.Threading;
 
 namespace Benchmarks.Data
 {
     public class DefaultRandom : IRandom
     {
-        private readonly Random _random = new Random();
+        private static int nextSeed = 0;
+        // Random isn't thread safe
+        private static readonly ThreadLocal<Random> _random = new ThreadLocal<Random>(() => new Random(Interlocked.Increment(ref nextSeed)));
 
         public int Next(int minValue, int maxValue)
         {
-            return _random.Next(minValue, maxValue);
+            return _random.Value.Next(minValue, maxValue);
         }
     }
-}
+}

+ 89 - 39
frameworks/CSharp/aspnetcore/Benchmarks/Data/RawDb.cs

@@ -1,21 +1,23 @@
 // Copyright (c) .NET Foundation. All rights reserved. 
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
+using System;
 using System.Collections.Generic;
 using System.Data;
 using System.Data.Common;
+using System.Text;
 using System.Threading.Tasks;
 using Benchmarks.Configuration;
 using Microsoft.Extensions.Options;
 
 namespace Benchmarks.Data
 {
-    public class RawDb
+    public class RawDb : IDb
     {
         private readonly IRandom _random;
         private readonly DbProviderFactory _dbProviderFactory;
         private readonly string _connectionString;
-
+        
         public RawDb(IRandom random, DbProviderFactory dbProviderFactory, IOptions<AppSettings> appSettings)
         {
             _random = random;
@@ -26,66 +28,111 @@ namespace Benchmarks.Data
         public async Task<World> LoadSingleQueryRow()
         {
             using (var db = _dbProviderFactory.CreateConnection())
-            using (var cmd = db.CreateCommand())
+            using (var cmd = CreateReadCommand(db))
             {
-                cmd.CommandText = "SELECT [Id], [RandomNumber] FROM [World] WHERE [Id] = @Id";
-                var id = cmd.CreateParameter();
-                id.ParameterName = "@Id";
-                id.DbType = DbType.Int32;
-                id.Value = _random.Next(1, 10001);
-                cmd.Parameters.Add(id);
-
                 db.ConnectionString = _connectionString;
                 await db.OpenAsync();
 
-                using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))
-                {
-                    await rdr.ReadAsync();
+                return await ReadSingleRow(db, cmd);
+            }
+        }
+        
+        async Task<World> ReadSingleRow(DbConnection connection, DbCommand cmd)
+        {
+            // Prepared statements improve PostgreSQL performance by 10-15%
+            cmd.Prepare();
 
-                    return new World
-                    {
-                        Id = rdr.GetInt32(0),
-                        RandomNumber = rdr.GetInt32(1)
-                    };
-                }
+            using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.SingleRow))
+            {
+                await rdr.ReadAsync();
+
+                return new World
+                {
+                    Id = rdr.GetInt32(0),
+                    RandomNumber = rdr.GetInt32(1)
+                };
             }
         }
 
+        DbCommand CreateReadCommand(DbConnection connection)
+        {
+            var cmd = connection.CreateCommand();
+            cmd.CommandText = "SELECT id, randomnumber FROM world WHERE id = @Id";
+            var id = cmd.CreateParameter();
+            id.ParameterName = "@Id";
+            id.DbType = DbType.Int32;
+            id.Value = _random.Next(1, 10001);
+            cmd.Parameters.Add(id);
+
+            return cmd;
+        }
+
         public async Task<World[]> LoadMultipleQueriesRows(int count)
         {
             var result = new World[count];
 
             using (var db = _dbProviderFactory.CreateConnection())
-            using (var cmd = db.CreateCommand())
+            using (var cmd = CreateReadCommand(db))
             {
                 db.ConnectionString = _connectionString;
                 await db.OpenAsync();
+                for (int i = 0; i < count; i++)
+                {
+                    result[i] = await ReadSingleRow(db, cmd);
+                    cmd.Parameters["@Id"].Value = _random.Next(1, 10001);
+                }
+            }
+
+            return result;
+        }
+
+        public async Task<World[]> LoadMultipleUpdatesRows(int count)
+        {
+            var results = new World[count];
+           
+            var updateCommand = new StringBuilder(count);
 
-                cmd.CommandText = "SELECT [Id], [RandomNumber] FROM [World] WHERE [Id] = @Id";
-                var id = cmd.CreateParameter();
-                id.ParameterName = "@Id";
-                id.DbType = DbType.Int32;
-                cmd.Parameters.Add(id);
+            using (var db = _dbProviderFactory.CreateConnection())
+            using (var updateCmd = db.CreateCommand())
+            using (var queryCmd = CreateReadCommand(db))
+            {
+                db.ConnectionString = _connectionString;
+                await db.OpenAsync();
 
                 for (int i = 0; i < count; i++)
                 {
-                    id.Value = _random.Next(1, 10001);
-                    using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.SingleRow))
-                    {
-                        await rdr.ReadAsync();
+                    results[i] = await ReadSingleRow(db, queryCmd);
+                    queryCmd.Parameters["@Id"].Value = _random.Next(1, 10001);
+                }
 
-                        result[i] = new World
-                        {
-                            Id = rdr.GetInt32(0),
-                            RandomNumber = rdr.GetInt32(1)
-                        };
-                    }
+                // postgres has problems with deadlocks when these aren't sorted
+                Array.Sort<World>(results, (a, b) => a.Id.CompareTo(b.Id));
+
+                for(int i = 0; i < count; i++)
+                {
+                    var id = updateCmd.CreateParameter();
+                    id.ParameterName = BatchUpdateString.Strings[i].Id;
+                    id.DbType = DbType.Int32;
+                    updateCmd.Parameters.Add(id);
+
+                    var random = updateCmd.CreateParameter();
+                    random.ParameterName = BatchUpdateString.Strings[i].Random;
+                    id.DbType = DbType.Int32;
+                    updateCmd.Parameters.Add(random);
+
+                    var randomNumber = _random.Next(1, 10001);
+                    id.Value = results[i].Id;
+                    random.Value = randomNumber;
+                    results[i].RandomNumber = randomNumber;
+
+                    updateCommand.Append(BatchUpdateString.Strings[i].UpdateQuery);
                 }
 
-                db.Close();
+                updateCmd.CommandText = updateCommand.ToString();
+                await updateCmd.ExecuteNonQueryAsync();
             }
 
-            return result;
+            return results;
         }
 
         public async Task<IEnumerable<Fortune>> LoadFortunesRows()
@@ -95,11 +142,14 @@ namespace Benchmarks.Data
             using (var db = _dbProviderFactory.CreateConnection())
             using (var cmd = db.CreateCommand())
             {
-                cmd.CommandText = "SELECT [Id], [Message] FROM [Fortune]";
+                cmd.CommandText = "SELECT id, message FROM fortune";
 
                 db.ConnectionString = _connectionString;
                 await db.OpenAsync();
 
+                // Prepared statements improve PostgreSQL performance by 10-15%
+                cmd.Prepare();
+
                 using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))
                 {
                     while (await rdr.ReadAsync())
@@ -119,4 +169,4 @@ namespace Benchmarks.Data
             return result;
         }
     }
-}
+}

+ 5 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Data/World.cs

@@ -1,12 +1,17 @@
 // Copyright (c) .NET Foundation. All rights reserved. 
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
+using System.ComponentModel.DataAnnotations.Schema;
+
 namespace Benchmarks.Data
 {
+    [Table("world")]
     public class World
     {
+        [Column("id")] 
         public int Id { get; set; }
 
+        [Column("randomnumber")] 
         public int RandomNumber { get; set; }
     }
 }

+ 0 - 47
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/ErrorHandlerMiddleware.cs

@@ -1,47 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved. 
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
-
-using System;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Http;
-
-namespace Benchmarks.Middleware
-{
-    public class ErrorHandlerMiddleware
-    {
-        private readonly RequestDelegate _next;
-
-        public ErrorHandlerMiddleware(RequestDelegate next)
-        {
-            _next = next;
-        }
-
-        public async Task Invoke(HttpContext httpContext)
-        {
-            try
-            {
-                await _next(httpContext);
-            }
-            catch (Exception ex)
-            {
-                if (!httpContext.Response.HasStarted)
-                {
-                    httpContext.Response.Clear();
-                    httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
-                    httpContext.Response.ContentType = "text/html";
-                }
-                
-                await httpContext.Response.WriteAsync($"<pre style='color:red'>{ex.ToString()}</pre>");
-            }
-        }
-    }
-
-    public static class ErrorHandlerMiddlewareExtensions
-    {
-        public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder builder)
-        {
-            return builder.UseMiddleware<ErrorHandlerMiddleware>();
-        }
-    }
-}

+ 5 - 5
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/FortunesEfMiddleware.cs

@@ -8,6 +8,7 @@ using Benchmarks.Configuration;
 using Benchmarks.Data;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
 
 namespace Benchmarks.Middleware
 {
@@ -16,13 +17,11 @@ namespace Benchmarks.Middleware
         private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbFortunesEf));
 
         private readonly RequestDelegate _next;
-        private readonly EfDb _db;
         private readonly HtmlEncoder _htmlEncoder;
 
-        public FortunesEfMiddleware(RequestDelegate next, EfDb db, HtmlEncoder htmlEncoder)
+        public FortunesEfMiddleware(RequestDelegate next, HtmlEncoder htmlEncoder)
         {
             _next = next;
-            _db = db;
             _htmlEncoder = htmlEncoder;
         }
 
@@ -30,7 +29,8 @@ namespace Benchmarks.Middleware
         {
             if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
             {
-                var rows = await _db.LoadFortunesRows();
+                var db = httpContext.RequestServices.GetService<EfDb>();
+                var rows = await db.LoadFortunesRows();
 
                 await MiddlewareHelpers.RenderFortunesHtml(rows, httpContext, _htmlEncoder);
 
@@ -48,4 +48,4 @@ namespace Benchmarks.Middleware
             return builder.UseMiddleware<FortunesEfMiddleware>();
         }
     }
-}
+}

+ 14 - 5
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MiddlewareHelpers.cs

@@ -2,6 +2,8 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
 using System.Text.Encodings.Web;
 using System.Threading.Tasks;
 using Benchmarks.Data;
@@ -33,15 +35,22 @@ namespace Benchmarks.Middleware
             httpContext.Response.StatusCode = StatusCodes.Status200OK;
             httpContext.Response.ContentType = "text/html; charset=UTF-8";
 
-            await httpContext.Response.WriteAsync("<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>");
-
+            var sb = new StringBuilder();
+            sb.Append("<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>");
             foreach (var item in model)
             {
-                await httpContext.Response.WriteAsync(
-                    $"<tr><td>{htmlEncoder.Encode(item.Id.ToString())}</td><td>{htmlEncoder.Encode(item.Message)}</td></tr>");
+                sb.Append("<tr><td>");
+                sb.Append(item.Id.ToString(CultureInfo.InvariantCulture));
+                sb.Append("</td><td>");
+                sb.Append(htmlEncoder.Encode(item.Message));
+                sb.Append("</td></tr>");
             }
 
-            await httpContext.Response.WriteAsync("</table></body></html>");
+            sb.Append("</table></body></html>");
+            var response = sb.ToString();
+            // fortunes includes multibyte characters so response.Length is incorrect
+            httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(response);
+            await httpContext.Response.WriteAsync(response);
         }
     }
 }

+ 5 - 5
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleQueriesEfMiddleware.cs

@@ -7,6 +7,7 @@ using Benchmarks.Configuration;
 using Benchmarks.Data;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
 using Newtonsoft.Json;
 using Newtonsoft.Json.Serialization;
 
@@ -21,20 +22,19 @@ namespace Benchmarks.Middleware
         };
 
         private readonly RequestDelegate _next;
-        private readonly EfDb _db;
 
-        public MultipleQueriesEfMiddleware(RequestDelegate next, EfDb db)
+        public MultipleQueriesEfMiddleware(RequestDelegate next)
         {
             _next = next;
-            _db = db;
         }
 
         public async Task Invoke(HttpContext httpContext)
         {
             if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
             {
+                var db = httpContext.RequestServices.GetService<EfDb>();
                 var count = MiddlewareHelpers.GetMultipleQueriesQueryCount(httpContext);
-                var rows = await _db.LoadMultipleQueriesRows(count);
+                var rows = await db.LoadMultipleQueriesRows(count);
 
                 var result = JsonConvert.SerializeObject(rows, _jsonSettings);
 
@@ -58,4 +58,4 @@ namespace Benchmarks.Middleware
             return builder.UseMiddleware<MultipleQueriesEfMiddleware>();
         }
     }
-}
+}

+ 61 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleUpdatesDapperMiddleware.cs

@@ -0,0 +1,61 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.Threading.Tasks;
+using Benchmarks.Configuration;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Benchmarks.Middleware
+{
+    public class MultipleUpdatesDapperMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbMultiUpdateDapper));
+        private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
+        {
+            ContractResolver = new CamelCasePropertyNamesContractResolver()
+        };
+
+        private readonly RequestDelegate _next;
+        private readonly DapperDb _db;
+
+        public MultipleUpdatesDapperMiddleware(RequestDelegate next, DapperDb db)
+        {
+            _next = next;
+            _db = db;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                var count = MiddlewareHelpers.GetMultipleQueriesQueryCount(httpContext);
+                var rows = await _db.LoadMultipleUpdatesRows(count);
+
+                var result = JsonConvert.SerializeObject(rows, _jsonSettings);
+
+                httpContext.Response.StatusCode = StatusCodes.Status200OK;
+                httpContext.Response.ContentType = "application/json";
+                httpContext.Response.ContentLength = result.Length;
+
+                await httpContext.Response.WriteAsync(result);
+
+                return;
+            }
+
+            await _next(httpContext);
+        }
+    }
+
+    public static class MultipleUpdatesDapperMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseMultipleUpdatesDapper(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<MultipleUpdatesDapperMiddleware>();
+        }
+    }
+}

+ 61 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleUpdatesEfMiddleware.cs

@@ -0,0 +1,61 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.Threading.Tasks;
+using Benchmarks.Configuration;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Benchmarks.Middleware
+{
+    public class MultipleUpdatesEfMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbMultiUpdateEf));
+        private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
+        {
+            ContractResolver = new CamelCasePropertyNamesContractResolver()
+        };
+
+        private readonly RequestDelegate _next;
+
+        public MultipleUpdatesEfMiddleware(RequestDelegate next)
+        {
+            _next = next;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                var db = httpContext.RequestServices.GetService<EfDb>();
+                var count = MiddlewareHelpers.GetMultipleQueriesQueryCount(httpContext);
+                var rows = await db.LoadMultipleUpdatesRows(count);
+
+                var result = JsonConvert.SerializeObject(rows, _jsonSettings);
+
+                httpContext.Response.StatusCode = StatusCodes.Status200OK;
+                httpContext.Response.ContentType = "application/json";
+                httpContext.Response.ContentLength = result.Length;
+
+                await httpContext.Response.WriteAsync(result);
+
+                return;
+            }
+
+            await _next(httpContext);
+        }
+    }
+
+    public static class MultipleUpdatesEfMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseMultipleUpdatesEf(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<MultipleUpdatesEfMiddleware>();
+        }
+    }
+}

+ 61 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleUpdatesRawMiddleware.cs

@@ -0,0 +1,61 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.Threading.Tasks;
+using Benchmarks.Configuration;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Benchmarks.Middleware
+{
+    public class MultipleUpdatesRawMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbMultiUpdateRaw));
+        private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
+        {
+            ContractResolver = new CamelCasePropertyNamesContractResolver()
+        };
+
+        private readonly RequestDelegate _next;
+        private readonly RawDb _db;
+
+        public MultipleUpdatesRawMiddleware(RequestDelegate next, RawDb db)
+        {
+            _next = next;
+            _db = db;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                var count = MiddlewareHelpers.GetMultipleQueriesQueryCount(httpContext);
+                var rows = await _db.LoadMultipleUpdatesRows(count);
+
+                var result = JsonConvert.SerializeObject(rows, _jsonSettings);
+
+                httpContext.Response.StatusCode = StatusCodes.Status200OK;
+                httpContext.Response.ContentType = "application/json";
+                httpContext.Response.ContentLength = result.Length;
+
+                await httpContext.Response.WriteAsync(result);
+
+                return;
+            }
+
+            await _next(httpContext);
+        }
+    }
+
+    public static class MultipleUpdatesRawMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseMultipleUpdatesRaw(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<MultipleUpdatesRawMiddleware>();
+        }
+    }
+}

+ 5 - 5
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/SingleQueryEfMiddleware.cs

@@ -7,6 +7,7 @@ using Benchmarks.Configuration;
 using Benchmarks.Data;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
 using Newtonsoft.Json;
 using Newtonsoft.Json.Serialization;
 
@@ -21,19 +22,18 @@ namespace Benchmarks.Middleware
         };
 
         private readonly RequestDelegate _next;
-        private readonly EfDb _db;
 
-        public SingleQueryEfMiddleware(RequestDelegate next, EfDb db)
+        public SingleQueryEfMiddleware(RequestDelegate next)
         {
             _next = next;
-            _db = db;
         }
 
         public async Task Invoke(HttpContext httpContext)
         {
             if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
             {
-                var row = await _db.LoadSingleQueryRow();
+                var db = httpContext.RequestServices.GetService<EfDb>();
+                var row = await db.LoadSingleQueryRow();
                 var result = JsonConvert.SerializeObject(row, _jsonSettings);
 
                 httpContext.Response.StatusCode = StatusCodes.Status200OK;
@@ -56,4 +56,4 @@ namespace Benchmarks.Middleware
             return builder.UseMiddleware<SingleQueryEfMiddleware>();
         }
     }
-}
+}

+ 2 - 2
frameworks/CSharp/aspnetcore/Benchmarks/NuGet.Config

@@ -1,8 +1,8 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <configuration>
   <packageSources>
     <clear />
-    <add key="AspNetVNext" value="https://dotnet.myget.org/F/aspnet1/api/v3/index.json" />
+    <add key="Npgsql" value="https://www.myget.org/F/npgsql-unstable/api/v3/index.json" />
     <add key="NuGet" value="https://api.nuget.org/v3/index.json" />
   </packageSources>
 </configuration>

+ 41 - 8
frameworks/CSharp/aspnetcore/Benchmarks/Startup.cs

@@ -4,6 +4,8 @@
 using System;
 using System.Data.Common;
 using System.Data.SqlClient;
+using System.Text.Encodings.Web;
+using System.Text.Unicode;
 using Benchmarks.Configuration;
 using Benchmarks.Data;
 using Benchmarks.Middleware;
@@ -11,7 +13,9 @@ using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
 using Microsoft.Extensions.PlatformAbstractions;
+using Npgsql;
 
 namespace Benchmarks
 {
@@ -49,11 +53,21 @@ namespace Benchmarks
             services.AddSingleton<ApplicationDbSeeder>();
             services.AddEntityFrameworkSqlServer()
                 .AddDbContext<ApplicationDbContext>();
-
+            
             if (Scenarios.Any("Raw") || Scenarios.Any("Dapper"))
             {
-                // TODO: Add support for plugging in different DbProviderFactory implementations via configuration
-                services.AddSingleton<DbProviderFactory>(SqlClientFactory.Instance);
+                services.AddSingleton<DbProviderFactory>((provider) => {
+                    var settings = provider.GetRequiredService<IOptions<AppSettings>>().Value;
+
+                    if (settings.Database == DatabaseServer.PostgreSql)
+                    {
+                        return NpgsqlFactory.Instance;
+                    }
+                    else
+                    {
+                        return SqlClientFactory.Instance;
+                    }
+                });
             }
 
             if (Scenarios.Any("Ef"))
@@ -73,7 +87,12 @@ namespace Benchmarks
 
             if (Scenarios.Any("Fortunes"))
             {
-                services.AddWebEncoders();
+                var settings = new TextEncoderSettings(UnicodeRanges.BasicLatin, UnicodeRanges.Katakana, UnicodeRanges.Hiragana);
+                settings.AllowCharacter('\u2014');  // allow EM DASH through
+                services.AddWebEncoders((options) =>
+                {
+                    options.TextEncoderSettings = settings;
+                });
             }
 
             if (Scenarios.Any("Mvc"))
@@ -83,7 +102,7 @@ namespace Benchmarks
                     //.AddApplicationPart(typeof(Startup).GetTypeInfo().Assembly)
                     .AddControllersAsServices();
 
-                if (Scenarios.MvcJson)
+                if (Scenarios.MvcJson || Scenarios.Any("MvcDbSingle") || Scenarios.Any("MvcDbMulti"))
                 {
                     mvcBuilder.AddJsonFormatters();
                 }
@@ -99,8 +118,6 @@ namespace Benchmarks
 
         public void Configure(IApplicationBuilder app, ApplicationDbSeeder dbSeeder, ApplicationDbContext dbContext)
         {
-            app.UseErrorHandler();
-
             if (Scenarios.Plaintext)
             {
                 app.UsePlainText();
@@ -143,6 +160,22 @@ namespace Benchmarks
                 app.UseMultipleQueriesEf();
             }
 
+            // Multiple update endpoints
+            if (Scenarios.DbMultiUpdateRaw)
+            {
+                app.UseMultipleUpdatesRaw();
+            }
+
+            if (Scenarios.DbMultiUpdateDapper)
+            {
+                app.UseMultipleUpdatesDapper();
+            }
+
+            if (Scenarios.DbMultiUpdateEf)
+            {
+                app.UseMultipleUpdatesEf();
+            }
+
             // Fortunes endpoints
             if (Scenarios.DbFortunesRaw)
             {
@@ -182,4 +215,4 @@ namespace Benchmarks
             app.RunDebugInfoPage();
         }
     }
-}
+}

+ 0 - 3
frameworks/CSharp/aspnetcore/Benchmarks/appsettings.json

@@ -1,3 +0,0 @@
-{
-  "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnet5-Benchmarks;Trusted_Connection=True;MultipleActiveResultSets=true"
-}

+ 4 - 0
frameworks/CSharp/aspnetcore/Benchmarks/appsettings.postgresql.json

@@ -0,0 +1,4 @@
+{
+  "ConnectionString": "Server={db_server_placeholder};Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=1024;NoResetOnClose=true;PersistPrepared=true",
+  "Database": "postgresql"
+}

+ 21 - 10
frameworks/CSharp/aspnetcore/Benchmarks/project.json

@@ -1,8 +1,15 @@
 {
   "version": "1.0.0-*",
-  "compilationOptions": {
+  "buildOptions": {
     "emitEntryPoint": true,
-    "preserveCompilationContext": true
+    "preserveCompilationContext": true,
+    "copyToOutput": {
+      "include": [
+        "appsettings.json",
+        "wwwroot",
+        "Views"
+      ]
+    }
   },
   "dependencies": {
     "Dapper": "1.50.0-*",
@@ -18,7 +25,9 @@
     "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0-*",
     "Microsoft.Extensions.Configuration.Json": "1.0.0-*",
     "Microsoft.Extensions.Configuration.CommandLine": "1.0.0-*",
-    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-*"
+    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-*",
+    "Npgsql": "3.2.0-*",
+    "Npgsql.EntityFrameworkCore.PostgreSQL": "1.0.2-*" 
   },
   "frameworks": {
     "netcoreapp1.0": {
@@ -28,7 +37,7 @@
           "version": "1.0.0-*",
           "type": "platform"
         },
-        "System.Runtime.Serialization.Primitives": "4.1.0-*"
+        "System.Runtime.Serialization.Primitives": "4.1.1-*"
       }
     },
     "net451": { }
@@ -42,14 +51,16 @@
       ]
     }
   },
-  "content": [
-    "appsettings.json",
-    "wwwroot",
-    "Views"
-  ],
+  "publishOptions": {
+    "include": [
+      "appsettings.json",
+      "wwwroot",
+      "Views"
+    ]
+  },
   "runtimeOptions": {
     "configProperties": {
       "System.GC.Server": true
     }
   }
-}
+}

+ 188 - 13
frameworks/CSharp/aspnetcore/benchmark_config.json

@@ -2,27 +2,112 @@
   "framework": "aspnetcore",
   "tests": [{
     "default": {
-      "setup_file": "setup",
-      "json_url": "/json",
+      "setup_file": "setup-plaintext",
       "plaintext_url": "/plaintext",
       "port": 8080,
       "approach": "Realistic",
-      "classification": "Fullstack",
+      "classification": "Micro",
       "database": "None",
       "framework": "aspnetcore",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Raw",
       "platform": "NET",
-      "webserver": "Kestrel",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "aspnetcore-linux",
       "notes": "",
       "versus": ""
     },
+    "middleware-json": {
+      "setup_file": "setup-json",
+      "json_url": "/json",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "None",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-linux",
+      "notes": "",
+      "versus": ""
+    },
+    "middleware-raw": {
+      "setup_file": "setup-raw",
+      "db_url": "/db/raw",
+      "query_url": "/queries/raw?queries=",
+      "update_url": "/updates/raw?queries=",
+      "fortune_url": "/fortunes/raw",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-middleware-raw",
+      "notes": "",
+      "versus": ""
+    },
+    "middleware-ef": {
+      "setup_file": "setup-ef",
+      "db_url": "/db/ef",
+      "query_url": "/queries/ef?queries=",
+      "update_url": "/updates/ef?queries=",
+      "fortune_url": "/fortunes/ef",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Full",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-middleware-ef",
+      "notes": "",
+      "versus": ""
+    },
+    "middleware-dapper": {
+      "setup_file": "setup-dapper",
+      "db_url": "/db/dapper",
+      "query_url": "/queries/dapper?queries=",
+      "update_url": "/updates/dapper?queries=",
+      "fortune_url": "/fortunes/dapper",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Micro",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-middleware-dapper",
+      "notes": "",
+      "versus": ""
+    },
     "mvc-linux": {
-      "setup_file": "setup-mvc",
-      "json_url": "/mvc/json",
+      "setup_file": "setup-plaintext",
       "plaintext_url": "/mvc/plaintext",
       "port": 8080,
       "approach": "Realistic",
@@ -32,26 +117,113 @@
       "language": "C#",
       "orm": "Raw",
       "platform": "NET",
-      "webserver": "Kestrel",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "aspnetcore-mvc-linux",
       "notes": "",
       "versus": ""
     },
+    "mvc-linux-json": {
+      "setup_file": "setup-json",
+      "json_url": "/mvc/json",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "None",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-mvc-linux",
+      "notes": "",
+      "versus": ""
+    },
+    "mvc-raw": {
+      "setup_file": "setup-raw",
+      "db_url": "/mvc/db/raw",
+      "query_url": "/mvc/queries/raw?queries=",
+      "update_url": "/mvc/updates/raw?queries=",
+      "fortune_url": "/mvc/fortunes/raw",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-mvc-raw",
+      "notes": "",
+      "versus": ""
+    },
+    "mvc-ef": {
+      "setup_file": "setup-ef",
+      "db_url": "/mvc/db/ef",
+      "query_url": "/mvc/queries/ef?queries=",
+      "update_url": "/mvc/updates/ef?queries=",
+      "fortune_url": "/mvc/fortunes/ef",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Full",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-mvc-ef",
+      "notes": "",
+      "versus": ""
+    },
+    "mvc-dapper": {
+      "setup_file": "setup-dapper",
+      "db_url": "/mvc/db/dapper",
+      "query_url": "/mvc/queries/dapper?queries=",
+      "update_url": "/mvc/updates/dapper?queries=",
+      "fortune_url": "/mvc/fortunes/dapper",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Micro",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-mvc-dapper",
+      "notes": "",
+      "versus": ""
+    },
     "win": {
       "setup_file": "setup-windows",
       "json_url": "/json",
       "plaintext_url": "/plaintext",
       "port": 8080,
       "approach": "Realistic",
-      "classification": "Fullstack",
+      "classification": "Micro",
       "database": "None",
       "framework": "aspnetcore",
       "language": "C#",
       "orm": "Raw",
       "platform": "NET",
-      "webserver": "Kestrel",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Windows",
       "database_os": "Linux",
       "display_name": "aspnetcore-win",
@@ -70,7 +242,8 @@
       "language": "C#",
       "orm": "Raw",
       "platform": "NET",
-      "webserver": "Kestrel",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Windows",
       "database_os": "Linux",
       "display_name": "aspnetcore-mvc-win",
@@ -83,13 +256,14 @@
       "plaintext_url": "/plaintext",
       "port": 8080,
       "approach": "Realistic",
-      "classification": "Fullstack",
+      "classification": "Micro",
       "database": "None",
       "framework": "aspnetcore",
       "language": "C#",
       "orm": "Raw",
       "platform": "NET",
-      "webserver": "WebListener",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Windows",
       "database_os": "Linux",
       "display_name": "aspnetcore-weblistener",
@@ -108,7 +282,8 @@
       "language": "C#",
       "orm": "Raw",
       "platform": "NET",
-      "webserver": "WebListener",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Windows",
       "database_os": "Linux",
       "display_name": "aspnetcore-mvc-weblistener",

Деякі файли не було показано, через те що забагато файлів було змінено