服务报价 | 域名主机 | 网络营销 | 软件工具| [加入收藏]
 热线电话: #
当前位置: 主页 > 开发教程 > postgresql教程 >

python和C++的对象关系映射(ORM)的性能测试

时间:2016-01-20 15:53来源:未知 作者:最模板 点击:
通过对比两个相似的CRUD server apps来说明性能差别,一个是用Python和SQLAlchemy,另一个是用C++和YB.ORM。 简介 在Martin Fowler写的书[Patterns of Enterprise Application Architecture, 2002]中提出的设计模式

通过对比两个相似的CRUD server apps来说明性能差别,一个是用Python和SQLAlchemy,另一个是用C++和YB.ORM。

简介

在Martin Fowler写的书[Patterns of Enterprise Application Architecture, 2002]中提出的设计模式对现代的应用程序与关系数据库的交互操作方式产生了极大的影响。在这一领域里,最有效的方法是对象关系映射 (ORM)。乍一看这个机制是主要用于业务逻辑对象和数据表之间的对应关系的映射。不同的ORM在具体的实现上有些差别,但是所有的ORM工具的原理都是创建一个新的抽象层,这样做的目的是希望简化对象的操作和包括应用程序的逻辑关系在内的关系。使用对象-关系映射很大程度上促进了联机事务处理系统(OLTP)发展出更高水平。选择用ORM架构将会影响整个工程的结构,所以如果想在已有的工程中添加该架构,最大的好处可能就是这个架构的广告效应。

不同的ORM解决方案开销会有什么不同呢?是否像SQLAlchemy的作者Michael Bayer在[SQLAlchemy文章地址.:http://aosabook.org/en/sqlalchemy.html]中所说的那样,这些性能的损失并不重要,而且会随着支持JIT的PyPy技术得到更多人认可而消失掉。这些都很难回答,因为我们需要各种不同ORM解决方案的单元测试结果的进行比较。

这篇文章中,我们将对比两种ORM的解决方案SQLAlchemy for Python 和 YB.ORM for C++ [YB.ORM简介]的开销。为了达到这个目的,我们搭建了连个特殊的测试环境,两个环境都有相同的OLTP逻辑和一样的测试套件来测试他们。

测试台

针对测试服务器应用,我们选择了以下主题:停车场自动化。主要操作包括:发放停车票,计算停车时间,支付服务,离开停车场时停车票回收,这就是所谓的后付费模式。预先付费模式也会被应用到,就是用户先支付一定时间的费用,然后可以在任意时间内支付费用延长停车时间,等用户离开停车场后,剩下没有用掉的时间将会发送到用户账户中,将用于下一次的支付。这些模式都符合CRUD (创建,读取,更新和删除)语义。

这些测试平台的源代码可以再Github上获取到:https://github.com/vnaydionov/teststand-parking

在这个上面你可以找到测试平台和两个应用,用于我们需要测试的两种平台:

Parking文件夹——python语言,使用的是SQLAlchemy(以下简称sa)

Parkingxx文件夹——C++语言,使用YB.ORM(以下简称jborm)

两个软件都是单一的测试套件,通过程序提供的API接口设置测试案例实现测试。这个测试套件是用python的标准测试模块unittest写成的。并且,两个测试程序在测试的时候都会显示“绿灯”。

为了在不同平台上运行同一个测试套件,我们需要进行某种形式的进程通讯(IPC)。最广普遍的进程通讯方法是通过Socket API,这套接口允许同一主机或者不同主机的进程间进行通信。我们这次测试用的API就采用类似TCP socket的方式,使用HTTP协议进行请求包/响应包的解析,并且使用JSON格式作为数据的格式。在python的测试服务器应用程序(sa)中,我们使用SimpleHTTPServer模块做HTTP服务器。在C++的测试服务器应用程序(yborm)中,我们使用HTTpServer类(在项目中的文件 parkingxx/src/micro_http.h, parkingxx/src/micro_http.cpp),运行examples/auth文件夹下的YB.ORM工程。为了让测试更加完美,我们把所有测试和应用程序都放在一个多核CPU的计算机上运行。

在实际的测试环境中,几乎每时每刻都会有请求需要程序来处理。总所周知,在CPython中,在多线程处理的时候有一个瓶颈,这是又python的解释器引起的。因此,我们用多进程来代替多线程来运行python的服务器程序。另一方面,在C++中没有这样的限制,因此测试的结果不会受线程/进程池的影响,只需要连续的向服务器发送请求就行了。

我们使用MySQL数据库服务器的处理引擎,两个服务器程序运行在相同的数据库实例中。

通过以上的配置,两种不同的程序运行就会产生相同的日志。所有的日志信息包括WEB服务器和数据库记录的。此外,我们自己写的测试程序没有产生日志。

这两个实现基本上是以同一种方式工作。为了更加简化的对比两个程序运行情况,程序代码的逻辑结构也被规范成相似的结构。本文中,我们不讨论ORM工具的执行效率,尽管它很重要。我们只是对比一些运行数据,代码的大小也被限制在了相似的大小:20.4KB(sa)对比22.5KB(yborm)的逻辑代码大小。以及3.4KB(sa)比5.7KB(yborm)的数据模块描述。以下是一段业务逻辑代码,用SQLAlchemy 和 Python写的:

1
2
3
4
5
6
7
8
9
10
11
def check_plate_number(session, version=None,
registration_plate=None):
plate_number = str(registration_plate or '')
assert plate_number
active_orders_count = session.query(Order).filter(
(Order.plate_number == str(plate_number)) &
(Order.paid_until_ts > datetime.datetime.now()) &
(Order.finish_ts == None)).count()
if active_orders_count >= 1:
raise ApiResult(mk_resp('success', paid='true'))
raise ApiResult(mk_resp('success', paid='false'))

相似的功能在C++和YB.ORM上的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ElementTree::ElementPtr check_plate_number(Session &session,
ILogger &logger,
const StringDict ¶ms)
{
string plate_number = params["registration_plate"];
YB_ASSERT(!plate_number.empty());
LongInt active_orders_count = query(session).filter_by(
(Order::c.plate_number == plate_number) &&
(Order::c.paid_until_ts > now()) &&
(Order::c.finish_ts == Value())).count();
ElementTree::ElementPtr res = mk_resp("success");
res->add_json_string("paid", active_orders_count >= 1?
"true": "false");
throw ApiResult(res);
}

硬件配置和软件包

我们用Ubuntu做测试系统,因为Ubuntu广泛的被用于部署服务器端应用程序。测试在带桌面的电脑上,具体硬件配置如下:Intel(R) Core(TM) i5 760 @2.80GHz, 4GB RAM, running 64-bit Ubuntu 12.04。

软件方面,除了PyPy,PyMySQL, SOCI 和 YB.ORM以外,其他所有版本的软件包都是从Ubuntu的repos中下载并安装的。

系统内核版本:Linux 3.8.0-39-generic #58~precise1-Ubuntu SMP x86_64

数据库服务器:MySQL 5.5.37-0ubuntu0.12.04.1

MySQL客户端的C接口:libmysqlclient18 5.5.37-0ubuntu0.12.04.1

Python解释器:CPython 2.7.3

MySQL客户端的CPython接口V2:MySQLdb 1.2.3-1ubuntu0.1

基于JIT的Python解释器:PyPy 2.3.1 (http://pypy.org/)

MySQL的PyPy接口:PyMySQL 0.6.2 (https://github.com/PyMySQL/PyMySQL)

C++编译器:GCC version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)

ODBC驱动管理:UnixODBC 2.2.14p2-5ubuntu3

MySQL的ODBC驱动:MyODBC 5.1.10-1

C++的数据库连接库:SOCI 3.2.0 (http://soci.sourceforge.net/)

C++的ORM框架:YB.ORM 0.4.5 (https://github.com/vnaydionov/yb-orm)

Python的ORM框架:SQLAlchemy 0.7.4-1ubuntu0.1 (http://www.sqlalchemy.org/)

测试组件的结构

为了知道在测试过程中具体产生了那些函数调用,如表1. 那些进行修改数据库的函数都被列出来了。他们的系统占用时间是通过配置YB.ORM 和 SOCI的后端产生的系统日志计算出来的。

接下来我们来看下测试套件在生成SQLstatements对象(SQLStatement 对象用于针对通过 SQLConnection 对象打开的本地 SQL 数据库执行 SQL 语句。)的时候的结构(如表2.)。它可以帮助我们对比不同平台上运行的软件有什么差别。对于我们选用的不同软件(SQLAlchemy 和 YB.ORM)不同的SQL调用会有一些不同,因为数据库的设计并不要求精确的匹配。但是,测试套件在不同的应用程序中都是以相同的方式工作的。

当然,实际的环境中,像测试套件这样的调用差别我们是感觉不到的。尽管如此,这个测试套件还是要让我们了解到不同的ORM的性能等级。

测试方法

我们的测试套件是以每20迭代长度为一个循环分批次地连续运行。对于每个服务器的配置的定时,测定5次,以使误差最小。它总共会根据配置运行100次。

时间是由Unix的时间命令产生,通过一个进程运行该命令输出时间。当然这个运行时间的命令本身也会占用时间,这会大大影响到测试的结果,因此,两个时间的总和才是真实的。

在客户端方面,只有正确的时间会被传给用户,输出的时间和输出的错误的时间都会被重新传入目录/dev/null中。

以下是一个测试组的命令样例

1
2
3
4
5
$ time ( for x in `yes | head -n 20`; do python parking_http_tests.py -apu 
http://localhost:8111/ &> /dev/null ; done )
real 0m24.762s
user 0m3.312s
sys 0m1.192s

对正在运行的服务器进程的用户,系统定时是具有实际的利益,因为他们与系统的CPU资源的物理消耗相关。实际上,定时花费的时间包括服务器等待请求接入的空闲时间或者其他I/O设备。当然,为了在测试运行的过程中观察实际的时间,其中一个必须要关闭服务,保证他正常的工作在一个无限循环里。

测试结果

我们做了一下四种配置的测试:

1. PyPy + SQLAlchemy

2. CPython + SQLAlchemy

3. YB.ORM + SOCI

4. YB.ORM + ODBC

每一种配置都有两种情况的分析:开启日志(logging on)的情况和关闭日志(logging off)的情况,总共有8个组合。对各组进行5次的测试后取平均值,总共连续测试20次后,将结果统计成如下图,图1所示:

图1 测试套件实际运行时间,用时越少越好

从部署和维护角度来说,获取服务器应用软件占用了多少CPU时间是很重要的。占用的时间越多,就意味着服务器连接相同数量的请求时将占用更多的CPU内核,并且产生更高的功耗和散热量。CPU消耗时间是一个进程在用户模式下占用的时间加上内核模式下耗费的时间,本例中的时间耗费如图2所示:

图2 服务端的CPU消耗,越少的越好

关闭日志

在提高搜索性能方面,经常被考虑到的选项是关闭调试功能。测试使用了ORM的软件的是要用日志记录下来,这是很常见的,这也意味着需要有一种方法来控制。但是在实际的环境中,这就会产生干扰。我们要将日志的干扰降到最小化或者直接关掉,尤其是当一个应用程序功能已经完善。

但是,日志文件究竟有多大?在这个测试程序中,每个从HTTP服务器发送过来的请求都会被记录下来,而且每个执行后的SQL statement的输入和输出数据都会被记录下来。正如上面所写的,每个服务器设置的测试套件都运行了100个时间,日志文件的统计如下表所示,表3:

测量结果显示了关闭日志功能后定时功能被提升的百分比(如表4所示)。其中,第一行将的是服务器的响应时间,第二行是CPU的消耗:

总结

通过以上测试我们可以得出以下结论:

1. 通过以上四种测试,最快的一种是C++ (yborm odbc)。它的效率几乎是Python (cpython sa)的三倍。

2. 服务器响应时间,例如,这段时间后用户就会看到响应,如果在YB.ORM被使用情况下也可能会缩短些,在这个例子中,这一点已经被提升了38 %

3. PyPy在多种架构上运行SQLAlchemy并没有达到官方承诺的那样。

4. 正如结果报告所示,使用YB.ORM和 SOCI比使用ODBC返回的结果更差。

5. 在服务端关闭日志功能并没有太大的影响,因为他可以被预期。特别是,服务端的提升情况是CPython+SQLAlchemy: 26 %, YB.ORM 提升了 20 %

很长一段时间里,python被认为是很高效的语言。其中一个理由是编码的周期更短。作为SQLAlchermy这样的工具,他使得这一点更加的吸引人。同时,一些性能可能会受到影响。有些情况下,用python做原型设计可能会更好,最后提到C++,现有的框架和工具与他都有的一比。

历史

(责任编辑:最模板)
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
栏目列表
热点内容