Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
ovidiucp committed Jul 16, 2011
0 parents commit fff4806
Show file tree
Hide file tree
Showing 2 changed files with 203 additions and 0 deletions.
63 changes: 63 additions & 0 deletions README
@@ -0,0 +1,63 @@
This is a simple benchmark for various asynchronous Python MySQL
client libraries.

The client libraries tested here are:

- Twisted's adbapi which uses the MySQL client library written in C.
- the pure Python txMySQL asynchronous client library

Both of these libraries assume they're running on the Twisted
framework. The benchmarks allow you to run different reactor
implementations:

- Twisted's default reactor
- the Tornado-based reactor for Twisted

The command line options for the benchmark are the following:

--mysql_db MySQL database to use
--mysql_host Database host
--mysql_passwd MySQL password
--mysql_user MySQL user to use
--pool_size Database connection pool size
--use_adbapi Use twisted's adbapi module
--use_tornado Use tornado twisted reactor instead of twisted's reactor
--use_txmysql Use txMySQL database module

One thing to note here is that the adbapi library will try to connect
to the mysql server using a Unix socket, while the txMySQL always uses
a TCP/IP socket. To make things even between the two implementations,
try to run your database server on a remote machine.

Below are some benchmark results. The benchmark program runs on my
Hackintosh Core i7-2600K 3.4GHz (4 cores 8 threads) running MacOS X
10.6.7. The database server runs on an Intel Xeon X3360 2.83GHz (4
cores 4 threads) running Ubuntu Linux 9.04.

The pool size specified was --pool_size=5

Here are the times in seconds reported by the program:

Tornado Twisted
adbapi 18.34 19.07
txMySQL 45.19 43.80


Network usage
adbapi 1.8MB/sec
txMySQL 0.42MB/sec


With the adbapi benchmark the CPU on the client machine is at 140%
with 6 threads.

With the txMySQL benchmark the CPU on the client machine is maxed out
at 100% with a single thread.

Conclusion
==========

With a connection pool of 5 connections txMySQL is 2.5 times slower
than adbapi. With all the I/O happening in the main thread, txMySQL
has a hard time competing against the multi-threaded and blocking
adbapi implementation.
140 changes: 140 additions & 0 deletions mysql_benchmark.py
@@ -0,0 +1,140 @@
# Author: Ovidiu Predescu
# Date: July 2011
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
A simple benchmark for various asynchronous Python MySQL client libraries.
"""
import time

import tornado.options
from tornado.options import define, options

define("mysql_host", default="127.0.0.1", help="Database host")
define("mysql_user", default="", help="MySQL user to use")
define("mysql_passwd", default="", help="MySQL password")
define("mysql_db", default="test", help="MySQL database to use")

define("use_tornado", default=False,
help="Use tornado twisted reactor instead of twisted's reactor")

define("use_txmysql", default=False, help="Use txMySQL database module")
define("use_adbapi", default=False, help="Use twisted's adbapi module")

define("pool_size", default=10, help="Database connection pool size")

class DbBenchmark:
def __init__(self):
self._pool = None

def run(self):
d = self._createTable()
d.addCallback(self._startBenchmark)
d.addErrback(self._dbError)

def _dbError(self, error):
print "Error accessing the database: %s" % error

def _createTable(self):
print 'creating benchmark table'
return self._pool.runOperation(
"""drop table if exists benchmark;
create table benchmark(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
data VARCHAR(100)
)
""")

def _startBenchmark(self, ignored):
print "Starting benchmark %s" % self.__class__
self._start_time = time.time()
self._totalNumInserts = 100000
self._numDone = 0
for i in xrange(self._totalNumInserts):
d = self._doInsert(i)
d.addCallback(self._insertDone)
d.addErrback(self._dbError)

def _doInsert(self, i):
raise NotImplementedError

def _insertDone(self, ignored):
self._numDone += 1
if self._numDone == self._totalNumInserts:
d = self._pool.runQuery("select count(*) from benchmark")
d.addCallback(self._allInsertsDone)
d.addErrback(self._dbError)

def _allInsertsDone(self, results):
self._end_time = time.time()
row = results[0]
num = row[0]
print "inserted %d records, time taken = %f seconds" %\
(num, (self._end_time - self._start_time))
reactor.stop()

class TwistedDbAPI(DbBenchmark):
def __init__(self):
from twisted.enterprise import adbapi
print "Creating twisted adbapi ConnectionPool"
self._pool = adbapi.ConnectionPool(dbapiName="MySQLdb",
host=options.mysql_host,
port=3306,
unix_socket='',
user=options.mysql_user,
passwd=options.mysql_passwd,
db=options.mysql_db,
cp_min=options.pool_size,
cp_max=options.pool_size)
self._numRuns = 0

def _doInsert(self, i):
return self._pool.runOperation(
"insert benchmark(data) values (%d)" % i)


class TxMySQL(DbBenchmark):
def __init__(self):
from txmysql import client
print "Creating txMySQL ConnectionPool"
self._pool = client.ConnectionPool(hostname=options.mysql_host,
username=options.mysql_user,
password=options.mysql_passwd,
database=options.mysql_db,
num_connections=options.pool_size)

def _doInsert(self, i):
return self._pool.runOperation(
"insert benchmark(data) values (%d)" % i)


if __name__ == "__main__":
tornado.options.parse_command_line()
if options.use_tornado:
import tornado.platform.twistedreactor
tornado.platform.twistedreactor.install()
from twisted.internet import reactor
print "Using reactor %s" % reactor

reactor.suggestThreadPoolSize(options.pool_size)

if options.use_adbapi:
benchmark = TwistedDbAPI()
benchmark.run()
elif options.use_txmysql:
benchmark = TxMySQL()
benchmark.run()
else:
print 'At least one of --use_adbapi or --use_txmysql should be '\
'specified'
reactor.run()

0 comments on commit fff4806

Please sign in to comment.