Python和Java代码的一个简单比较

当前位置:首页>Python>Python和Java代码的一个简单比较

Python和Java代码的一个简单比较

时间:2015-07-13 10:37来源:网络整理 作者:KKWL 点击:
Python和Java代码的一个简单比较 分类:编程 标签:Java, Python, 性能 我想概述什么的大家都看烦了,所以我就直接以代码来说明了。 这个例子是从一个UTF-8编码的文本文件里读取所有字符

Python和Java代码的一个简单比较

分类:编程 标签:Java, Python, 性能

我想概述什么的大家都看烦了,所以我就直接以代码来说明了。

这个例子是从一个UTF-8编码的文本文件里读取所有字符,转换成Shift-JIS编码,再将每个字节与0xAB异或,最后写入另一个文件。可以算是破解日文游戏经常需要做的事,尚据一定代表性吧~

测试平台:
CPU:Intel Core2 Duo T9400 @ 2.53GHz
内存:3GB
操作系统:Windows XP Pro SP2 英文版
Python SDK:2.5.4
Java SDK:1.6.0_13
测试的文本是从《家族计划》里选的最大的一个,大小为781 KB (800,053 bytes)。(不要和我扯为什么不弄几百兆的来测试,我不是来翻译百科全书的。)

先上Python的代码:from __future__ import with_statement import codecs from cStringIO import StringIO from time import time t = time() with codecs.open(r'B:\test.txt', 'r', 'utf8') as inFile: inFile.seek(3) # skip BOM content = inFile.read() content = content.encode('sjis') outBuf = StringIO() for c in content: outBuf.write(chr(ord(c) ^ 0xAB)) with open(r'B:\test2.txt', 'wb') as outFile: outFile.write(outBuf.getvalue()) print time() - t测试结果:约550毫秒(波动较大)。

接着是Java:import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStreamReader; import java.io.Reader; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; public final class Hello { public static void main(String[] args) throws Exception { long t = System.currentTimeMillis(); Reader inFile = new BufferedReader(new InputStreamReader(new FileInputStream("B:\\test.txt"), "UTF-8")); inFile.skip(1); // skip BOM int size = 1 << 20; // 1M cache CharBuffer inBuf = CharBuffer.allocate(size); inFile.read(inBuf); inFile.close(); inBuf.position(0); ByteBuffer outBuf = Charset.forName("shift-jis").newEncoder().encode(inBuf); byte[] outBytes = outBuf.array(); for (int i = 0, length = outBytes.length; i < length; ++i) { if (outBytes[i] == 0) { size = i; break; } outBytes[i] ^= 0xAB; } FileOutputStream outFile = new FileOutputStream("B:\\test2.txt"); outFile.write(outBytes, 0, size); outFile.close(); System.out.println(System.currentTimeMillis() - t); } }测试结果:约62毫秒。

性能约是Python的9倍,非常值得Java程序员骄傲了。这点也和通常的看法一样,静态语言一般是比动态语言快1个数量级的。

但性能背后却留下了很多问题,我仍不得不提。

首先显而易见的是代码量,Java足足比Python多了一倍。而且初看上去,Python的核心代码不到10行,逻辑一目了然;Java却多了很多稀奇古怪的东西,语句也特别长。

其次是写代码所用时间。我对API算不上熟悉,实际上里面几乎每行代码我都查看了文档或Google了一下,Python顶多用了半小时,Java用了2个多小时。

接着是代码的健壮性。为了减少代码量,在Java代码里,我设置了1M的固定缓冲区,超过的话这个程序就是不能正常工作的;我也没去捕捉IO异常,这可能导致文件不会正常关闭;还有一点下面会说到,我不知道怎么解决。而在Python里,我不需要多余的代码去做这些事(除了引入with语句)。

最后看看API的易用性。
Java读写文件的组合实在太多了,看得我头疼。为了能读取UTF-8,加上文件很小,所以我并未使用nio。当然,这段代码是从网上抄来的。
接着将文件内容读入了CharBuffer中,再编码到一个ByteBuffer里,然后生成一个字节数组,再写入文件。这里又费了我不少时间。
但是运行发现出错了,生成了一堆空白的文件,调试了半天才知道得把输入缓冲的position设为0。
设完后终于出内容了,但大小却不对劲,老是生成1MB的文件。于是接着调试,发现是CharBuffer的容量设为1MB了,于是没从文件中读取到的就都是'\0'了;而它又直接全部传给了ByteBuffer,继而生成了后面全是0的字节数组。我试了很多办法,例如截取一个子序列,但没想到子序列居然共用同一个数组…
看到这个问题没法解决,我就只好在数组上下工夫了。可惜我那种方式读取文件只能知道UTF-8的字符数,和Shift-JIS的字节数是不成正比的,而且数组也不支持切片操作。
没办法只好遍历判断是不是0了,这种行为有个缺点,如果数组中间有0的话,后面就都被截断了,这也就是前面所述的第3点不健壮的地方;但好在文本文件不会这么搞,所以我的程序仍勉强凑合…
说实话写完就想骂人了,这缓冲区也太无语了吧…

当然,虽然累了大半夜,但Java高手肯定写得比我好,我也就懒得再琢磨Java代码了,转而看Python。
性能差的原因一下就能找到,因为就那么几行代码,读写文件是不可能去优化的,就只能优化循环中那句chr(ord(c) ^ 0xAB)了。
我把它改成c,时间立刻就减少到200多毫秒,也就是约有40%的时间用在这上面了。
很显然,将字符串转成数字,再转回字符串,这个操作非常别扭。Python不像C,没有内置的char类型,因此每次都构造一个字符串的开销是很大的,于是就想到了array。
稍微改了4行代码(包括import),优化版就诞生了:from __future__ import with_statement import codecs #from cStringIO import StringIO from array import array from time import time t = time() with codecs.open(r'B:\test.txt', 'r', 'utf8') as inFile: inFile.seek(3) # skip BOM content = inFile.read() content = content.encode('sjis') #outBuf = StringIO() outBuf = array('B') for c in content: #outBuf.write(chr(ord(c) ^ 0xAB)) outBuf.append(ord(c) ^ 0xAB) with open(r'B:\test2.txt', 'wb') as outFile: #outFile.write(outBuf.getvalue()) outBuf.tofile(outFile) print time() - t虽然不敢保证array比cStringIO快,但循环里少了个chr函数,应该不会慢,而测试也证明了这点:约360毫秒,加快了约53%。

当然,ord的调用也很费时,所以可以继续优化,只是这样就比较难看懂了:from __future__ import with_statement import codecs from array import array from struct import unpack from time import clock t = clock() with codecs.open(r'B:\test.txt', 'r', 'utf8') as inFile: inFile.seek(3) # skip BOM content = inFile.read() content = content.encode('sjis') outBuf = array('B') unpackedContent = unpack('%dB' % len(content), content) for byte in unpackedContent: outBuf.append(byte ^ 0xAB) with open(r'B:\test2.txt', 'wb') as outFile: outBuf.tofile(outFile) print clock() - t结果是约310毫秒,比最初版本快了约77%,是Java的1/5。

当然,剩下的就没什么可优化的了,除非把遍历content进行异或写成C扩展。(当然,Psyco或许对第2个版本有一定帮助,毕竟调用了很多次ord函数。)
顺便说下,如果不进行异或的话,Python需要约15毫秒,而Java仍然是62毫秒。也就是说,Python用C编写的模块在IO性能(读写小文件)并进行编码处理上比Java更好,只是没有内置byte类型,而在处理字节方面开销大了不少。
反观Java,初看上去完全不知道该怎么优化,或许多组合几种IO类型,调整一下缓冲区大小,改进一下数组结束的判断都可能会提升,但那需要非常枯燥而又没有技术含量的测试了。

综上,对我而言,Python在编码方面少花了2个小时,这就足够了。哪怕这个文本需要使用这个程序10000次(一般来说不会超过100次),我也少浪费了不少时间,还不必为了无语的缓冲区而吐血。
当然,Python也不是万能的,例如dll就还是得用C/C++写…大家爱挑什么毛病都能挑出来,所以我就此打住了=。=

11条评论

向下滚动可载入更多评论,或者点这里。

查看评论

想说点什么呢?

您需要登录您的Google账号才能进行评论。

最新评论 分类
  • 网站建设 标签

    响应时间:8 毫秒 • 数据库时间:2 毫秒 • 数据库访问次数:9 次 • 页面生成时间:2015-07-13 10:37:16

    ©keakon的涂鸦馆Powered by DoodleDesigned by keakon

    ------分隔线----------------------------