🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: pl.comp.lang.c
Article #309477

Re: Zabrali kopiowanie przy zapisie...

#309477
From: Piotr Wyderski
Date: Thu, 12 Oct 2017 22:41
99 lines
7391 bytes
M.M. wrote:

> Mogę poprosić jakieś przykłady na poparcie tego? Proszę, bo chcę
> zrozumieć co masz na myśli.

Są zasadniczo trzy główne przypadki użycia danych tekstowych:

1. Budowanie napisu.
2. Pełnoskalowe użycie zbudowanego napisu, z funkcjami wyszukiwania,
regexpami itp.
3. Ograniczone użycie zbudowanego napisu, które traktuje jego zawartość
jako całość i gdzie nie interesuje nas, skąd się napis wziął.

C++ stawia na 2, koślawie łącząc go z 1 i całkowicie ignorując 3.
Tak się jednak składa, że to 3. jest kluczowe dla wydajnego
przetwarzania danych wielkoskalowych, a 2. służy do pokazywania
studentom i zastosowań nieistotnych w rodzaju program_options itp.
drobnicy, gdzie stringów jest maksymalnie kilka tysięcy. Ja mówię
o miliardach. 3 sprowadza się do wzbogaconej o lukier syntaktyczny
std::pair<const char*, std::size_t> i jest wyjątkowo niemedialny.
Pozwala jednak zrobić cuda, w razie potrzeby nawet na zatarcie
źródła pochodzenia danych -- treść może pochodzić z std::string,
mogę sobie skonstruować ten wrapper na gołej tablicy bajtów w pamięci
itp. -- po stronie konsumenta danych tego nie widać. W C++17
(wreszcie!) wprowadzono string_view, choć jak to w komitecie
bywa, zatrzymali się w połowie drogi i nie wyciągnęli kluczowych
wniosków. Choćby takich, że taka warstwa prezentacji jest generyczna
i nadaje się do wszelkich innych koncepcyjnie równoważnych rzeczy,
np. do wyjęcia gołej zawartości std::vector<double>. Trzeba
było wprowadzić std::view<T> i specjalizować go dla znaków.
Ja coś takiego zrobiłem kilka lat temu i nazwałem segmentem.
Sprawdza się znakomicie.

Przyjęcia 2 jako wersji wiodącej i połączenie jej z 1 narzuca
na 1 dramatyczne ograniczenia, które rykoszetem odbijają się na 2.
Pomijając już sprawę wielowątkowości, z chęci redukcji narzutu 
pamięciowego i ogólnych własności takiej konstrukcji wynika,
że string może rosnąć tylko w jedną stronę. Przyjęto, że na
końcu. Wydajnie doklejać znaczków na początek się nie da, bo
trzeba kopiować całe dane w prawo. Ale skoro string może
rosnąć i dane są trzymane w obszarze ciągłym, to kiedyś ten
obszar się wyczerpie i trzeba będzie go powiększać. Kiedy?
Jeśli chcemy oszczędzać pamięć, a w przypadku danych o dużym
wolumenie nawet musimy, to rezerwa musi być mała. Idealnie
jednoznakowa. No ale wtedy na append() kopiuję cały string
co znaczek i budowanie stringa wybucha mi do czasu kwadratowego.
Genialne. No to od drugiej strony: powiększam rezerwę wykładniczo,
co mi złożoność worst case redukuje do O(n log n). Ale moje
dane mogą mieć długość paskudnie interferującą z algorytmem
powiększania i po systemie latają wielkie bufory z krótką
treścią. A ja nie mam na nie miejsca. Można stosować różne
kompromisy między tymi skrajnościami i nawet się z nich doktoryzować,
ale rozwiązaniem poprawnym jest zauważenie, że budowanie stringa
i jego użycie to dwie zupełnie różne czynności i każda z nich
powinna być zaimplementowana optymalnie. Builder nie nakłada
na mnie żadnych ograniczeń na to, od której strony buduję finalny
napis, trywialna implementacja równie dobrze poradzi sobie z obiema.
Podczas budowania nie muszę też niczego kopiować, bo nie ma
najmniejszej potrzeby, by dane tymczasowe znajdowały się w pamięci
ciągłej. Skończy mi się buforek, to sobie dodam następny, jak
w std::deque. Otrzymuję funkcjonalność znacznie bogatszą niż
oryginał przy gwarantowanej i bardzo niskiej złożoności czasowej
operacji. Jeśli alokator poza standardowym API wspiera też pewne
specjalne konstrukcje, np. alokację bloków o ustalonej wielkości,
to gwarancje złożonościowe mogą być nawet formalne, a nie tylko
spodziewane. A gdy wreszcie string powstaje, mogę sprawdzić, czy
już takiego nie budowałem i zwrócić gotowy element, a nie alokować
duplikat. Porównania na równość sprowadzą mi się wtedy do porównania
wskaźników na bufor, bez konieczności zaglądania w znaczki. A to od
razu dramatycznie przyspiesza kontenery unordered. I mnóstwo innych
tego typu implikacji.

Tylko tu trzeba wyjść od skali rzeczywistego zastosowania i popatrzeć
na problem całościowo, bo rozwiązanie opiera się na efekcie synergii
wynikającej z dostosowania różnych składowych biblioteki, a nie z 
ulepszania jednej klasy. Tylko z efektów można wywnioskować, że
komitet nie ma zwyczaju tak działać i wolą się bawić modelowaniem
pojedynczego stringa, by w przykładach ładnie wyglądał. Problem w tym,
że C++ nie ma większego sensu w projektach małoskalowych (wygodniej
przetworzysz sobie swoje dane w Javie lub innym Pythonie, choćby 
dlatego, że nie musisz zarządzać czasem życia obiektu), a do 
wielkoskalowych się nie nadaje ze względu na ograniczenia projektowe
swojej biblioteki standardowej. I trzeba uprawiać rzeźbiarstwo 
artystyczne...

> Hmmm, ja nie wiem. Dla mnie string to string, biorę i używam. A
> jeśli działa za wolno, to coś kombinuję samemu.

Właśnie od tego jest biblioteka standardowa, byś nie musiał robić
samemu, tylko wziąć gotowiec z pudełka i z zadowoleniem używać.
A skoro jednak kombinujesz, i do tego nie Ty jeden, to znaczy, że
z klasą biblioteczną jest coś bardzo nie tak. Własnych wektorów
jakoś ludzie nie piszą, ani mapek. Wzajemnie niezgodnych mutacji
stringa jest za to zatrzęsienie i wcale nie dlatego, że kontener
do napisów jest niesamowicie interesującą problematyką, tylko
wersja referencyjna stwarza więcej problemów niż rozwiązuje
i ludzie sobie to obchodzą jak umieją. "Po owocach ich poznacie je".

	Pozdrawiam, Piotr
Technical Details
Message-ID: <oror44$t12$1@node1.news.atman.pl>
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.icm.edu.pl!newsfeed2.atman.pl!newsfeed.atman.pl!.POSTED!not-for-mail
References: <or4v67$ene$1@node1.news.atman.pl> <72ac4d9c-806c-4c0d-9d17-e6a1bca634cb@googlegroups.com> <148be543-e417-4b7b-aaa1-5fad87fe41bd@googlegroups.com> <137swhz7qzvw7.dlg@tyczka.com> <6a1a94c7-2d75-4569-8fae-574fb23c919e@googlegroups.com> <d73024b7-92e0-49f9-b827-bfc7b639739b@googlegroups.com> <73ec442f-1a8b-43fc-a06f-a5534a6f3438@googlegroups.com> <da509e00-e7ff-49bb-8870-b77b25bcc560@googlegroups.com> <c8b4dab5-5afa-45a4-a3d8-67bc8a5b2aee@googlegroups.com> <fe52664e-2569-46e3-b18c-750c30cd8963@googlegroups.com> <orokn5$n01$1@node1.news.atman.pl> <8a58cf5e-40ab-48dd-a359-83e30597eba3@googlegroups.com>

Show Full Headers
Complete Article Headers
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.icm.edu.pl!newsfeed2.atman.pl!newsfeed.atman.pl!.POSTED!not-for-mail
From: Piotr Wyderski <peter.pan@neverland.mil>
Newsgroups: pl.comp.lang.c
Subject: Re: Zabrali kopiowanie przy zapisie...
Date: Fri, 13 Oct 2017 00:41:50 +0200
Organization: ATMAN - ATM S.A.
Lines: 100
Message-ID: <oror44$t12$1@node1.news.atman.pl>
References: <or4v67$ene$1@node1.news.atman.pl> <72ac4d9c-806c-4c0d-9d17-e6a1bca634cb@googlegroups.com> <148be543-e417-4b7b-aaa1-5fad87fe41bd@googlegroups.com> <137swhz7qzvw7.dlg@tyczka.com> <6a1a94c7-2d75-4569-8fae-574fb23c919e@googlegroups.com> <d73024b7-92e0-49f9-b827-bfc7b639739b@googlegroups.com> <73ec442f-1a8b-43fc-a06f-a5534a6f3438@googlegroups.com> <da509e00-e7ff-49bb-8870-b77b25bcc560@googlegroups.com> <c8b4dab5-5afa-45a4-a3d8-67bc8a5b2aee@googlegroups.com> <fe52664e-2569-46e3-b18c-750c30cd8963@googlegroups.com> <orokn5$n01$1@node1.news.atman.pl> <8a58cf5e-40ab-48dd-a359-83e30597eba3@googlegroups.com>
NNTP-Posting-Host: h82-143-146-166-static.e-wro.net.pl
Mime-Version: 1.0
X-Trace: node1.news.atman.pl 1507848132 29730 82.143.146.166 (12 Oct 2017 22:42:12 GMT)
X-Complaints-To: usenet@atman.pl
NNTP-Posting-Date: Thu, 12 Oct 2017 22:42:12 +0000 (UTC)
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Thunderbird/45.8.0
In-Reply-To: <8a58cf5e-40ab-48dd-a359-83e30597eba3@googlegroups.com>
X-Antivirus: Avast (VPS 171012-0, 2017-10-12), Outbound message
X-Antivirus-Status: Clean
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit