【问题标题】:how fast is python's slicepython的切片有多快
【发布时间】:2009-08-18 15:12:46
【问题描述】:

为了节省空间和保持不同来源之间数据一致性的复杂性,我正在考虑存储一些子字符串的开始/结束索引,而不是存储子字符串本身。诀窍是,如果我这样做,我可能会一直创建切片。这是要避免的事情吗?切片运算符是否足够快,我不需要担心?新的对象创建/销毁开销如何?


好的,我吸取了教训。除非您正在尝试解决真正的问题,否则不要进行优化。 (当然,这并不意味着纠正不必要的错误代码,但这无关紧要......)此外,在堆栈溢出之前进行测试和分析。 =D 谢谢大家!

【问题讨论】:

  • 为什么不试试看呢?写一个简单的测试。
  • 我同意并投票支持 balpha,但我一直想知道 Python 切片的速度有多快。我一直使用它就像一个简单的分配一样容易,但我敢肯定它比这要慢得多。
  • -1:没有做任何计时实验。
  • -1:过早的优化。 “足够快”?够快做什么?
  • tehgeekmeister,我讨厌这样的想法。不要聪明。然而,这几乎是 Java 对子字符串所做的。

标签: python optimization


【解决方案1】:
  1. 足够快而不是什么?你现在怎么做?你到底在存储什么,你到底在检索什么?答案可能很大程度上取决于此。这让我们...

  2. 测量!不要从理论上讨论和分析;尝试衡量什么是更高效的方式。然后确定可能的性能提升是否值得重构您的数据库。

编辑:我刚刚运行了一个测试,测量字符串切片与在以(start, end) 元组为键的字典中查找。这表明没有太大区别。不过,这是一个非常幼稚的测试,所以请谨慎对待。

【讨论】:

  • 当前的方法只是将句子中的文本、句子和标记都存储为单独的字符串(在数据库中),并将它们相互链接。对我来说似乎有很多不必要的臃肿;一个 2mb 的文本最终会占用 28mb 的数据库。无论如何,将一直检索到的是文本中的单个句子。另一种方法是根据存储的索引对文本进行切片。但你有一个非常好的观点。测量可能是最好的方法。 =P
  • 也不要低估决策部分:如果存在性能/存储空间权衡(并且大部分时间存在),您必须考虑您拥有的资源。如果您真的需要 CPU 时间,28mb 并不算多,但您可以使用 TB 级硬盘。如果您正在运行一个每天只能访问一次的小型嵌入式系统,那么 28 mb 就很多了。好吧,我想我刚刚写的整件事都归结为“它总是取决于”:-)
  • @tehgeekmeister:请用这些额外的事实更新您的问题。
【解决方案2】:

在评论中,OP 提到“在数据库中”膨胀——但没有关于他所谈论的数据库的信息;从该评论中的少量信息来看,似乎不一定涉及 Python 字符串切片,而是由数据库引擎在检索时完成“切片”。

如果这是实际情况,那么我会建议不要在数据库中存储冗余信息的一般原则——一种“正常形式”(可能是一种松散的表达方式;-),其中信息只存储一次并派生信息重新计算(或数据库引擎的缓存费用等;-)应该是规范,并且通过故意存储派生信息的“非规范化”非常例外,并且只有在特定的、经过良好衡量的检索性能需求证明合理时。

如果对“数据库”的引用是错误的;-),或者更确切地说是像我在上面对“正常形式”所做的那样在宽松的意义上使用;-),那么另一个考虑可能适用:因为 Python 字符串是不可变的,所以它不必通过复制来进行切片似乎是很自然的,而是让每个切片重用要从中切片的父级内存空间的一部分(就像对 numpy 数组的切片所做的那样)。但是,这目前不是 Python 核心的一部分。我曾经尝试过一个补丁来达到这个目的,但是添加一个对大字符串的引用并因此让它留在内存中的问题只是因为它的一个小子字符串仍然被引用,以便进行通用适应。对于大“父”字符串无论如何都需要保留在内存中的情况,仍然可以创建一个特殊用途的字符串子类(和一个 unicode 子类)。目前buffer 做了一点点,但是你不能在缓冲区对象上调用字符串方法(没有先将它显式复制到字符串对象),所以它只对输出和一些特殊情况非常有用......但是对于添加字符串方法并没有真正的概念障碍(我怀疑它会在核心中采用,但无论如何它应该很容易作为第三方模块进行维护;-)。

这种方法的价值很难通过测量得到可靠的证明,无论以何种方式——速度与当前的隐式复制方法非常相似;优势将完全体现在减少内存占用方面,这不会使任何给定的 Python 代码更快,而是允许某个程序在 RAM 更少的机器上执行,或者在多个实例时更好地执行多任务在不同的进程中同时使用。请参阅rope,了解曾经在 C++ 环境中尝试过的类似但更丰富的方法(但请注意,它并未成为标准;-)。

【讨论】:

    【解决方案3】:

    我也没有进行任何测量,但听起来您已经在使用 C 方法解决 Python 中的问题,您可能想看看Python's built-in mmap library

    内存映射文件对象的行为既像字符串,又像文件对象。然而,与普通字符串对象不同的是,它们是可变的。您可以在大多数需要字符串的地方使用 mmap 对象;例如,您可以使用 re 模块搜索内存映射文件。因为它们是可变的,你可以通过 obj[index] = 'a' 来改变单个字符,或者通过分配给切片来改变子字符串:obj[i1:i2] = '...'。您还可以从当前文件位置开始读写数据,并通过文件seek() 到不同位置。

    我不确定你的问题是否正是你要找的。值得重申的是,您需要进行一些测量。 Python's timeit library 很容易使用,但也有 cProfilehotshot,尽管据我了解,hotshot 有被从标准库中删除的风险。

    【讨论】:

      【解决方案4】:

      切片会因为创建源字符串的副本而无效吗?这可能是也可能不是问题。如果事实证明这是一个问题,是否无法简单地实现“字符串视图”?具有对源字符串的引用并具有起点和终点的对象。在访问/迭代时,它只是从源字符串中读取。

      【讨论】:

      • 这是我的担心。但我认为每个人都是对的:我还不需要优化,如果我这样做了,我应该在来这里之前测量和测试。
      【解决方案5】:

      过早的优化是万恶之源。

      向自己证明你确实需要优化代码,然后采取行动。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-02-11
        • 2015-03-16
        • 2020-05-03
        • 2021-11-26
        • 2019-03-29
        • 2019-06-19
        • 1970-01-01
        相关资源
        最近更新 更多