Pourquoi ne std::string effectuer des opérations de mal?

J'ai fait un test pour comparer les opérations de la chaîne en plusieurs langues pour le choix d'une langue pour l'application côté serveur. Les résultats semblait normal jusqu'à ce que j'ai finalement essayé C++, ce qui m'a surpris beaucoup. Donc je me demande si je n'avais pas compris tout de l'optimisation et de venir ici pour vous aider.

Le test sont principalement intensive opérations de la chaîne, y compris les concaténer et de la recherche. Le test est effectué sur Ubuntu 11.10 amd64, avec GCC version 4.6.1. La machine est Dell Optiplex 960, avec la 4G de RAM et PROCESSEUR Quad-core.

en 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()

qui donne le résultat:

x's length is : 104448

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

en 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 = %d\n", s.length());
    }
}

qui donne le résultat:

x's length = 104448

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

en 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);
}();

qui donne le résultat:

x's length = 104448

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

en C++ g++ -Ofast)

Il n'est pas surprenant que Nodejs effectue mieux que Python ou Java. Mais je m'attendais à libstdc++ donnerait une bien meilleure performance que Nodejs, dont le résultat vraiment surpris moi.

#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();
}

qui donne le résultat:

x length = 104448

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

Bref Résumé

OK, maintenant nous allons voir le résumé:

  • javascript sur Nodejs(V8): 3.1 s
  • Python sur Disponible 2.7.2 : 8,8 s
  • C++ avec la bibliothèque libstdc++: 5.9 s
  • Java OpenJDK 7: 50.4 s

Surprise! J'ai essayé "-O2-O3" en C++, mais en notant aidé. C++ semble qu'environ 50% des performances de javascript V8, et même les pauvres que Disponible. Quelqu'un pourrait-il m'expliquer si j'avais manqué quelques-uns d'optimisation de GCC ou est-ce le cas? Je vous remercie beaucoup.

  • Vous êtes le test d'un mélange des opérations, vous devriez probablement essayer de diviser le test dans les différents tests que d'effectuer différents contrôles de performance, par exemple: la croissance des chaînes, ou trouver, ou ... actuellement, vous ne pouvez pas savoir où le temps est passé. Et BTW, c'est probablement l'un tout à fait inutile de test de décider sur une langue...
  • Essayez s.reserve(limit); avant la boucle.
  • peut-être parce que chaînes de caractères en Java sont immuables. Je suppose que s += "X" est une performance killer là. C'est la raison pour StringBuilder existe.
  • Ouais, c'est vraiment de 50 ans, et j'ai essayé plusieurs fois. - Je censé Java devrait faire mieux que Python au moins, si.
  • Se débarrasser de la sortie de la console de votre test, il sera l'inclinaison de vos résultats
  • En java, les chaînes sont immuables et mis en commun, donc très lente. Normalement, vous utilisez stringbuilders pour assembler des chaînes de caractères. Tout cela ici, c'est comparer des pommes et des oranges, de toute façon.
  • Vous êtes aussi, y compris l'exécution de démarrage et de fin de chaque langue dans vos résultats.
  • Je ne pense pas que la sortie de la console est censé se produire, sauf pour une seule ligne à la fin...
  • Peut-être essayer un autre compilateur. J'ai compilé l'échantillon à l'aide de Visual Studio 2010. Runtime 1.79 Secondes sur un XEON 2.8 GHz.
  • Il serait vide de sens si je test chaque type d'opération séparément, il n'y aura pas de gagnants sur l'ensemble des opérations. Ce test est d'émuler un démon serveur qui écoute sur une socket ou l'attend sur le terminal pour les flux de caractères, et l'analyse en temps réel. Mais pour des raisons de simplicité, le programme de test omet beaucoup de pas-si-opérations pertinentes.
  • Avez-vous l'intention sur le chargement d'un caractère à la fois? Si oui, alors ce test est représentatif de votre utilisation, et la meilleure chose que vous pouvez le faire de ne pas décider d'une langue, mais à la recherche d'une meilleure approche. Divisant le test avec plus d'informations, car il vous permettra de savoir pour chaque langue, ce que sont les opérations les plus coûteuses et donc vous permettent d'optimiser votre traitement, et encore, le test va être absurde. Juste en C++, en changeant de s+="X" à s.push_back('X') aura probablement un impact plus important que les différences avec d'autres langues...
  • Euh, comment avez-C++ effectuer plus de mal que de Disponible? (Vous montrer 5.5 s avec C++, 8.8 secondes avec Disponible)
  • Pas aussi important pour le C++ partie de la Question, mais Matt Joiner est correcte dans son évaluation de la solution Java. Utiliser un StringBuffer pour concaténer au lieu d'une Chaîne de caractères. Cordes de Java ne sont pas mutables, et il va créer une nouvelle Chaîne chaque fois que vous += pour elle. À partir de docs.oracle.com/javase/1.5.0/docs/api/java/lang/..., "Une mémoire tampon de chaîne, c'est comme un String, mais peut être modifié. "
  • Je confirme qu'un autre compilateur peut vous aider. Sur ma machine de la compilation avec (visual studio 2010) cl /EHsc -O2 donne 1.753 s (près de vos résultats) tout en python 2.7.1 prend 6.266 s (plus ou moins en ligne avec les OP résultats)
  • Vous devriez demander à quelqu'un qui connaît la langue pour mettre en œuvre votre cas de test. Simplement à l'aide de StringBuilder en Java coupe le temps d'exécution de 75 secondes à 4 sur ma machine.
  • J'ai essayé de s.push_back ("X"), mais seulement une amélioration de l'ordre de 0,1 s. Comme d'autres " et mon option, le col de la bouteille est principalement la procédure de recherche. Et j'ai aussi testé string::trouver est plus rapide que strstr ou memmem si "-O" handicapé", mais beaucoup plus lentement si "-O2" est activé. Donc, string::trouverez peut-être difficile à optimiser.
  • Eh bien, ce n'est pas juste de comparer directement Disponible et C++ à l'aide de 'temps' de la commande, pour Disponible passe plus de temps qu'en C++ pour démarrer le moteur, qui est, de chargement et de compilation en byte-code. J'ai donc supposé Disponible plus rapidement si il C++ n'a pas dépasse de beaucoup plus, ce qui bien sûr, n'est pas très précise.
  • Ouais vous avez raison. J'ai testé avec StringBuilder et Java a donné le score de 2,8 s, la meilleure performance au sein de tous les tests, ce qui bien sûr encore plus lent que strstr en pur C (environ 1,6 s). Alors que bizarrement Java avec StringBuilder encore effectuée environ 30% plus lent que Nodejs sur mon Thinkpad R400 ordinateur portable.
  • Vous pouvez faire le même argument à l'encontre de Java ou d'un Nœud. (Essayez d'écrire un minimum de bonjour tout le monde python application et de soustraire le temps que prend à partir de l'indice de référence a pris, ce qu'il faudrait se débarrasser de retard de démarrage)
  • Notez que Lua chaînes sont aussi immuables et mis en commun (internés), et j'ai trouvé que Lua version de ce test s'exécute plus de deux fois plus rapide que la prochaine plus rapide de l'interprète (nodejs/v8). [à propos de 1,5 s pour Lua contre environ 3,1 s pour nodejs]
  • Vous pourriez aussi essayer de profil guidée optimisations -- en C et C++ ceci peut être important pour les boucles comme ceci (nœud.js/V8 sont certainement compilé avec de telles choses sur)
  • Il semble que GNU libstdc++ inline codé d'une boucle for std::string trouver. Muet déplacer. memmem et strstr sont beaucoup plus optimisé à la main, les versions écrites à l'assemblée de prendre avantage de SSE2 et SSE4 extensions.

InformationsquelleAutor Wu Shu | 2011-11-29