Browse Source

Merge branch 'master' of https://github.com/davidmoreno/FrameworkBenchmarks into davidmoreno-master

Patrick Falls 12 years ago
parent
commit
036fcf47f8
6 changed files with 287 additions and 0 deletions
  1. 21 0
      onion/Makefile
  2. 7 0
      onion/README
  3. 0 0
      onion/__init__.py
  4. 13 0
      onion/benchmark_config
  5. 228 0
      onion/hello.c
  6. 18 0
      onion/setup.py

+ 21 - 0
onion/Makefile

@@ -0,0 +1,21 @@
+all: hello
+
+CFLAGS=-O2 -Ionion/src/
+LDFLAGS=-lonion -ljson -lmysqlclient -lpthread -L/usr/lib64/mysql/ -L.
+
+hello.o: onion hello.c 
+
+hello: hello.o libonion_static.a
+	cc hello.o libonion_static.a -o hello -lpthread -lmysqlclient -ljson -lgnutls -lrt
+
+clean:
+	rm -f *.o hello
+
+	
+libonion_static.a: onion
+	(cd onion && mkdir -p build && cd build && cmake .. && cd src && make -j4)
+	cp onion/build/src/onion/libonion_static.a .
+
+onion:
+	git clone https://github.com/davidmoreno/onion.git
+

+ 7 - 0
onion/README

@@ -0,0 +1,7 @@
+Makefile works for ubuntu. Other distros may need tweak. These extra packages are needed:
+
+libgnutls-dev
+libmysqlclient-dev
+libjson0-dev
+
+Any question or comment to [email protected].

+ 0 - 0
onion/__init__.py


+ 13 - 0
onion/benchmark_config

@@ -0,0 +1,13 @@
+{
+  "framework": "onion-raw",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/",
+      "db_url": "/db",
+      "query_url": "/db?queries=",
+      "port": 8080,
+      "sort": 37
+    }
+  }]
+}

+ 228 - 0
onion/hello.c

@@ -0,0 +1,228 @@
+// Compile it with: 
+//   $ gcc -o bench bench.c -lonion -Wall -O2 -lsqlite3
+//   $ export ONION_LOG=noinfo  # To properly test, you may need some control over logging. Here almost disabled. 
+//   $ ./bench
+// It listens to localhost:8080, known addresses: http://localhost:8080/ , http://localhost:8080/db , http://localhost:8080/db20
+// Test it with ab:
+//   $ ab -k -t 10 -c 20 http://localhost:8080/
+// It gave me (Intel(R) Core(TM) i7-2677M CPU @ 1.80GHz):
+//   Requests per second:    58288.10 [#/sec] (mean)
+
+// Done in response of http://www.techempower.com/blog/2013/03/28/framework-benchmarks/
+// Although onion is not a framework.
+
+// Copyright (c) 2013, David Moreno
+// Under BSD license
+
+// All rights reserved. 
+// 
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met: 
+// 
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer. 
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution. 
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// 
+// The views and conclusions contained in the software and documentation are those
+// of the authors and should not be interpreted as representing official policies, 
+// either expressed or implied, of the FreeBSD Project.
+
+
+
+#include <onion/onion.h>
+#include <onion/handler.h>
+#include <onion/dict.h>
+#include <onion/block.h>
+#include <onion/log.h>
+#include <string.h>
+#include <mysql/mysql.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <json/json.h>
+#include <semaphore.h>
+#include <pthread.h>
+#include <signal.h>
+
+/// Gets the dict and converts it to JSON and writes it into the response. 
+onion_connection_status return_json(onion_dict *json, onion_request *req, onion_response *res){
+	onion_block *bl=onion_dict_to_json(json);
+	size_t size=onion_block_size(bl);
+	onion_response_set_header(res, "Content-Type","application/json");
+	onion_response_set_length(res, size);
+	onion_response_write(res, onion_block_data(bl), size);
+	onion_block_free(bl);
+	return OCS_PROCESSED;
+}
+
+/// Gets the dict and converts it to JSON and writes it into the response. 
+onion_connection_status return_json_libjson(void *_, onion_request *req, onion_response *res){
+	json_object *hello=json_object_new_object();
+	json_object_object_add(hello, "message", json_object_new_string("Hello, world"));
+	
+	const char *hello_str=json_object_to_json_string(hello);
+	int size=strlen(hello_str);
+
+	onion_response_set_header(res, "Content-Type","application/json");
+	onion_response_set_length(res, size);
+	onion_response_write(res, hello_str, size);
+	json_object_put(hello);
+	return OCS_PROCESSED;
+}
+
+/// Do one sqlite petition
+onion_connection_status return_db(MYSQL *db, onion_request *req, onion_response *res){
+	char query[256];
+	char *error;
+	const char *nqueries_str=onion_request_get_query(req,"queries");
+	int queries=(nqueries_str) ? atoi(nqueries_str) : 1;
+	
+	
+
+	json_object *json=json_object_new_object();
+	json_object *array=json_object_new_array();
+	int i;
+	for (i=0;i<queries;i++){
+		json_object *obj=json_object_new_object();
+		
+		snprintf(query,sizeof(query), "SELECT * FROM World WHERE id = %d", 1 + (rand()%10000));
+		mysql_query(db, query);
+		MYSQL_RES *sqlres = mysql_store_result(db);
+		MYSQL_ROW row = mysql_fetch_row(sqlres);
+		
+		json_object_object_add(obj, "randomNumber", json_object_new_int( atoi(row[1]) ));
+		json_object_array_add(array, obj);
+		mysql_free_result(sqlres);
+	}
+	json_object_object_add(json,"json",array);
+	const char *str=json_object_to_json_string(json);
+	int size=strlen(str);
+	onion_response_set_header(res,"Content-Type","application/json");
+	onion_response_set_length(res, size);
+	onion_response_write(res, str, size);
+	
+	json_object_put(json);
+	return OCS_PROCESSED;
+}
+
+#define NCONN 10
+// Some data needed by the handler
+struct test_data{
+	onion_dict *hello;
+	MYSQL *db[NCONN];
+	int free_db[NCONN];
+	pthread_mutex_t mutex;
+	sem_t sem;
+};
+
+MYSQL *get_connection(struct test_data *data){
+	while( 1 ){
+		sem_wait(&data->sem);
+		pthread_mutex_lock(&data->mutex);
+		int i;
+		for (i=0;i<NCONN;i++){
+			if (data->free_db[i]){
+				data->free_db[i]=0;
+				pthread_mutex_unlock(&data->mutex);
+				return data->db[i];
+			}
+		}
+		
+		pthread_mutex_unlock(&data->mutex); // I think it should never get here, but just in case
+		sem_post(&data->sem);
+	}
+}
+
+void free_connection(struct test_data *data, MYSQL *db){
+	int i;
+	for (i=0;i<NCONN;i++){
+		if (data->db[i]==db){
+			pthread_mutex_lock(&data->mutex);
+			data->free_db[i]=1;
+			pthread_mutex_unlock(&data->mutex);
+			sem_post(&data->sem);
+		}
+	}
+}
+
+/// Multiplexes to the proper handler depending on the path.
+/// As there is no proper database connection pool, take one connection randomly, and uses it.
+onion_connection_status muxer(struct test_data *data, onion_request *req, onion_response *res){
+	const char *path=onion_request_get_path(req);
+	if (strcmp(path, "")==0)
+		return return_json(data->hello, req, res);
+	if (strcmp(path, "json")==0)
+		return return_json_libjson(NULL, req, res);
+	
+	if (strcmp(path, "db")==0){
+		MYSQL *db=get_connection(data);
+		int ret=return_db(db, req, res);
+		free_connection(data, db);
+		return ret;
+	}
+	
+	return OCS_INTERNAL_ERROR;
+}
+
+onion *o=NULL;
+
+static void shutdown_server(int _){
+	if (o) 
+		onion_listen_stop(o);
+}
+
+
+/// Creates the onion http server, creates some server data, creates the handler, listens.
+int main(void){
+	signal(SIGINT,shutdown_server);
+	signal(SIGTERM,shutdown_server);
+
+	o=onion_new(O_POOL);
+	
+	struct test_data data;
+	data.hello=onion_dict_new();
+	
+	int i;
+	for (i=0;i<NCONN;i++){
+		data.db[i]=mysql_init(NULL);
+		data.free_db[i]=1;
+		if (data.db[i]==NULL){
+			ONION_ERROR("Cant create db connection: %s", mysql_error(data.db[i]));
+			return 1;
+		}
+		if (mysql_real_connect(data.db[i], "localhost", "benchmarkdbuser", 
+		                       "benchmarkdbpass", "hello_world", 0, NULL, 0) == NULL) {
+			ONION_ERROR("Error %u: %s\n", mysql_errno(data.db[i]), mysql_error(data.db[i]));
+			return 1;
+		}
+	}
+	pthread_mutex_init(&data.mutex,NULL);
+	sem_init(&data.sem,0, NCONN);
+	
+	onion_dict_add(data.hello,"message","Hello, world", 0);
+
+	onion_set_root_handler(o, onion_handler_new((void*)&muxer, (void*)&data, NULL));
+
+	printf("Listening at http://localhost:8080/\n");
+	onion_listen(o);
+	
+	onion_dict_free(data.hello);
+	for (i=0;i<NCONN;i++){
+		mysql_close(data.db[i]);
+	}
+	onion_free(o);
+	return 0;
+}

+ 18 - 0
onion/setup.py

@@ -0,0 +1,18 @@
+import subprocess
+import sys
+import os
+
+def start(args):
+	os.putenv("ONION_LOG","noinfo")
+  os.system("make && ./hello &")
+  return 0
+
+def stop():
+  
+  p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
+  out, err = p.communicate()
+  for line in out.splitlines():
+    if 'hello' in line:
+      pid = int(line.split(None, 2)[1])
+      os.kill(pid, 9)
+  return 0