Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit fff4806
Showing
2 changed files
with
203 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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() |