Efektywna konkatenacja ciągów w C++

Słyszałem kilka osób wyrażających obawy o operator " + " w std::string i różne obejścia, aby przyspieszyć konkatenację. Czy któraś z tych rzeczy jest naprawdę potrzebna? Jeśli tak, to jaki jest najlepszy sposób łączenia łańcuchów w C++?

Author: GEOCHET, 2009-03-04

11 answers

Dodatkowa praca prawdopodobnie nie jest tego warta, chyba że naprawdę potrzebujesz wydajności. prawdopodobnie będziesz miał znacznie lepszą wydajność po prostu za pomocą operatora += zamiast.

Po tym zrzeczeniu się odpowiem na twoje pytanie...

Wydajność klasy stl string zależy od implementacji STL, której używasz.

Możesz zagwarantować skuteczność i mieć większą kontrolę samemu wykonując konkatenację ręcznie przez c wbudowane funkcje.

Dlaczego operator+ nie jest skuteczny:

Spójrz na ten interfejs:

template <class charT, class traits, class Alloc>
basic_string<charT, traits, Alloc>
operator+(const basic_string<charT, traits, Alloc>& s1,
          const basic_string<charT, traits, Alloc>& s2)

Możesz zobaczyć, że nowy obiekt jest zwracany po każdym +. Oznacza to, że za każdym razem używany jest nowy bufor. Jeśli wykonujesz mnóstwo dodatkowych operacji+, nie jest to skuteczne.

Dlaczego można uczynić go bardziej wydajnym:

    Gwarantujesz skuteczność, a nie ufasz delegatowi, który zrobi to skutecznie za Ciebie]}
  • the std:: string class nie wie nic o maksymalnym rozmiarze Twojego łańcucha, ani jak często będziesz z nim łączyć. Możesz mieć tę wiedzę i możesz robić rzeczy w oparciu o te informacje. Doprowadzi to do zmniejszenia realokacji.
  • będziesz sterował buforami ręcznie, więc możesz mieć pewność, że nie skopiujesz całego ciągu znaków do nowych buforów, jeśli tego nie chcesz.
  • możesz użyć stosu dla swoich buforów zamiast stosu, który jest znacznie większy wydajny.
  • operator string + utworzy nowy obiekt string i zwróci go za pomocą nowego bufora.

Uwagi dotyczące wdrożenia:

  • śledź Długość łańcucha.
  • Zachowaj wskaźnik do końca łańcucha i początku lub po prostu początku i użyj start + długość jako przesunięcia, aby znaleźć koniec łańcucha.
  • upewnij się, że bufor, w którym przechowujesz łańcuch, jest wystarczająco duży, więc nie musisz realokacja danych
  • użyj strcpy zamiast strcat, więc nie musisz iterować na długości łańcucha, aby znaleźć koniec łańcucha.

Struktura danych Liny:

Jeśli potrzebujesz naprawdę szybkich konkatenacji rozważ użycie struktury danych linowych.

 76
Author: Brian R. Bondy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-16 13:41:15

Zachowaj ostatnią spację przed, a następnie użyj metody append z buforem. Na przykład, powiedzmy, że oczekujesz, że ostateczna Długość łańcucha będzie wynosić 1 milion znaków:

std::string s;
s.reserve(1000000);

while (whatever)
{
  s.append(buf,len);
}
 67
Author: Carlos A. Ibarra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-04 16:29:49

Nie martwiłbym się o to. Jeśli robisz to w pętli, ciągi zawsze będą prealokować pamięć, aby zminimalizować realokacje - wystarczy użyć operator+= w tym przypadku. A jeśli zrobisz to ręcznie, coś takiego lub dłużej

a + " : " + c

Następnie tworzy tymczasowe-nawet jeśli kompilator mógłby wyeliminować niektóre kopie zwracające wartość. Jest tak dlatego, że w sukcesywnie wywołanym operator+ nie wie, czy parametr odniesienia odwołuje się do nazwanego obiektu, czy do tymczasowego zwracanego z pod operator+ inwokacja. Wolałbym nie martwić się o to, zanim nie wyprofiluję. Ale weźmy przykład, aby to pokazać. Najpierw Wprowadzamy nawiasy, aby Wiązanie było jasne. Argumenty umieszczam bezpośrednio po deklaracji funkcji, która jest używana dla jasności. Poniżej pokazuję, jakie jest wtedy wyrażenie wynikowe:

((a + " : ") + c) 
calls string operator+(string const&, char const*)(a, " : ")
  => (tmp1 + c)

Teraz, w tym dodatku, tmp1 jest tym, co zostało zwrócone przez pierwsze wywołanie do operatora+ z pokazanymi argumentami. Zakładamy, że kompilator jest naprawdę sprytny i optymalizuje kopię zwracanej wartości. Tak więc otrzymujemy jeden nowy ciąg zawierający konkatenację a i " : ". Tak się dzieje:

(tmp1 + c)
calls string operator+(string const&, string const&)(tmp1, c)
  => tmp2 == <end result>

Porównaj to z poniższym:

std::string f = "hello";
(f + c)
calls string operator+(string const&, string const&)(f, c)
  => tmp1 == <end result>

Używa tej samej funkcji dla tymczasowego i nazwanego ciągu! Tak więc kompilator mA skopiować argument do nowego ciągu znaków i dołączyć do niego i zwrócić go z ciała operator+. Nie może wziąć pamięci tymczasowej i dołączyć do niej. Im większe wyrażenie jest, im więcej kopii ciągów trzeba zrobić.

Next Visual Studio i GCC będą wspierać semantykę move c++1x (uzupełniając Kopiuj semantykę) i odniesienia rvalue jako eksperymentalny dodatek. Pozwala to ustalić, czy parametr odwołuje się do tymczasowego czy też nie. To sprawi, że takie dodatki będą zdumiewająco szybkie, ponieważ wszystkie powyższe zostaną umieszczone w jednym "potoku dodawania" bez kopii.

Jeśli okaże się wąskim gardłem, nadal możesz do

 std::string(a).append(" : ").append(c) ...

Wywołania append dołączają argument do *this, a następnie zwracają odniesienie do siebie. Więc nie kopiuje się tam tymczasowych. Alternatywnie można użyć operator+=, ale trzeba by brzydkie nawiasy naprawić pierwszeństwo.

 16
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-04 17:16:19

Dla większości zastosowań, to po prostu nie ma znaczenia. Po prostu napisz swój kod, błogiej nieświadomości, jak dokładnie działa operator+, i weź sprawy w swoje ręce tylko wtedy, gdy stanie się widocznym wąskim gardłem.

 11
Author: Pesto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-04 16:15:18

W przeciwieństwie do systemu. NET.Strings, C++'S std::strings mutowalne i dlatego mogą być budowane poprzez prostą konkatenację tak szybko jak za pomocą innych metod.

 7
Author: James Curran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-04 16:23:33

Może zamiast tego std:: stringstream?

Ale Zgadzam się z sentymentem, że prawdopodobnie powinieneś po prostu zachować to do utrzymania i zrozumiałe, a następnie profil, aby zobaczyć, czy naprawdę masz problemy.

 5
Author: Tim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-04 16:16:28

W Imperfect C++, Matthew Wilson przedstawia dynamiczny string concatenator, który wstępnie oblicza długość ostatniego ciągu, aby mieć tylko jeden przydział przed połączeniem wszystkich części. Możemy również zaimplementować statyczny konkatenator, bawiąc się szablonami wyrażeń .

Tego typu pomysł został zaimplementowany w STLport implementacja std::string -- która nie jest zgodna ze standardem z powodu tego dokładnego włamania.

 4
Author: Luc Hermitte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-04 17:04:00

std::string operator+ przydziela nowy ciąg znaków i kopiuje dwa ciągi znaków za każdym razem. powtarzaj wiele razy i robi się drogo, O (n).

std::string append i operator+= z drugiej strony, zwiększ pojemność o 50% za każdym razem, gdy ciąg musi rosnąć. Co znacznie zmniejsza liczbę alokacji pamięci i operacji kopiowania, O (log n).

 3
Author: timmerov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-22 16:56:31

Dla małych strun to nie ma znaczenia. Jeśli masz duże ciągi, lepiej przechowywać je, ponieważ są one w vector lub w innej kolekcji jako części. I dodaj swój algorytm do pracy z takim zestawem danych zamiast jednego dużego ciągu.

Preferuję std:: ostringstream do kompleksowej konkatenacji.

 2
Author: Mykola Golubyev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-04 16:20:05

Jak w przypadku większości rzeczy, łatwiej jest czegoś nie robić niż robić to.

Jeśli chcesz wypisać Duże ciągi znaków do interfejsu graficznego, może się zdarzyć, że niezależnie od tego, do czego jesteś wyprowadzany, może obsługiwać ciągi w kawałkach lepiej niż jako duży ciąg (na przykład, łączenie tekstu w edytorze tekstu-zwykle zachowują linie jako oddzielne struktury).

Jeśli chcesz przesyłać dane do pliku, przesyłaj je strumieniowo, zamiast tworzyć duży ciąg znaków i wysyłać je.

I ' ve never found a need to zrobić konkatenację szybciej konieczne, jeśli usunąłem niepotrzebną konkatenację z wolnego kodu.

 2
Author: Pete Kirkham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-04 16:22:13

Prosta tablica znaków, zamknięta w klasie, która śledzi rozmiar tablicy i liczbę przydzielonych bajtów jest najszybsza.

Sztuką jest zrobić tylko jeden duży przydział na początku.

At

Https://github.com/pedro-vicente/table-string

Benchmarki

Dla Visual Studio 2015, x86 debug build, poprawienie merytoryczne nad C++ std:: string.

| API                   | Seconds           
| ----------------------|----| 
| SDS                   | 19 |  
| std::string           | 11 |  
| std::string (reserve) | 9  |  
| table_str_t           | 1  |  
 0
Author: Pedro Vicente,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-02 18:13:32