【问题标题】:String concatenation vs string buffers in JavascriptJavascript中的字符串连接与字符串缓冲区
【发布时间】:2010-11-25 03:27:21
【问题描述】:

我正在阅读这本书 - 面向 Web 开发人员的专业 Javascript,其中作者提到与使用数组存储字符串然后使用连接方法创建最终字符串相比,字符串连接是一项昂贵的操作。很好奇,我在这里做了几个测试,看看它能节省多少时间,这就是我得到的 -

http://jsbin.com/ivako

不知何故,Firefox 通常以两种方式产生的时间有点相似,但在 IE 中,字符串连接要快得多。那么,这个想法现在可以被认为是过时的(浏览器可能已经改进了吗?

【问题讨论】:

  • 我刚刚在 IE 7 (jsbin.com/ivako) 中尝试了您的示例,它似乎与您所说的相反。我得到:与加号连接:92829 毫秒与 StringBuffer 连接:125 毫秒。在同一台机器上的 Firefox 3.5 中,我得到 Concatenation with plus:110 毫秒 Concatenation with StringBuffer:113 毫秒
  • 这很奇怪。因为我在本地运行脚本,所以我认为我得到了歪斜的结果,所以我放了 jsbin。还是一样。我使用的是 IE8 和 Firefox 3.5。我打算在我的虚拟电脑上的 IE7 上试试这个。
  • 92829 毫秒?那是1分32秒。你确定吗?
  • @bucabay 是的,没错。不是时代!

标签: javascript optimization


【解决方案1】:

**编辑:我假设人们仍在查看这篇文章 - 但它已经 3 年多了,所以你最好只查看:http://jsperf.com/string-concatenation/14

查看这篇文章的变化,看看它曾经说过什么。

【讨论】:

  • 很好的答案,但是如果连接的字符串更长(至少 500 个字符),那么无论运行哪种计算机,差异都会很明显
  • TraceMonkey,不是 Trackmonkey :)
  • 看看这个性能测试:jsperf.com/string-concatenation/14。显然,上面显示的方式在任何浏览器中都不是最佳的。
  • @zachzurn 这篇文章已有 3 年以上的历史了。事情肯定发生了变化。
  • 当然。人们仍然看到这些答案,所以我觉得有必要发表评论。
【解决方案2】:

即使它是 true 并且 join() 比串联更快也没关系。我们在这里谈论的是完全可以忽略不计的微量毫秒。

我总是更喜欢结构良好且易于阅读的代码而不是微观性能提升,我认为使用串联看起来更好并且更易于阅读。

只要我的两分钱。

【讨论】:

  • 在循环中多次发生的毫秒数并不总是可以忽略不计。它可以加起来几秒钟、几天或几年——取决于应用程序,我不敢相信你不知道。你的两分钱一文不值。
  • 完全同意@DaveWalley ...如果您不考虑complexity,即您的算法的运行时间/空间,这可能意味着平滑之间的差异并且反应迟钝。我认为你的两美分实际上值两美分......债务。
  • 我认为 Richard Knop 有一个总有效点。在项目的早期阶段,可读性非常重要。只要您不在 i>1000 循环中,连接就没有问题。请不要落入这个“过早优化”的陷阱……这是一个众所周知的反模式,在我的经历中造成了最大的悲痛。
【解决方案3】:

在我的系统(Windows 7 中的 IE 8)上,StringBuilder 在该测试中的时间在大约 70-100% 的范围内 - 也就是说,它不稳定 - 尽管平均值约为 95%正常的附加。

虽然现在说“过早的优化”很容易(我怀疑几乎在所有情况下都是如此),但有些事情值得考虑:

重复字符串连接的问题在于重复的内存分配和重复的数据复制(高级字符串数据类型可以减少/消除其中的大部分,但我们现在继续假设一个简单的模型)。由此让我们提出一些问题:

  • 使用什么内存分配?在幼稚的情况下,每个 str+=x 都需要分配 str.length+x.length 新内存。例如,标准的 C malloc 是一个相当糟糕的内存分配器。多年来,JS 实现发生了变化,其中包括更好的内存子系统。当然,这些变化并不止于此,而是触及现代 JS 代码的所有方面。因为现在古老的实现在某些任务中可能非常缓慢,并不一定意味着同样的问题仍然存在,或者程度相同。

  • 与上面一样,Array.join 的实现非常重要。如果它在构建最终字符串之前没有为最终字符串预分配内存,那么它只会节省数据复制成本——这些天主内存有多少 GB/s? 10,000 x 50 几乎没有突破极限。使用 POOR MEMORY ALLOCATOR 的智能 Array.join 操作预计会执行得更好一点,因为减少了重新分配的数量。随着分配成本的降低,预计这种差异会最小化。

  • 微基准代码可能存在缺陷,具体取决于 JS 引擎是否为每个 UNIQUE 字符串文字创建一个新对象。 (这会使它偏向 Array.join 方法,但一般需要考虑)。

  • 基准确实是一个微型基准 :) 根据上述任何或所有(然后是一些)条件,增加不断增长的大小应该会对性能产生影响。通常很容易展示有利于某种方法的极端情况——预期的用例通常更重要。

虽然老实说,对于任何形式的 sane 字符串构建,我都会使用普通的字符串连接,直到它被确定为瓶颈,如果有的话。

我会重新阅读本书中的上述陈述,看看作者是否确实要调用其他隐含的考虑,例如“对于非常大的字符串”或“疯狂数量的字符串操作”或“在 JScript/ IE6”等...如果不是,那么这样的陈述与“插入排序是 O(n*n)”一样有用 [当然,实现的成本取决于数据的状态和 n 的大小]。

免责声明:代码的速度取决于浏览器、操作系统、底层硬件、月球引力,当然还有您的计算机对您的感觉。

【讨论】:

    【解决方案4】:

    原则上这本书是正确的。加入一个数组应该比重复连接到同一个字符串要快很多。作为不可变字符串的简单算法,它明显更快。

    诀窍在于:JavaScript 作者大多是非专家涉猎者,他们在野外编写了大量使用连接的代码,而使用数组连接等方法的“好”代码相对较少。结果是浏览器作者可以通过迎合和优化“坏”、更常见的连接选项来更好地提高平均网页的速度。

    所以这就是发生的事情。较新的浏览器版本有一些相当复杂的优化内容,可以检测您何时进行大量连接,并对其进行破解,以便在内部以或多或少相同的速度更像数组连接。

    【讨论】:

      【解决方案5】:

      我实际上在这方面有一些经验,因为我的主要产品是一个大型的、仅限 IE 的 web 应用程序,它执行大量字符串连接以构建 XML 文档以发送到服务器。例如,在最坏的情况下,一个页面可能有 5-10 个 iframe,每个 iframe 都有几百个文本框,每个文本框都有 5-10 个 expando 属性。

      对于我们的保存功能,我们遍历每个选项卡 (iframe) 和该选项卡上的每个实体,提取每个实体上的所有 expando 属性并将它们全部填充到一个巨大的 XML 文档中。

      在分析和改进我们的保存方法时,我们发现在 IE7 中使用字符串连接比使用字符串数组方法慢很多。其他一些有趣的地方是访问 DOM 对象 expando 属性真的很慢,所以我们将它们全部放入 javascript 数组中。最后,自己生成 javascript 数组实际上最好在服务器上完成,然后您将其作为文字控件写入页面,以便在页面加载时执行。

      【讨论】:

      • 我在我的一个应用程序中遇到了类似的问题,我决定今天尝试这种方法 - 怎么说 - IE7 的改进绝对是可见的。
      【解决方案6】:

      众所周知,并非所有浏览器都是平等的。因此,保证不同区域的性能因浏览器而异。

      除此之外,我注意到与您相同的结果;但是,在删除不必要的缓冲区类后,直接使用数组和 10000 个字符串,结果更加紧密/一致(在 FF 3.0.12 中):http://jsbin.com/ehalu/

      除非您要进行大量的字符串连接,否则我会说这种优化是一种微优化。你的时间最好花在限制 DOM reflowsqueries(通常使用 document.getElementbyById/getElementByTagName)、实现 AJAX 结果的缓存(如果适用)和利用事件冒泡(某处有一个链接,我只是现在找不到)。

      【讨论】:

      • 我试过你的脚本,不幸的是它在 Firefox 3.5 中一直超时。
      • 更突出的是浏览器及其版本之间的性能差异。
      【解决方案7】:

      好的,这里有一个相关的模块:

      http://www.openjsan.org/doc/s/sh/shogo4405/String/Buffer/0.0.1/lib/String/Buffer.html

      这是创建字符串缓冲区的有效方法,通过使用

      var buffer = new String.Buffer();
      buffer.append("foo", "bar");
      

      这是我所知道的最快的字符串缓冲区实现。首先,如果您正在实现字符串缓冲区,请不要使用 push,因为这是一种内置方法并且速度很慢,因为一次 push 会遍历整个参数数组,而不是仅添加一个元素。

      这完全取决于join方法的实现,join方法的一些实现真的很慢,有些比较大。

      【讨论】:

      猜你喜欢
      • 2013-07-10
      • 2020-05-07
      • 2010-09-14
      • 2018-08-16
      • 1970-01-01
      • 2015-01-22
      • 1970-01-01
      • 2021-12-10
      • 1970-01-01
      相关资源
      最近更新 更多