2015-03-15 21 views
9

Python ve C++ arasındaki performans denemelerini araştırırken, çoğunlukla dilsiz bir alt dizgi eşleşmesine odaklanan küçük bir örnek oluşturdum.Dize eşleştirme performansı: gcc versus CPython

using std::string; 
std::vector<string> matches; 
std::copy_if(patterns.cbegin(), patterns.cend(), back_inserter(matches), 
    [&fileContents] (const string &pattern) { return fileContents.find(pattern) != string::npos; }); 

yukarıdaki -o3 ile inşa edilmiştir:

İşte alakalı C++ olduğunu.

def getMatchingPatterns(patterns, text): 
    return filter(text.__contains__, patterns) 

İkisi desenleri ve giriş dosyasının large-ish set almak ve dilsiz alt dize aramayı kullanarak dosyada bulunan olanlara kalıplarının listeyi filtre:

Ve burada Python .

sürümleri şunlardır:

  • gcc - 4.8.2 (Ubuntu) ve 4.9.2 (cygwin)
  • piton - 2.7.6 (Ubuntu) ve 2.7.8 (cygwin)

Şaşırtıcı olan şey performanstır. Ben hem düşük spec Ubuntu koştum ve Python yaklaşık% 20 daha hızlı oldu. Cygwin ile orta spec PC'de aynı - Python iki kat daha hızlı. Profiler, döngülerin% 99'unun dize eşlemede harcandığını gösterir (dizi kopyalama ve liste anlamaları önemsizdir).

Açıkçası, Python uygulaması doğal C'dür ve C++ ile aynı olması beklenir, ancak bunu hızlı beklemezdim.

Gcc ile ilgili olarak ilgili CPython optimizasyonları hakkında herhangi bir fikir en çok hoş karşılanır.

Referans için, burada tam örnekler.

Python:

import sys 

def getMatchingPatterns(patterns, text): 
    return filter(text.__contains__, patterns) 

def serialScan(filenames, patterns): 
    return zip(filenames, [getMatchingPatterns(patterns, open(filename).read()) for filename in filenames]) 

if __name__ == "__main__": 
    with open(sys.argv[1]) as filenamesListFile: 
     filenames = filenamesListFile.read().split() 
    with open(sys.argv[2]) as patternsFile: 
     patterns = patternsFile.read().split() 

    resultTuple = serialScan(filenames, patterns) 
    for filename, patterns in resultTuple: 
     print ': '.join([filename, ','.join(patterns)]) 

C++: girişler sadece 50K HTLMs bir dizi (tümü her test, hiçbir özel önbelleğe alma içinde diskten okuma) almak

#include <iostream> 
#include <iterator> 
#include <fstream> 
#include <string> 
#include <vector> 
#include <unordered_map> 
#include <algorithm> 

using namespace std; 
using MatchResult = unordered_map<string, vector<string>>; 
static const size_t PATTERN_RESERVE_DEFAULT_SIZE = 5000; 

MatchResult serialMatch(const vector<string> &filenames, const vector<string> &patterns) 
    { 
    MatchResult res; 
    for (auto &filename : filenames) 
     { 
     ifstream file(filename); 
     const string fileContents((istreambuf_iterator<char>(file)), 
             istreambuf_iterator<char>()); 
     vector<string> matches; 
     std::copy_if(patterns.cbegin(), patterns.cend(), back_inserter(matches), 
        [&fileContents] (const string &pattern) { return fileContents.find(pattern) != string::npos; }); 

     res.insert(make_pair(filename, std::move(matches))); 
     } 
    return res; 
    } 

int main(int argc, char **argv) 
    { 
    vector<string> filenames; 
    ifstream filenamesListFile(argv[1]); 
    std::copy(istream_iterator<string>(filenamesListFile), istream_iterator<string>(), 
      back_inserter(filenames)); 

    vector<string> patterns; 
    patterns.reserve(PATTERN_RESERVE_DEFAULT_SIZE); 
    ifstream patternsFile(argv[2]); 
    std::copy(istream_iterator<string>(patternsFile), istream_iterator<string>(), 
      back_inserter(patterns)); 

    auto matchResult = serialMatch(filenames, patterns); 

    for (const auto &matchItem : matchResult) 
     { 
     cout << matchItem.first << ": "; 
     for (const auto &matchString : matchItem.second) 
     cout << matchString << ","; 
     cout << endl; 
     } 
    } 
+0

@SylvainLeroux - Ben profiler çalıştıran,% 99% gerçek eşleme, std :: string :: find 've' string .__ includes__' harcanıyor. '' '' '(Multiple multiple''' string string string string string string string string string string string string string string string string string string string string string string string string string string string string string string string string string string string string ((((((((((((((((((((((((((((((((((but but but but but but but but but but but but but but but but but but but but but but but but but but but but but but but but but but but ins ins ins ins ins ins ins ins ins ins ins ins ins but ins but but but but but but but but but but but but – RomanK

+3

Bu "Python nasıl bu kadar hızlı olabilir" sorusu değil, "C++ nasıl bu kadar yavaş olabilir" sorusu –

+0

Dosya önbelleğini hariç tuttunuz mu? gcc sürümü? Bir MCVE sağlayabilir misiniz? – edmz

cevap

13

piton 3.4 kod b'abc' in b'abcabc' (veya örneğinizde olduğu gibi b'abcabc'.__contains__(b'abc')),yöntemini çalıştırır; bu da satır içi işlevi stringlib_find; Aramayı FASTSEARCH'a delege eder.

hızlı arama/uygulama saymak, boyer- Moore ve Horspool arasında bir karışımı dayalı üstünde birkaç çan ve ıslık ile:

FASTSEARCH işlevi daha sonra değiştirilmiş Boyer-Moore arama algoritması kullanır.Biraz daha arka plan için , bkz:

not:: FASTSEARCH kullanırken Python en bir sorun değildir s[n], erişebilir yorumlarla tarafından belirtildiği gibi http://effbot.org/zone/stringlib.htm

da bazı değişiklikler vardır sıradan dize türleri, ancak bu kodu diğer bağlamlarda kullanarak iseniz sorunlara neden olabilir. Ayrıca, sayı dizisi, hedef dizede bir eşleşme mümkün değilse -1 değerini döndürür veiçin 0 gerçekten eşleşmeleri denetlemiş, ancak bulamadı. arayanlar dikkat!


GNU C++ Standard Library basic_string<T>::find() uygulama genel (ve dilsiz) mümkün olduğu gibidir; Sadece eşleşmeyi bulana kadar her ardışık karakter pozisyonundaki deseni uyuşturarak çalışır.


TL; DR: C++ standart kütüphane Python ile karşılaştırıldığında çok yavaş olmasının sebebi o std::basic_string<char> üstüne bir jenerik algoritma yapmaya çalışır çünkü, ama daha ilginç için verimli bunu yapmak için başarısız durumlar; Python'da ise programcı, vaka bazında ücretsiz olarak en verimli algoritmaları alır.

+2

Birisi C++ ile Boyer-Moore vs Python'da olabilir mi? Ne yazık ki şu anda hiçbir şeyi derleyemiyorum, ancak Boost'un [aramanın uygulanması] vardır (http://www.boost.org/doc/libs/1_57_0/libs/algorithm/doc/html/algorithm/Searching.html#the_boost_algorithm_library .Searching.BoyerMoore). –

+0

Teşekkürler, bu tam olarak aradığım cevaptır. Teknik olarak, libstdC++ 'yazarlarının 'char_traits' olmadan daha verimli eşleme yapan basic_string :: find' için bir şablon uzmanlığı yazmasını engelleyen hiçbir şey yoktur, bu yüzden daha çok gcc'nin bölümünde bir eksiklik gibi görünüyor. @BaummitAugen - Bahşiş için teşekkürler, Boost kütüphanesine gitmeyi denemeyi deneyeceğim. – RomanK

+3

“std :: search” öğesini geliştirmek için bir [öneri] (http://open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3905.html) olduğunu ve Özel durumlarda Boyer-Moore gibi daha hızlı algoritmalardan yararlanmak. – edmz