为什么std :: string操作表现不佳?

我做了一个测试,比较几种语言的字符串操作,以选择服务器端应用程序的语言。 结果似乎很正常,直到我终于尝试C ++,这让我感到很惊讶。 所以我想知道我是否错过了任何优化并来这里寻求帮助。

测试主要是密集的字符串操作,包括连接和搜索。 该测试在Ubuntu 11.10 amd64上进行,并使用GCC的4.6.1版本进行。 该机器是戴尔Optiplex 960,具有4G RAM和四核CPU。

在Python(2.7.2)中:

def test():
    x = ""
    limit = 102 * 1024
    while len(x) < limit:
        x += "X"
        if x.find("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0) > 0:
            print("Oh my god, this is impossible!")
    print("x's length is : %d" % len(x))

test()

这给出了结果:

x's length is : 104448

real    0m8.799s
user    0m8.769s
sys     0m0.008s

在Java(OpenJDK-7)中:

public class test {
    public static void main(String[] args) {
        int x = 0;
        int limit = 102 * 1024;
        String s="";
        for (; s.length() < limit;) {
            s += "X";
            if (s.indexOf("ABCDEFGHIJKLMNOPQRSTUVWXYZ") > 0)
            System.out.printf("Find!n");
        }
        System.out.printf("x's length = %dn", s.length());
    }
}

这给出了结果:

x's length = 104448

real    0m50.436s
user    0m50.431s
sys     0m0.488s

在Javascript(Nodejs 0.6.3)

function test()
{
    var x = "";
    var limit = 102 * 1024;
    while (x.length < limit) {
        x += "X";
        if (x.indexOf("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0) > 0)
            console.log("OK");
    }
    console.log("x's length = " + x.length);
}();

这给出了结果:

x's length = 104448

real    0m3.115s
user    0m3.084s
sys     0m0.048s

在C ++(g ++ -Ofast)

Nodejs比Python或Java表现更好并不奇怪。 但是我希望libstdc ++能够比Nodejs提供更好的性能,其结果真让我惊讶。

#include <iostream>
#include <string>
using namespace std;
void test()
{
    int x = 0;
    int limit = 102 * 1024;
    string s("");
    for (; s.size() < limit;) {
        s += "X";
        if (s.find("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0) != string::npos)
            cout << "Find!" << endl;
    }
    cout << "x's length = " << s.size() << endl;
}

int main()
{
    test();
}

这给出了结果:

x length = 104448

real    0m5.905s
user    0m5.900s
sys     0m0.000s

小结

好的,现在让我们看看总结:

  • JavaScript上的Nodejs(V8):3.1s
  • CPython 2.7.2上的Python:8.8s
  • C ++与libstdc ++:5.9s
  • Java在OpenJDK 7上:50.4s
  • 出奇! 我在C ++中尝试了“-O2,-O3”,但注意到了帮助。 C ++似乎在V8中只有50%的JavaScript性能,甚至比CPython差。 任何人都可以向我解释,如果我错过了GCC的一些优化或是这种情况? 万分感谢。


    并不是说std::string表现不佳(就像我不喜欢C ++一样),那就是字符串处理对于其他语言来说已经大大优化了。

    如果你比较弦乐表演的比较是误导性的,而且如果它们不仅仅是为了表示这一点,那么就是放肆的。

    我知道一个事实,即Python字符串对象完全用C语言实现,实际上在Python 2.7中,由于unicode字符串和字节之间没有分离,所以存在许多优化。 如果你在Python 3.x上运行这个测试,你会发现它比较慢。

    Javascript有很多重度优化的实现。 预计这里的字符串处理非常好。

    你的Java结果可能是由于不适当的字符串处理或其他一些糟糕的情况。 我希望Java专家可以介入并修改这个测试并做一些修改。

    至于你的C ++示例,我期望性能稍微超过Python版本。 它执行相同的操作,解释器开销较少。 这反映在你的结果中。 在s.reserve(limit);之前进行测试s.reserve(limit); 会消除重新分配的开销。

    我会重申,你只是测试语言实现的单一方面。 此测试的结果不反映整体语言速度。

    我已经提供了一个C版本来展示这样的小事如何愚蠢的比赛:

    #define _GNU_SOURCE
    #include <string.h>
    #include <stdio.h>
    
    void test()
    {
        int limit = 102 * 1024;
        char s[limit];
        size_t size = 0;
        while (size < limit) {
            s[size++] = 'X';
            if (memmem(s, size, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26)) {
                fprintf(stderr, "zomgn");
                return;
            }
        }
        printf("x's length = %zun", size);
    }
    
    int main()
    {
        test();
        return 0;
    }
    

    定时:

    matt@stanley:~/Desktop$ time ./smash 
    x's length = 104448
    
    real    0m0.681s
    user    0m0.680s
    sys     0m0.000s
    

    所以我在ideone.org上玩了一下。

    这里有一个原始C ++程序的稍微修改过的版本,但是在循环中添加消除,所以它只测量对std::string::find()的调用。 请注意,我必须将迭代次数减少到〜40%,否则ideone.org会终止进程。

    #include <iostream>
    #include <string>
    
    int main()
    {
        const std::string::size_type limit = 42 * 1024;
        unsigned int found = 0;
    
        //std::string s;
        std::string s(limit, 'X');
        for (std::string::size_type i = 0; i < limit; ++i) {
            //s += 'X';
            if (s.find("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0) != std::string::npos)
                ++found;
        }
    
        if(found > 0)
            std::cout << "Found " << found << " times!n";
        std::cout << "x's length = " << s.size() << 'n';
    
        return 0;
    }
    

    我在ideone.org的结果是time: 3.37s 。 (当然,这是非常可疑的,但放纵一下,等待其他结果。)

    现在我们采用这些代码并交换注释行,以测试附加,而不是查找。 请注意,这一次,我尝试查看任何时间结果时将迭代次数增加了10倍。

    #include <iostream>
    #include <string>
    
    int main()
    {
        const std::string::size_type limit = 1020 * 1024;
        unsigned int found = 0;
    
        std::string s;
        //std::string s(limit, 'X');
        for (std::string::size_type i = 0; i < limit; ++i) {
            s += 'X';
            //if (s.find("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0) != std::string::npos)
            //    ++found;
        }
    
        if(found > 0)
            std::cout << "Found " << found << " times!n";
        std::cout << "x's length = " << s.size() << 'n';
    
        return 0;
    }
    

    尽管迭代次数增加了10倍,但我在ideone.org上的结果是time: 0s

    我的结论:这个基准在C ++中由搜索操作高度支配,循环中字符的附加对结果没有任何影响。 那真的是你的意图吗?


    惯用的C ++解决方案将是:

    #include <iostream>
    #include <string>
    #include <algorithm>
    
    int main()
    {
        const int limit = 102 * 1024;
        std::string s;
        s.reserve(limit);
    
        const std::string pattern("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    
        for (int i = 0; i < limit; ++i) {
            s += 'X';
            if (std::search(s.begin(), s.end(), pattern.begin(), pattern.end()) != s.end())
                std::cout << "Omg Wtf found!";
        }
        std::cout << "X's length = " << s.size();
        return 0;
    }
    

    我可以通过将字符串放在堆栈上并使用memmem来大幅提高速度 - 但似乎没有必要。 在我的机器上运行,这已经超过了python解决方案速度的10倍。

    [在我的笔记本上]

    time ./test X's length = 104448 real 0m2.055s user 0m2.049s sys 0m0.001s

    链接地址: http://www.djcxy.com/p/31513.html

    上一篇: Why do std::string operations perform poorly?

    下一篇: How to get line count cheaply in Python?