PythonTip >> 博文 >> chinaunix

python bytes和bytearray之IO效率测试

zihua 2014-03-12 18:03:58 点击: 1302 | 收藏


前面的一篇文章关注的是Python str的拼接效率,其实bytes的拼接原理和str类似。现在继续关注另外一个bytes比str多出来的特性bytearray,bytearray最突出的特点就是可以被修改,这是str无法办到的,这也意味着bytearray在传输大量数据时,可以免去一些数据copy,提升CPU和RAM利用率,尤其在网络IO时很有用。

测试环境:Mac OS X 10.9,Python 3.3
测试文件:GNU C Library完整HTML文档,约4.9MB

下文运行三个case来对比bytes与bytearray的效率问题:
1. 基准。将完整测试文件读入io.BytesIO,后面的#2和#3将基于io.BytesIO进行,便于观察bytes与bytearray带来的直接影响。
2. bytes读取。通过file.read方式读取io.BytesIO,每次返回的都是一个新bytes对象。
2. bytearray读取。通过file.readinto方式读取io.BytesIO,每次都将内容写入到一个预先分配的bytearray对象。

完整源码

点击(此处)折叠或打开(bytesio.py)

  1. import argparse
  2. from functools import reduce
  3. import io
  4. import os

  5. PAGESIZE = os.sysconf('SC_PAGESIZE')

  6. def run_case1(fp):
  7.     """基准"""
  8.     with fp.getbuffer() as view:
  9.         pass

  10. def run_case2(fp):
  11.     """btyes读取"""
  12.     while True:
  13.         blk = fp.read(PAGESIZE)
  14.         if not blk:
  15.             break

  16. def run_case3(fp):
  17.     """bytearray读取"""
  18.     blk = bytearray(PAGESIZE)
  19.     while True:
  20.         if fp.readinto(blk) <= 0:
  21.             break

  22. def collect_rusage(fp, count, proc):
  23.     for i in range(count):
  24.         fp.seek(0)
  25.         if os.fork() == 0:
  26.             proc(fp)
  27.             os._exit(os.EX_OK)
  28.         pid, status, rusage = os.wait3(os.WEXITED)
  29.         yield rusage.ru_utime, rusage.ru_stime, rusage.ru_maxrss

  30. def main():
  31.     parser = argparse.ArgumentParser(description='Python bytes')
  32.     parser.add_argument('infile', type=argparse.FileType('rb'),
  33.                         help='binary source')
  34.     parser.add_argument('-c', '--count', type=int,
  35.                         help='loop count for each case')
  36.     args = parser.parse_args()
  37.     memfile = io.BytesIO(args.infile.read())

  38.     # main logic begins
  39.     testcases = [run_case1, run_case2, run_case3]
  40.     for case in testcases:
  41.         collector = list(collect_rusage(memfile, args.count, case))
  42.         avg_utime, avg_stime, avg_maxrss = \
  43.             map(lambda x: x/len(collector),
  44.                 reduce(lambda x, y: (m+n for m, n in zip(x, y)), collector))
  45.         print('{} utime: {:.06f}, stime: {:.06f}, maxrss: {}'.format(
  46.                case.__doc__, avg_utime, avg_stime, int(avg_maxrss)))

  47. if __name__ == '__main__':
  48.     main()

执行三次,每次执行中,每个case都会运行100次取均值
  1. bash-3.2 $python bytesio.py The\ GNU\ C\ Library.html -c 100
  2. 基准 utime: 0.000143, stime: 0.000205, maxrss: 761446
  3. btyes读取 utime: 0.001460, stime: 0.000916, maxrss: 5853880
  4. bytearray读取 utime: 0.001223, stime: 0.000922, maxrss: 5878210

  5. bash-3.2 $python bytesio.py The\ GNU\ C\ Library.html -c 100
  6. 基准 utime: 0.000144, stime: 0.000202, maxrss: 767672
  7. btyes读取 utime: 0.001451, stime: 0.000930, maxrss: 5861130
  8. bytearray读取 utime: 0.001216, stime: 0.000913, maxrss: 5886238

  9. bash-3.2 $python bytesio.py The\ GNU\ C\ Library.html -c 100
  10. 基准 utime: 0.000145, stime: 0.000206, maxrss: 756817
  11. btyes读取 utime: 0.001444, stime: 0.000925, maxrss: 5855764
  12. bytearray读取 utime: 0.001204, stime: 0.000901, maxrss: 5883535

最后分析结果,其实还是有与我预期不一致的地方,还好在可理解的范围内:
1. CPU。系统CPU相差无几,但bytes用户CPU超过bytearray约20%,这在实践中是一个不可忽略的差距。
2. RAM。竟然相差无几,莫非新建的bytes对象内存都重用了?只能归功于Python heap manager了。。。
原文链接:http://blog.chinaunix.net/uid-190176-id-4142243.html

作者:zihua | 分类: chinaunix | 标签: python | 阅读: 1302 | 发布于: 2014-03-12 18时 |