SORU
7 Ocak 2013, PAZARTESİ


c ve python daha 11 düzenli yavaş

Merhaba bölünmüş bir dize aşağıdaki kodu düzenli ifade kullanarak bölünmüş neden anlamak istiyorum

#include<regex>
#include<vector>
#include<string>

std::vector<std::string> split(const std::string &s){
    static const std::regex rsplit("  ");
    auto rit = std::sregex_token_iterator(s.begin(), s.end(), rsplit, -1);
    auto rend = std::sregex_token_iterator();
    auto res = std::vector<std::string>(rit, rend);
    return res;
}

int main(){
    for(auto i=0; i< 10000;   i)
       split("a b c", " ");
    return 0;
}

daha yavaş daha sonra aşağıdaki python kodu

import re
for i in range(10000):
    re.split('  ', 'a b c')

işte

> python test.py  0.05s user 0.01s system 94% cpu 0.070 total
./test  0.26s user 0.00s system 99% cpu 0.296 total

Im) üzerinde çınlama kullanarak.

derleme -O3 0.09s user 0.00s system 99% cpu 0.109 total aşağı getiriyor

CEVAP
9 Ocak 2013, ÇARŞAMBA


Dikkat edin

Ayrıca bu cevaba bakınız: temel olan http://stackoverflow.com/a/217082152 DÜZENLEYİNalt burada.


1000000 döngü daha iyi bir zamanlama ölçmek için artırılmış ettim.

Bu belki de benim zamanlama:

real    0m2.038s
user    0m2.009s
sys     0m0.024s

İşte kodunuz, biraz daha güzel bir eşdeğeri

#include <regex>
#include <vector>
#include <string>

std::vector<std::string> split(const std::string &s, const std::regex &r)
{
    return {
        std::sregex_token_iterator(s.begin(), s.end(), r, -1),
        std::sregex_token_iterator()
    };
}

int main()
{
    const std::regex r("  ");
    for(auto i=0; i < 1000000;   i)
       split("a b c", r);
    return 0;
}

Zamanlama:

real    0m5.786s
user    0m5.779s
sys     0m0.005s

Bu vektör ve string nesneleri inşaat/ayırma önlemek için bir optimizasyon:

#include <regex>
#include <vector>
#include <string>

void split(const std::string &s, const std::regex &r, std::vector<std::string> &v)
{
    auto rit = std::sregex_token_iterator(s.begin(), s.end(), r, -1);
    auto rend = std::sregex_token_iterator();
    v.clear();
    while(rit != rend)
    {
        v.push_back(*rit);
          rit;
    }
}

int main()
{
    const std::regex r("  ");
    std::vector<std::string> v;
    for(auto i=0; i < 1000000;   i)
       split("a b c", r, v);
    return 0;
}

Zamanlama:

real    0m3.034s
user    0m3.029s
sys     0m0.004s

Bu 100% performans artışı yakındır.

Vektör döngü önce oluşturulur ve ilk yineleme bellek büyüyebilir. Daha sonra kaldırma hafızasında tutar ve dizeleri inşa bellek yokyerinde.


Başka bir performans artışı inşaat std::string tamamen/bozulmaması için olacak, ve bu nedenle, ayırma nesneleri kaldırma/.

Bu yönde bir belirsiz

#include <regex>
#include <vector>
#include <string>

void split(const char *s, const std::regex &r, std::vector<std::string> &v)
{
    auto rit = std::cregex_token_iterator(s, s   std::strlen(s), r, -1);
    auto rend = std::cregex_token_iterator();
    v.clear();
    while(rit != rend)
    {
        v.push_back(*rit);
          rit;
    }
}

Zamanlama:

real    0m2.509s
user    0m2.503s
sys     0m0.004s

Nihai bir gelişme const char * std::vector her char işaretçi s orijinal içinde bir alt noktası yere dönmek gibi olurc dizekendisi. Asıl sorun, her boş sonlandırılmış (bunun için daha sonra bir örnek string_ref 1y C kullanımı) olmaz çünkü bunu yapamazsın.


Bu son gelişme de bu ile elde edilebilir:

#include <regex>
#include <vector>
#include <string>

void split(const std::string &s, const std::regex &r, std::vector<std::string> &v)
{
    auto rit = std::cregex_token_iterator(s.data(), s.data()   s.length(), r, -1);
    auto rend = std::cregex_token_iterator();
    v.clear();
    while(rit != rend)
    {
        v.push_back(*rit);
          rit;
    }
}

int main()
{
    const std::regex r("  ");
    std::vector<std::string> v;
    for(auto i=0; i < 1000000;   i)
       split("a b c", r, v); // the constant string("a b c") should be optimized
                             // by the compiler. I got the same performance as
                             // if it was an object outside the loop
    return 0;
}

-O3 ile 3.3 (trunk) çınlama örnekleri yaptım. Belki diğer normal ifade kütüphaneler daha iyi yapabilir, ama her durumda, ayırma/deallocations sık bir performans isabet.


Boost.Normal ifade

Bunun için boost::regex zamanlamac dizedeğişkenler örnek:

real    0m1.284s
user    0m1.278s
sys     0m0.005s

Aynı kodu, boost::regex ve bu örnek std::regex arayüzü ad değiştirmek ve eklemek için gerekli sadece aynıdır.

Bunun için en iyi zaman içinde daha iyi olsun dileklerini, C stdlib düzenli uygulamalar çok yenidir.

EDİT

Tamamlama uğruna, bu (yukarıda "son gelişme" öneri) ve bir şey std::vector<std::string> &v eşdeğer sürümü geliştirilmiş performans değildi: . bahsedilen denedim

#include <regex>
#include <vector>
#include <string>

template<typename Iterator> class intrusive_substring
{
private:
    Iterator begin_, end_;

public:
    intrusive_substring(Iterator begin, Iterator end) : begin_(begin), end_(end) {}

    Iterator begin() {return begin_;}
    Iterator end() {return end_;}
};

using intrusive_char_substring = intrusive_substring<const char *>;

void split(const std::string &s, const std::regex &r, std::vector<intrusive_char_substring> &v)
{
    auto rit = std::cregex_token_iterator(s.data(), s.data()   s.length(), r, -1);
    auto rend = std::cregex_token_iterator();
    v.clear(); // This can potentially be optimized away by the compiler because
               // the intrusive_char_substring destructor does nothing, so
               // resetting the internal size is the only thing to be done.
               // Formerly allocated memory is maintained.
    while(rit != rend)
    {
        v.emplace_back(rit->first, rit->second);
          rit;
    }
}

int main()
{
    const std::regex r("  ");
    std::vector<intrusive_char_substring> v;
    for(auto i=0; i < 1000000;   i)
       split("a b c", r, v);

    return 0;
}

Bu array_ref and string_ref proposal ile bir ilgisi yoktur. İşte bir örnek kodu kullanma

#include <regex>
#include <vector>
#include <string>
#include <string_ref>

void split(const std::string &s, const std::regex &r, std::vector<std::string_ref> &v)
{
    auto rit = std::cregex_token_iterator(s.data(), s.data()   s.length(), r, -1);
    auto rend = std::cregex_token_iterator();
    v.clear();
    while(rit != rend)
    {
        v.emplace_back(rit->first, rit->length());
          rit;
    }
}

int main()
{
    const std::regex r("  ");
    std::vector<std::string_ref> v;
    for(auto i=0; i < 1000000;   i)
       split("a b c", r, v);

    return 0;
}

Ayrıca string_ref bir vektör yerine vektör dönüş split durum string kopya dönmek için daha ucuz olacaktır.

2 DÜZENLEYİN

Bu yeni çözüm getiri ile çıkış almak mümkün. Marshall ve Etkileşimleri (string_ref yeniden var) kütüphanenin uygulama https://github.com/mclow/string_view bulunan string_view kullandım.

#include <string>
#include <string_view>
#include <boost/regex.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/iterator/transform_iterator.hpp>

using namespace std;
using namespace std::experimental;
using namespace boost;

string_view stringfier(const cregex_token_iterator::value_type &match) {
    return {match.first, static_cast<size_t>(match.length())};
}

using string_view_iterator =
    transform_iterator<decltype(&stringfier), cregex_token_iterator>;

iterator_range<string_view_iterator> split(string_view s, const regex &r) {
    return {
        string_view_iterator(
            cregex_token_iterator(s.begin(), s.end(), r, -1),
            stringfier
        ),
        string_view_iterator()
    };
}

int main() {
    const regex r("  ");
    for (size_t i = 0; i < 1000000;   i) {
        split("a b c", r);
    }
}

Zamanlama:

real    0m0.385s
user    0m0.385s
sys     0m0.000s

Bu önceki sonuçlara göre kaç not. Tabii ki, değil bir dolgu vector döngünün içinde (ne eşleşen bir şey önceden muhtemelen çok), ama bir aralık neyse, dizi bitti dizi tabanlı for, hatta kullanmak için bir dolgu vector.

iterator_range üzerinde değişen string(veya . orijinal üzerinde string_views oluşturur gibi ^em>boş dize sonlandırıldıbu çok hafif olur, hiç gereksiz dize ayırma oluşturuluyor.

Sadece split bu uygulama kullanarak, ama aslında bunu yapabiliriz vector bir dolum karşılaştırmak için:

int main() {
    const regex r("  ");
    vector<string_view> v;
    v.reserve(10);
    for (size_t i = 0; i < 1000000;   i) {
        copy(split("a b c", r), back_inserter(v));
        v.clear();
    }
}

Bu kullanır aralığı kopya algoritmanın her tekrarında vektör doldurmak için destek, zamanlama:

real    0m1.002s
user    0m0.997s
sys     0m0.004s

Olarak görülebilir, ** 60 optimum çıkış param sürümü ile karşılaştırıldığında pek bir fark yok.

Not ayrıca bu şekilde çalışacak a proposal for a std::split var.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • J Medema

    J Medema

    11 EKİM 2006
  • Smith Micro Graphics

    Smith Micro

    15 Mayıs 2008
  • WK

    WK

    9 Ocak 2006