Java和Python之间不可变字符串连接的性能比较

Cup*_*tor 5 python java string performance concatenation

更新:非常感谢Gabe和Glenn的详细解释.该测试不是用于语言比较基准,而是为了研究VM优化技术.

我做了一个简单的测试来理解Java和Python之间字符串连接的性能.

该测试是两种语言中默认不可变String对象/类型的目标.所以我不在Java测试中使用StringBuilder/StringBuffer.

测试只需添加100k次的字符串.Java消耗约32秒完成,而Python仅使用约13秒的Unicode字符串和0.042秒的非Unicode字符串.

我对结果有点惊讶.我认为Java应该比Python更快.Python利用什么优化技术来实现更好的性能?或者String对象在Java中设计得太重了?

操作系统:Ubuntu 10.04 x64 JDK:Sun 1.6.0_21 Python:2.6.5

Java测试确实使用-Xms1024m来最小化GC活动.

Java代码:

public class StringConcateTest {
public static void test(int n) {
    long start = System.currentTimeMillis();
    String a = "";
    for (int i = 0; i < n; i++) {
        a = a.concat(String.valueOf(i));
    }
    long end = System.currentTimeMillis();
    System.out.println(a.length() + ", time:" + (end - start));
}

public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
        test(1000 * 100);           
    }
}
Run Code Online (Sandbox Code Playgroud)

}

Python代码:

import time
def f(n):
    start = time.time()
    a = u'' #remove u to use non Unicode string
    for i in xrange(n):
        a = a + str(i)
    print len(a), 'time', (time.time() - start)*1000.0
for j in xrange(10):
    f(1000 * 100)
Run Code Online (Sandbox Code Playgroud)

Gle*_*ard 5

@ Gabe的答案是正确的,但需要清楚地展示而不是假设.

CPython(可能只有CPython)会在可能的情况下附加就地字符串.它何时可以做到这一点有局限性.

首先,它不能用于实习字符串.这就是为什么你在测试时永远不会看到这个a = "testing"; a = a + "testing",因为分配一个字符串文字导致一个实习字符串.您必须动态创建字符串,就像此代码一样str(12345).(这不是一个限制;一旦你以这种方式附加一次,结果是一个未处理的字符串,所以如果你在循环中追加字符串文字,这只会在第一次发生.)

其次,Python 2.x只能这样做str,而不是unicode.Python 3.x确实为Unicode字符串执行此操作.这很奇怪:这是一个主要的性能差异 - 复杂性的差异.这不鼓励在2.x中使用Unicode字符串,因为它们应该鼓励它来帮助过渡到3.x.

最后,没有其他对字符串的引用.

>>> a = str(12345)
>>> id(a)
3082418720
>>> a += str(67890)
>>> id(a)
3082418720
Run Code Online (Sandbox Code Playgroud)

这解释了为什么非Unicode版本在测试中比Unicode版本快得多.

这样做的实际代码是string_concatenatePython/ceval.c和同时适用于s1 = s1 + s2s1 += s2.该功能_PyString_ResizeObjects/stringobject.c还明确地说:下面的函数打破的概念,字符串是不可变的.另见http://bugs.python.org/issue980695.