Chinaunix首页 | 论坛 | 博客
  • 博客访问: 307486
  • 博文数量: 214
  • 博客积分: 4258
  • 博客等级: 上校
  • 技术积分: 2021
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-02 09:16
个人简介

http://blog.csdn.net/ly21st http://ly21st.blog.chinaunix.net

文章分类

全部博文(214)

文章存档

2018年(16)

2015年(1)

2014年(2)

2012年(22)

2011年(173)

分类: Python/Ruby

2012-09-19 17:03:35

  CLR/C#/Java/Python/IronPython/JavaScript/Lua/Ruby/Squirre   l性能测试

 

今天蛋疼地看到一篇《原生代码与托管代码的一个简单性能对比》,考虑到已经是2年前的文章了,现在的编译器可能会进一步优化,所以自行测试了一遍。


这是2007,该文的作者拿到了最佳优化奖,但此处的代码并非最优化的,只是改进了乘方、自己实现随机数而已。(最优版本可参见蒋黎的代码侯思松的代码,感觉很变态
其中,C++C++ CLR的代码相同,只是采用的编译指令不同而已。
此外,C#代码从二维数组改为一维数组,我稍微测试了一下,C#二维数组和嵌套数组的速度确实很慢。原作者的代码似乎还把下标弄错了,所以也改了下。
PythonCythonJavaScriptLua的代码是我自己照英特尔的原程序改编的,没有对随机数进行优化,附在文末。
Squirrel
的代码由dwing提供,我只是将table改成了数组,RAND_MAX + 1.0改成了32768.0的常数,时间快了约5秒左右。

测试平台:
CPU
Intel Core2 Duo T9400 @ 2.53GHz
内存:
3 GB
操作系统:Windows XP Pro SP2英文版

C++
编译器:gcc version 4.4.1 (TDM-2 mingw32)(以下简称G++)、Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86(以下简称VC++ 2008)、VC++ 2010 Beta2
C++ CLR
编译器:VC++ 2008
VC++ 2010 Beta2
C#
编译器:Microsoft (R) Visual C# 2008 Compiler version 3.5.21022.8
Microsoft (R) Visual C# 2010 Compiler version 4.0.21006.1
Java
Java(TM) SE Runtime Environment (build 1.6.0_13-b03)
Python
Python 2.5.4 (r254:67916, Dec 23 2008, 15:10:54) [MSC v.1310 32 bit (Intel)] on win32Python 2.6.4 (r264:75708, Oct 26 2009, 08:23:19) [MSC v.1500 32 bit (Intel)] on win32
Python 3.1.1 (r311:74483, Aug 17 2009, 17:02:12) [MSC v.1500 32 bit (Intel)] on win32
Cython
0.11.3
Psyco
1.6
IronPython
IronPython 2.6 (2.6.10920.0) on .NET 2.0.50727.1433
JavaScript
ChromePlus 1.2.6.0 Chromium    4.0.222.3 (Official Build 28644)V8 1.3.15

Lua
5.1.4
LuaJIT
1.1.5
2.0.0-beta1
Ruby
ruby 1.9.1p129 (2009-05-12 revision 23412) [i386-mswin32]
Squirrel
Squirrel 3.0 alpha 2 Copyright (C) 2003-2009 Alberto Demichelis (32 bits)
顺带一提,G++是采用版(目前尚无GCC 4.4.14.4.2Windows版),VC++ 2008编译器是Microsoft Visual C++ Toolkit 2008里的,C#编译器是Microsoft .NET Framework 3.54.0 Beta2里的,CythonLuaJIT用的C编译器是GCC 4.4.1LuaJIT 2.0.0还测试了VC++ 2003编译的版本,Ruby没有采用更慢的稳定版。


测试时关掉了大部分占用资源的程序,每种搭配测试5次,取时间最少的一次作为最终结果。
从控制台运行时似乎会影响速度(此时是C++ CLR以超过第23%的优势胜出),所以对于exe文件,我改成了双击运行和控制台各测5次,取最小值。

测试结果:

语言/编译器

编译参数

运行时间()

相对速度

C++ (G++ 4.4.1)

-O3 -s

1.765

85.84%

C++ (G++ 4.4.1)

-O3 -s -mtune=native -march=native

1.859

81.50%

C++ (G++ 4.4.1)

-O3 -s -mfpmath=sse -mtune=native -march=native

1.578

96.01%

C++ (G++ 4.4.1)

-O3 -s -mfpmath=sse -mtune=native -march=native -ffast-math

1.546

97.99%

C++ (VC++ 2008)

/O2 /MD

1.625

93.23%

C++ (VC++ 2008)

/O2 /MD /arch:SSE2

1.593

95.10%

C++ (VC++ 2008)

/O2 /MD /arch:SSE2 /fp:fast

1.562

96.99%

C++ (VC++ 2010 Beta2)

/O2 /MD

1.64

92.38%

C++ (VC++ 2010 Beta2)

/O2 /MD /arch:SSE2

1.562

96.99%

C++ (VC++ 2010 Beta2)

/O2 /MD /arch:SSE2 /fp:fast

1.531

98.95%

C++ CLR (VC++ 2008)

/O2 /MD /clr

1.578

96.01%

C++ CLR (VC++ 2010 Beta2)

/O2 /MD /clr

1.515

100.00%

C# 3.5

无参数

3.281

46.17%

C# 3.5

/o

3.219

47.06%

C# 4.0 Beta2

无参数

2.203

68.77%

C# 4.0 Beta2

/o

2.187

69.27%

Java 1.6

无参数

2.093

72.38%

Python 2.5.4

无参数

117.163

1.29%

Python 2.6.4

无参数

91.137

1.66%

Python 3.1.1

无参数

109.531

1.38%

Cython 0.11.3

-O3

11.795

12.84%

Psyco 1.6 (bind)

无参数

26.206

5.78%

Psyco 1.6 (full)

无参数

25.650

5.91%

IronPython 2.6

无参数

59.630

2.54%

JavaScript (V8 1.3.15)

无参数

16.653

9.10%

Lua 5.1.4

无参数

48.969

3.09%

Lua 5.1.4 (Luac编译)

-s

49.031

3.09%

LuaJIT 1.1.5

无参数

24.141

6.28%

LuaJIT 1.1.5

运行:-O2

9.641

15.71%

LuaJIT 1.1.5 (Luac编译)

编译:-s

24.093

6.29%

LuaJIT 1.1.5 (Luac编译)

编译:-s 运行:-O2

9.641

15.71%

LuaJIT 2.0.0-beta1 (GCC 4.4.1编译)

无参数

1.546

97.99%

LuaJIT 2.0.0-beta1 (GCC 4.4.1编译)

运行:-O2

4.578

33.09%

LuaJIT 2.0.0-beta1 (GCC 4.4.1编译)

无参数

1.625

93.23%

LuaJIT 2.0.0-beta1 (VC++ 2003编译)

运行:-O2

4.578

33.09%

Ruby 1.9.1

无参数

327.797

0.46%

Squirrel 3.0 alpha 2

无参数

73.873

2.05%

Squirrel 3.0 alpha 2 (编译成字节码)

无参数

77.828

1.95%

冠军让C++ CLR夺走了,这点出乎我的意料。测试了多次,均比其他C++实现的时间要少,可见不属于测试误差。看来相对于原生代码,CLR会自动对当前平台进行优化。缺点就是体积稍大了点(29.5KB(VC++ 2010)/24KB(VC++ 2008),而其他的C++实现为7KB左右,VC++ 2008编译的还会生成1KB manifest文件),且需要安装.net framework才能运行。没有测试Intel编译器是一大遗憾,估计它才是性能王者。(由于运行库不兼容,我的电脑上无法测试;在别人的电脑上,Intel C++ 11编译器速度约为VC++2010 CLR2倍,非常震惊。)
G++
在最佳的参数下和VC++基本持平,不过单一的-O3参数并没有什么优势,要组合出那么多种参数也蛮难为人的,而且-ffast-math也是不符合IEEE规范的浮点实现。顺带一提,用该参数编译未优化版本的结果是1.578秒,和优化后几乎没有区别。
VC++ 2008
各种参数的性能都差不多,所以不用过多考虑搭配。
VC++ 2010 Beta2
略微超过了VC++ 2008G++,不过需要附带一个新的动态链接库(msvcr100.dll749 KB)。
让我无语的是C# 3.5居然远远落后于Java(慢了约53.8%),而且是在Windows操作系统上,也未测试可能更快的Java 7IBMDEAJVMGCJC# 4.0的性能提高了不少,只略低于Java 6了。(顺带一提,C# 2.03.5成绩几乎相同,看来微软在4.0上花了大气力来提升性能。)此外,C#生成的代码只有5KB,且无manifest文件。(但别忘了运行库的体积。)
Python则仅快于Ruby,比静态编译的实现慢了约2个数量级,这也在我意料之中。而在性能方面,2.6.4 > 3.1.1 > 2.5.4,而3.x的表现还不错。顺带一提动态语言的多维数组都比较慢,所以改成了一维。
Cython
目前仍无针对数组的优化(详见),而本程序大量涉及对数组的操作,所以性能提升相当有限。此外,将列表设为了1维,速度比2维快了约50%。同时,由于Cython不能对乘方进行优化,因此我改成了乘法。
Python+Psyco
的组合还算不错,只增加了2行,性能就提升了约5倍,凌驾于其他动态语言之上。同上,我将乘方改成了乘法,开方则调用系统函数。
IronPython
的性能让我很惊讶,因为Jython的性能据说很糟糕,没想到在.net平台上会发挥得这么好。
JavaScript
只测试了性能最好的V8引擎。由于输出是调用DOM API,较为影响速度,所以我直接删除了。优化同上,数组采用1维。
在接受dwing的建议,将全局变量和系统函数声明为local后,Lua的速度提升了1倍,表现很不错。不过Luac编译似乎没有帮助,以后不去用了。此外,同样也将table优化成了1维数组。
LuaJIT 1.1.5
表现非常不错,改成local声明后,速度提升了3倍,在-O2开启时居然比Cython更快。速度大约是C++1/6Java1/5,相差已经在一个数量级之内了。
而最近刚出的LuaJIT 2.0.0-beta1更是达到了C++的水平,这对动态语言来说简直是不可思议的。不过大部分测试是稳定在1.625秒,只有一次达到1.546。而VC++ 2003编译出来的是在1.6251.640之间波动,但体积略小。至于兼容性,采用-O0-O2来运行都会有大幅的性能下降(非常诡异),而且现在不支持luac编译的代码。此外,文档里也提到了,LuaJIT 2在数值计算方面接近于静态编译语言,而其他方面(例如字符串)仍略逊于后者,整体性能和JVM 1.6 Client持平。
Ruby
是很久没碰了,于是测试了半天,选了种最快的实现,但仍比Python 2.6.4慢了59%,速度仅有C++1/200。代码里我用while取代了for,因为后者(无论0...n还是0.upto(n))比前者慢了一半。(Python里正好相反,于是人们会倾向于用最优雅的方式得到最好的性能。)此外数组采用了1维,乘方改成了乘法,开方用系统函数。
Squirrel
表现平平,和Lua相比完全出于劣势,而且我也不喜欢它的语法。顺带一提,第一次运行时为73秒,之后就一直是78秒了,编译成字节码也只能到77秒,这也是测出非编译版本更快的原因。

Python
IronPython

from random import random

from time import clock

 

NPARTS = 1000

NPARTS2 = NPARTS * 2

NITER = 201

DIMS = 3

pot = .0

SIZE = NPARTS * DIMS

r = [.0]  * SIZE

 

def computePot():

  global NPARTS, DIMS, r, pot

  for i in xrange(NPARTS):

    for j in xrange(i - 1):

      distx = r[j] - r[i]

      disty = r[NPARTS + j] - r[NPARTS + i]

      distz = r[NPARTS2 + j] - r[NPARTS2 + i]

      dist = (distx * distx + disty * disty + distz * distz) ** .5

      pot += 1.0 / dist

 

def initPositions():

  global NPARTS, DIMS, r

  for i in xrange(SIZE):

    r[i] = 0.5 + random()

 

def updatePositions():

  global NPARTS, DIMS, r

  for i in xrange(SIZE):

    r[i] -= 0.5 + random()

 

def main():

  global pot, NITER

  initPositions()

  updatePositions()

 

  start = clock()

  for i in xrange(NITER):

    pot = .0

    computePot()

    if not i % 10:

      print "%5d: Potential: %10.7f" % (i, pot)

    updatePositions()

  stop = clock()

  print "Seconds = %10.9f" % (stop - start)

 

if __name__ == '__main__':

  main()

Python 3000:将Python的代码中,print改成函数调用形式(加括号),xrange改成range即可。

Cython

from random import random

from time import clock

 

cdef int NPARTS = 1000

cdef int NPARTS2 = NPARTS * 2

cdef int NITER = 201

cdef int DIMS = 3

cdef int SIZE = NPARTS * DIMS

cdef double pot = .0

cdef list r = [.0]  * SIZE

 

cpdef computePot():

  global NPARTS, DIMS, r, pot

  cdef int i, j

  cdef double distx, disty, distz, dist

  for i in xrange(NPARTS):

    for j in xrange(i - 1):

        distx = r[j] - r[i]

        disty = r[NPARTS + j] - r[NPARTS + i]

        distz = r[NPARTS2 + j] - r[NPARTS2 + i]

        dist = (distx * distx + disty * disty + distz * distz) ** .5

        pot += 1.0 / dist

 

cpdef initPositions():

  global NPARTS, DIMS, r

  cdef int i, j

  for i in xrange(SIZE):

    r[i] = 0.5 + random()

 

cpdef updatePositions():

  global NPARTS, DIMS, r

  cdef int i, j

  for i in xrange(SIZE):

    r[i] -= 0.5 + random()

 

def main():

  global pot, NITER

  cdef int i

  cdef double start, stop

  initPositions()

  updatePositions()

 

  start = clock()

  for i in xrange(NITER):

    pot = .0

    computePot()

    if not i % 10:

      print "%5d: Potential: %10.7f" % (i, pot)

    updatePositions()

  stop = clock()

 

  print "Seconds = %10.9f" % (stop - start)

Psyco(使用bind()):

from random import random

from time import clock

from math import sqrt

from psyco import bind

 

 

NPARTS = 1000

NITER = 201

DIMS = 3

pot = .0

r = [[.0]  * NPARTS] * DIMS

 

def computePot():

  global NPARTS, DIMS, r, pot

  for i in xrange(NPARTS):

    for j in xrange(i - 1):

      distx = (r[0][j] - r[0][i])

      distx *= distx

      disty = (r[1][j] - r[1][i])

      disty *= disty

      distz = (r[2][j] - r[2][i])

      distz *= distz

      dist = sqrt(distx + disty + distz)

      pot += 1.0 / dist

 

def initPositions():

  global NPARTS, DIMS, r

  for i in xrange(DIMS):

    for j in xrange(NPARTS):

      r[i][j] = 0.5 + random()

 

def updatePositions():

  global NPARTS, DIMS, r

  for i in xrange(DIMS):

    for j in xrange(NPARTS):

      r[i][j] -= 0.5 + random()

 

def main():

  global pot, NITER

  initPositions()

  updatePositions()

 

  start = clock()

  for i in xrange(NITER):

    pot = .0

    computePot()

    if not i % 10:

      print "%5d: Potential: %10.7f" % (i, pot)

    updatePositions()

  stop = clock()

  print "Seconds = %10.9f" % (stop - start)

 

bind(computePot)

bind(initPositions)

bind(updatePositions)

bind(main)

 

if __name__ == '__main__':

  main()

Psyco(使用full()):

from random import random

from time import clock

from math import sqrt

from psyco import full

 

 

full()

 

NPARTS = 1000

NITER = 201

DIMS = 3

pot = .0

r = [[.0]  * NPARTS] * DIMS

 

def computePot():

  global NPARTS, DIMS, r, pot

  for i in xrange(NPARTS):

    for j in xrange(i - 1):

      distx = (r[0][j] - r[0][i])

      distx *= distx

      disty = (r[1][j] - r[1][i])

      disty *= disty

      distz = (r[2][j] - r[2][i])

      distz *= distz

      dist = sqrt(distx + disty + distz)

      pot += 1.0 / dist

 

def initPositions():

  global NPARTS, DIMS, r

  for i in xrange(DIMS):

    for j in xrange(NPARTS):

      r[i][j] = 0.5 + random()

 

def updatePositions():

  global NPARTS, DIMS, r

  for i in xrange(DIMS):

    for j in xrange(NPARTS):

      r[i][j] -= 0.5 + random()

 

def main():

  global pot, NITER

  initPositions()

  updatePositions()

 

  start = clock()

  for i in xrange(NITER):

    pot = .0

    computePot()

    if not i % 10:

      print "%5d: Potential: %10.7f" % (i, pot)

    updatePositions()

  stop = clock()

  print "Seconds = %10.9f" % (stop - start)

 

if __name__ == '__main__':

  main()

我顺便还写了个numpy的实现:

from numpy import add, square

from numpy.random import rand

from time import clock

 

NPARTS = 1000

NITER = 201

DIMS = 3

r = rand(NPARTS, DIMS)

pot = .0

 

def computePot():

  global NPARTS, DIMS, r, pot

  for i in xrange(NPARTS):

    for j in xrange(i - 1):

        pot += 1.0 / (add.reduce(square(r[j] - r[i])) ** .5)

 

def initPositions():

  global r

  r += 0.5

 

def updatePositions():

  c = clock()

  print c

  global DIMS, NPARTS, r

  r -= (rand(NPARTS, DIMS) + .5)

  print clock() -c

 

def main():

  global pot, NITER

  initPositions()

  updatePositions()

 

  start = clock()

  for i in xrange(NITER):

    pot = .0

    computePot()

    if not i % 10:

      print "%5d: Potential: %10.7f" % (i, pot)

    updatePositions()

  stop = clock()

  print "Seconds = %10.9f" % (stop - start)

 

if __name__ == '__main__':

  main()

然而让我无语的是,numpy版比纯Python版还慢1个数量级。
稍微测试了一下,发现了原因:numpy的函数调用开销很大,因此不适合对小数组进行操作。
简单来说,计算二维数组里2列间差的平方和时,numpy调用了3次函数,而纯Python则完全是内置操作符,所以调用开销较小。而这个二维数组每列只有3个数,所以即便numpy的函数计算很快,但总体来说仍慢于纯Python版。
不过对付大数组则是numpy占据优势了:在数组长度为3时,numpyPython10倍;长度为33时,2者持平;长度为330时,numpy8倍;长度为3300时,numpy30倍。(纯Python版的时间复杂度基本可视为O(N)。)

JavaScript

var NPARTS = 1000;

var NPARTS2 = 2000;

var NITER = 201;

var DIMS = 3;

var SIZE = DIMS * NPARTS;

 

var r = new Array(SIZE);

var pot;

 

function initPositions() {

   for(var i = 0; i < SIZE; i++)

     r[i] = 0.5 + Math.random();

}

 

function updatePositions() {

   for (var i = 0; i < SIZE; i++)

      r[i] -= 0.5 + Math.random();

}

 

function computePot() {

   for (var i = 0; i < NPARTS; i++ ) {

      for (var j = 0, k = i - 1; j < k; j++ ) {

        var distx = r[j] - r[i];

        var disty = r[NPARTS + j] - r[NPARTS + i];

        var distz = r[NPARTS2 + j] - r[NPARTS2 + i];

        var dist = Math.sqrt(distx * distx + disty * disty + distz * distz);

        pot += 1.0 / dist;

      }

   }

}

 

initPositions();

updatePositions();

 

var start = new Date();

for (var i = 0; i < NITER; i++) {

  pot = 0.0;

  computePot();

  updatePositions();

}

var stop = new Date();

 

alert(stop - start);

Lua

local NPARTS = 1000

local NITER = 201

local DIMS = 3

local NPARTS2 = 2000

local SIZE = DIMS * NPARTS

local pot = .0

local r = {}

local sqrt = math.sqrt

local random = math.random

 

 

function computePot()

  for i = 1, NPARTS do

    for j = 1, i - 1 do

      local distx = r[j] - r[i]

      local disty = r[NPARTS + j] - r[NPARTS + i]

      local distz = r[NPARTS2 + j] - r[NPARTS2 + i]

      pot = pot + 1.0 / sqrt(distx * distx + disty * disty + distz * distz)

    end

  end

end

 

function initPositions()

  for i = 1, SIZE do

    r[i] = 0.5 + random()

  end

end

 

function updatePositions()

  for i = 1, SIZE do

    r[i] = r[i] - (0.5 + random())

  end

end

 

initPositions()

updatePositions()

 

local start = os.clock()

for i = 1, NITER do

  pot = .0

  computePot()

  if i % 10 == 0 then

    print(string.format("%5d: Potential: %10.7f", i, pot))

  end

  updatePositions()

end

local stop = os.clock()

print(string.format("Seconds = %10.9f", stop - start))

Ruby

$NPARTS = 1000

$NPARTS2 = 2000

$NITER = 201

$DIMS = 3

$SIZE = $DIMS * $NPARTS

$pot = 0.0

$r = [0.0]  * ($NPARTS * $DIMS)

 

def computePot()

  i = 0

  while i < $NPARTS

    j = 0

    max = i - 1

    while j < max

      distx = $r[j] - $r[i]

      disty = $r[$NPARTS + j] - $r[$NPARTS + i]

      distz = $r[$NPARTS2 + j] - $r[$NPARTS2 + i]

      $pot = $pot + 1.0 / Math.sqrt(distx * distx + disty * disty + distz * distz)

      j += 1

    end

    i += 1

  end

end

 

 

def initPositions()

  i = 0

  while i < $SIZE

    $r[i] = 0.5 + rand()

    i += 1

  end

end

 

def updatePositions()

  i = 0

  while i < $SIZE

    $r[i] -= 0.5 + rand()

    i += 1

  end

end

 

initPositions()

updatePositions()

 

start = Time.now()

i = 0

while i < $NITER

    pot = 0.0

    computePot()

    if i % 10 == 0

        print "%5d Potential %10.7f\n" % [i, $pot]

  end

    updatePositions()

  i += 1

end

stop = Time.now()

print "Seconds = %10.9f\n" % (stop - start)

Squirrel

const NPARTS = 1000

const NITER = 201

const DIMS = 3

const NPARTS2 = 2000

const RANDMAX  = 32168.0

local SIZE = DIMS * NPARTS

local pot = 0.0

local r = []

local sqrt = sqrt

local rand = rand

 

local function computePot() {

  for(local i = 0; i < NPARTS; ++i) {

    for(local j = 0, k = i-1; j < k; ++j) {

      local distx = r[j] - r[i]

      local disty = r[NPARTS  + j] - r[NPARTS  + i]

      local distz = r[NPARTS2 + j] - r[NPARTS2 + i]

      pot += 1.0 / sqrt(distx * distx + disty * disty + distz * distz)

    }

  }

}

 

local function initPositions() {

  for(local i = 0; i < SIZE; ++i) {

    r.append(0.5 + rand() / RANDMAX)

  }

}

 

local function updatePositions() {

  for(local i = 0; i < SIZE; ++i) {

    r[i] -= 0.5 + rand() / RANDMAX

  }

}

 

initPositions()

updatePositions()

 

local start = clock()

for(local i = 0; i < NITER; ++i) {

  pot = 0.0

  computePot()

  if(i % 10 == 0) {

    print(format("%5d: Potential: %10.7f\n", i, pot))

  }

  updatePositions()

}

local stop = clock()

print(format("Seconds = %10.9f\n", stop - start))

你可能还会对下列文章感兴趣:

 

 

阅读(2729) | 评论(0) | 转发(0) |
0

上一篇:lua5.2 改动 .

下一篇:STL之vector的使用

给主人留下些什么吧!~~