[英]Fast string to integer conversion in Python
一个简单的问题,实际上:在TSV(制表符分隔值)文件中,您有十亿(1e + 9)个无符号32位整数存储为十进制ASCII字符串。 与在同一数据集上工作的其他工具相比,使用int()
转换的速度非常慢。 为什么? 更重要的是:如何让它更快?
因此,问题是:在Python中,将字符串转换为整数的最快方法是什么?
我真正想到的是一些半隐藏的Python功能,可以(ab)用于此目的,这与Guido在他的“优化轶事”中使用array.array
不同。
示例数据 (标签扩展到空格)
38262904 "pfv" 2002-11-15T00:37:20+00:00
12311231 "tnealzref" 2008-01-21T20:46:51+00:00
26783384 "hayb" 2004-02-14T20:43:45+00:00
812874 "qevzasdfvnp" 2005-01-11T00:29:46+00:00
22312733 "bdumtddyasb" 2009-01-17T20:41:04+00:00
读取数据所花费的时间与此无关,处理数据是瓶颈。
微基准测试
以下所有都是解释语言。 主机正在运行64位Linux。
Python 2.6.2与IPython 0.9.1,每秒约214k转换(100%):
In [1]: strings = map(str, range(int(1e7)))
In [2]: %timeit map(int, strings);
10 loops, best of 3: 4.68 s per loop
REBOL 3.0版本2.100.76.4.2,~231kcps(108%):
>> strings: array n: to-integer 1e7 repeat i n [poke strings i mold (i - 1)]
== "9999999"
>> delta-time [map str strings [to integer! str]]
== 0:00:04.328675
REBOL 2.7.6.4.2(2008年3月15日),约523kcps(261%):
正如John在评论中指出的那样,这个版本没有构建转换整数的列表,所以给出的速度比是相for str in strings: int(str)
Python的4.99s运行时。
>> delta-time: func [c /local t] [t: now/time/precise do c now/time/precise - t]
>> strings: array n: to-integer 1e7 repeat i n [poke strings i mold (i - 1)]
== "9999999"
>> delta-time [foreach str strings [to integer! str]]
== 0:00:01.913193
KDB + 2.6t 2009.04.15,~2016kcps(944%):
q)strings:string til "i"$1e7
q)\t "I"$strings
496
我可能会建议,对于原始速度,Python不适合执行此任务。 手动编码的C实现将轻松击败Python。
通过确保在最紧密的循环中仅使用“本地”变量,您将获得一定比例的速度。 int
函数是全局的,因此查找它将比本地更昂贵。
你真的需要内存中的所有十亿个数字吗? 考虑使用一些迭代器一次只给你几个值。十亿个数字会占用一些存储空间。 将这些附加到列表中,一次一个,将需要几次大的重新分配。
如果可能的话,让你完全循环使用Python。 这里的地图功能可以是你的朋友。 我不确定您的数据是如何存储的。 如果每行只有一个数字,则可以将代码减少到
values = map(int, open("numberfile.txt"))
如果每行有多个值是空格分隔的,请深入研究itertools以保持循环代码不受Python影响。 此版本具有创建数字迭代器的额外好处,因此您可以一次只从文件中移出一个或多个数字,而不是一次性移动十亿个数字。
numfile = open("numberfile.txt")
valIter = itertools.imap(int, itertools.chain(itertools.imap(str.split, numfile)))
以下最简单的C扩展已经在内置程序上进行了大量改进,每秒转换的字符串数量增加了三倍(650kcps vs 214kcps):
static PyObject *fastint_int(PyObject *self, PyObject *args) {
char *s; unsigned r = 0;
if (!PyArg_ParseTuple(args, "s", &s)) return NULL;
for (r = 0; *s; r = r * 10 + *s++ - '0');
return Py_BuildValue("i", r);
}
这显然不适合任意长度的整数和各种其他特殊情况,但这在我们的场景中没有问题。
同意格雷格; Python作为一种解释语言通常很慢。 您可以尝试使用Psyco库动态编译源代码,或者使用较低级别的语言(如C / C ++)编写应用程序。
正如其他人所说,您可以编写自己的C模块来为您进行解析/转换。 然后你可以简单地导入它并调用它。 您可以使用Pyrex或其Cython衍生物从Python生成C(通过向Python添加一些类型约束提示)。
您可以阅读有关Cython的更多信息,看看是否有帮助。
我想到的另一个问题是......你将用这十亿个整数做什么? 是否有可能将它们作为字符串加载,将它们作为字符串搜索并根据需要执行延迟转换? 或者您可以使用threading
或多multiprocessing
模块和队列来并行化转换和其他计算吗? (让一个或多个线程/进程执行转换并提供处理引擎从中获取它们的队列)。 换句话说,生产者/消费者设计会缓解这个问题吗?
它可能不是你的选择,但我会很难看到使用二进制文件而不是文本。 它经常变化吗? 如果没有,您可以预先处理它。
这个numpy非常好的东西:
np.fromstring(line,dtype = np.float,sep =“”)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.