【问题标题】:Fast string to integer conversion in PythonPython中的快速字符串到整数转换
【发布时间】:2009-08-20 22:11:33
【问题描述】:

一个简单的问题,真的:您有十亿 (1e+9) 个无符号 32 位整数作为十进制 ASCII 字符串存储在 TSV(制表符分隔值)文件中。与处理同一数据集的其他工具相比,使用 int() 的转换速度非常慢。为什么?更重要的是:如何让它更快?

因此问题是:在 Python 中,将字符串转换为整数的最快方法是什么?

我真正想到的是一些半隐藏的 Python 功能,可以(ab)用于此目的,这与 Guido 在他的 "Optimization Anecdote" 中使用 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 在 cmets 中指出的那样,此版本构建转换后的整数列表,因此给出的速度比相对于 Python 的 4.99 秒运行时 for str in strings: int(str)

>> 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

【问题讨论】:

  • 尝试numpy.fromfile 加载“十亿正整数”(顺便说一句,“十亿”是什么意思(在美国是10**9,在英国可能是10**12)?
  • 十亿左右的好收获;尽管后一种用法在 1970 年代在英国已经过时。
  • 你试过编译代码吗?
  • (1) 请比“在文本文件中存储为 ASCII 字符串”更明确。固定列还是分隔?这是文件中唯一的数据类型吗?显示一些示例行。 (2) 向我们展示您当前正在使用的代码,如果您希望我们相信 int() 是问题并且这不是家庭作业问题 (3) 请以 SI 单位表示速度,而不是“非常慢” ”。 (4) 还有哪些工具? (5)什么平台,什么版本的Python?
  • (6) 整数的平均位数是多少? (7) 数字是十进制/十六进制/八进制/其他吗?

标签: python performance optimization


【解决方案1】:

以下最简单的 C 扩展已经对内置进行了重大改进,每秒可以转换超过三倍的字符串(650kcps 对 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);
}

这显然不适合任意长度的整数和各种其他特殊情况,但在我们的场景中这没有问题。

【讨论】:

  • 有什么理由不使用 C 标准库的函数,例如strtoul()
【解决方案2】:

通过确保在最紧凑的循环中仅使用“局部”变量,您将获得一定比例的速度。 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)))

【讨论】:

    【解决方案3】:

    我可能会建议,就原始速度而言,Python 不是此任务的正确工具。手动编码的 C 实现将轻松击败 Python。

    【讨论】:

    • 我完全同意,但这并不是我问题的重点。我添加了一段我正在寻找的内容。不过,自定义 Python 扩展也是一种选择。
    【解决方案4】:

    同意格雷格; Python 作为一种解释性语言,通常速度很慢。您可以尝试使用 Psyco library 即时编译源代码或使用 C/C++ 等较低级别的语言编写应用程序。

    【讨论】:

    • -1 解释 ==> 慢推论。在这种情况下,C 实现会更快,但您的概括是错误的。
    • 解释语言必须在执行时翻译成机器代码,这比执行编译的目标代码要慢。仍然不明白你的反对意见。请解释为什么你认为“我的概括”是错误的。
    • 解释型语言可以在运行时对字节码进行优化,有时会导致比本机机器码更好的性能。查一下,已经讨论到死了。
    • 好吧,我想 90% 的案例都不足以概括,所以对其进行了编辑。
    • 使用 psyco.full() 尽可能多地移出内部循环并在 1e7 次迭代中运行它需要 27 秒。因此,在我的机器上执行 1e9 大约需要 45 分钟。尽管我没有对它们进行基准测试,但我坚信 C/C++/C# 会更快。
    【解决方案5】:

    正如其他人所说,您可以编写自己的 C 模块来为您进行解析/转换。然后你可以简单地导入它并调用它。您也许可以使用 Pyrex 或其 Cython 衍生工具从 Python 生成 C(通过向 Python 添加一些类型约束提示)。

    您可以阅读有关Cython 的更多信息,看看是否有帮助。

    我想到的另一个问题......你打算用这些十亿整数做什么?您是否可以将它们作为字符串加载,将它们作为字符串搜索并在必要时执行延迟转换?或者您可以使用threadingmultiprocessing 模块和队列并行化转换和其他计算吗? (让一个或多个线程/进程执行转换并提供一个队列,您的处理引擎从中获取它们)。换句话说,生产者/消费者设计会缓解这个问题吗?

    【讨论】:

      【解决方案6】:

      这可能不是您的选择,但我会非常努力地使用二进制文件而不是文本。它经常变化吗?如果没有,您可以对其进行预处理。

      【讨论】:

        【解决方案7】:

        这点 numpy 做得很好:

        np.fromstring(line, dtype=np.float, sep="")

        【讨论】:

          猜你喜欢
          • 2014-06-06
          • 1970-01-01
          • 2013-12-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-02-15
          • 1970-01-01
          • 2022-01-26
          相关资源
          最近更新 更多