🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

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

Re: Zabrali kopiowanie przy zapisie...

#309478
From: M.M.
Date: Fri, 13 Oct 2017 09:41
263 lines
13692 bytes
On Friday, October 13, 2017 at 12:42:13 AM UTC+2, Piotr Wyderski wrote:
> 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ął.

Hmmmm. 


> C++ stawia na 2, koślawie łącząc go z 1 i całkowicie ignorując 3.
Chyba masz rację. W QT, jak dla moich potrzeb, budowanie napisu jest
bardzo wygodne, a jak mi mało, to mogę także QTextStream skonstruować
na QStringu.

> 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. 

Do wydajnego przetwarzania dużej ilości drobnicy potrzebny jest 
jakiś szablon obudowujący tablicę o stałym rozmiarze.

template< int MAX_SIZE=32, typename TS=char  >
class String {
  TS string[MAX_SIZE+1];
};


> Ja mówię o miliardach. 
Tak tak.


> 3 sprowadza się do wzbogaconej o lukier syntaktyczny
> std::pair<const char*, std::size_t> i jest wyjątkowo niemedialny.
Przyznaję, brzydkie. Ja bym na powyższym szablonie bazował.


> 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ć. 
No tak.


> 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.
Trochę się zgubiłem, co zrobiłeś?



> 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ę. 
Ale zwróć uwagę, że w przypadku dużej ilości malutkich napisów zazwyczaj
lepiej robić memmove i dopisywać na początek, niż zaczynać napis od
'środka' tablicy. 


> Przyjęto, że na
> końcu. Wydajnie doklejać znaczków na początek się nie da, bo
> trzeba kopiować całe dane w prawo. 
Tak, ale statystycznie rzadziej dokleja się na początku. W przypadku
drobnicy, mało znaczków przerzuca się na koniec. Gdy operacja 
doklejania na początku jest wiodąca w danym programie, to można
napisy trzymać w odwrotnej kolejności znaków. Jeśli dla drobnicy
zarezerwujemy kilka bajtów z przodu, to czasem będzie trzeba 
przerzucić bajty do przodu, aby pisać na końcu. Problemy z drobnicą
są, gdy ilość napisów idzie w miliardy, więc rezerwowanie większej
pamięci może skończyć się brakiem pamięci w systemie. 

O ile program nie jest bardzo specyficzny, to tamten powyższy
template wydaje się uniwersalny do drobnicy.



> 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.

Z pamięci piszę: często realloc umie dodać mały kawałek pamięci na
końcu bez kopiowania całości.


> 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ą. 
By się przydał jakiś autotune string. Mam coś podobnego w
drzewach czerwono czarnych, tyle że nie są auto, ale sam
muszę ustawić flagę jak się drzewo ma przekonfigurować.

Jeśli wiadomo jak string będzie się rozszerzał, to można dostosować
sposób allokacji. A jak nie wiadomo, to lipa.

W Qt jest struktura która automatycznie zmienia się z tablicy 
na stosie w coś podobnego do wektora. Można wydajnie operować 
na małych danych, a jak dojdzie do przekroczenia zakresu, to 
automatycznie zrobi malloc i rozszerza się jak wektor. Ale to
jest rada na sytuację, gdy jest dużo drobnicy, przy czym 
z rzadka z drobnicy robi się długi napis.


> 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.
Czy chodzi o C++ Buildera i TString? Używałem tego tak dawno temu, że
nie pamiętam jakie możliwości mają tam stringi.


> Podczas budowania nie muszę też niczego kopiować, bo nie ma
> najmniejszej potrzeby, by dane tymczasowe znajdowały się w pamięci
> ciągłej. 
W QT wszystkie kolekcje głęboką kopię robią dopiero przy 
modyfikowaniu, za to działają nieznacznie wolniej - wada. Zaleta
jest taka, że można bezkarnie sobie pisać 
duża_struktura1 = duża_struktura2



> 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. 
To jakieś hashe byś musiał trzymać, albo drzewo wyszukiwań. Na QStringu
chyba dobrze zadziałałoby operowanie na samych odnośnikach do tych
samych napisów.


> Porównania na równość sprowadzą mi się wtedy do porównania
> wskaźników na bufor, bez konieczności zaglądania w znaczki. 
Tak, to ogromna zaleta unikalności napisów. Ale zwróć uwagę, że
gdy użytkownik wpisze string, to i tak i tak trzeba go 
porównywać z zaglądaniem w znaczki, stosować drzewa poszukiwań i/albo
hashe.


> A to od
> razu dramatycznie przyspiesza kontenery unordered. I mnóstwo innych
> tego typu implikacji.
No tak, porównanie wskaźników jest szybsze niż znaczków. Ale gdy 
mamy dobrą hash-table, to porównań jest mało. Gdy mamy duży zbiór i
losowe strzały (w unorder set są zawsze losowe) w RAM, to wąskim
gardłem jest dostęp do pamięci. Na pewno współdzielone wskaźniki
przyspieszą, ale z powodu losowych strzałów w RAM, tutaj nie
spodziewałbym się dużych przyspieszeń.


> 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. 
Oczywiście. Dlatego zdecydowana większość autorów programów 
szachowych, gdzie wydajność jest bardzo ważna, nie stosuje 
ani jednego kontenera z biblioteki standardowej :D


> 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ł. 
Jak to nazwałeś, synergia, jest kluczową cechą w dużych bibliotekach.
Osobiście najczęściej używam QT i nie narzekam na brak synergii.
Zwróć jednak uwagę, że synergia nie idzie w parze z wydajnością, 
kontenery z QT zazwyczaj są wolniejsze niż z STL.


> 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), 
Ja nie zarządzam czasem życia w helołłerdach. Nawet gdybym
miał 50 wycieków pamięci, ale wiem że program się zakończy 
gdy zabraknie pamięci i mnie to nie obchodzi. Jednak wyciek
mam rzadko, bo QVector i inna hash-table same zwolnią gdy 
wykonanie dojdzie do klamerki.


> a do 
> wielkoskalowych się nie nadaje ze względu na ograniczenia projektowe
> swojej biblioteki standardowej. I trzeba uprawiać rzeźbiarstwo 
> artystyczne...
Czasami trzeba napisać swoją bibliotekę, gdy projekt jest wielkoskalowy, 
to relatywny koszt napisania biblioteki jest mały, a gdy zespół, albo
nawet jedna osoba, piszą po raz któryś podobny system, to dostosowują
biblioteki z poprzednich systemów. Jednym słowem, tutaj bardzo mocno z
Tobą się nie zgodzę.



> > 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ć.
Ja tak używam biblioteki QT. Na standardową prędzej czy później
bluzgam. Ale gdy jest potrzebna duża wydajność, o której czesto
piszesz, to i tak i tak jest potrzebna biblioteka specyficzna, a
taką bibliotekę programista musi sam albo napisać, albo dostosować z
innego podobnego projektu który robił wcześniej.


> A skoro jednak kombinujesz, i do tego nie Ty jeden, to znaczy, że
> z klasą biblioteczną jest coś bardzo nie tak. 
Tak, tylko nie pomieszajmy paradygmatów. Można kombinować, bo w
bibliotece nie ma, jak to ładnie nazwałeś, ani synergii, ani wygody, ani
funkcjonalności, a można kombinować, bo funkcjonalność jest za 
duża, co skutkuje małą wydajnością.



> Własnych wektorów
> jakoś ludzie nie piszą, ani mapek. 
Ależ piszą i to np. dwie różne dla jednej aplikacji!


> 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".

Nie wiem jaka jest prawdziwa przyczyna bibliotecznych wielości 
stringów w C++. Do zwykłego programowania ja używam QStringa i
nawet nie myślę o std::stringu. Gdy mam dużo drobnicy to obudowuję
szablonem stałą tablicę, korzystam z varlenghtarray, itd. Gdy
zobaczyłem jakiś czas temu ile trzeba się namęczyć, aby zrobić
kodowanie std::stringu, to se dałem spokój. QString natywnie 
operuje na utf16, nic nie musi deszyfrować w operacjach typu
'find', a gdy trzeba, to można go eksportować do bardzo wielu
kodowań.

Pozdrawiam
Technical Details
Message-ID: <041ef4ae-9bf0-40af-9afb-e2c4cd627457@googlegroups.com>
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.icm.edu.pl!news.nask.pl!news.nask.org.pl!news.unit0.net!peer03.am4!peer.am4.highwinds-media.com!peer03.iad!feed-me.highwinds-media.com!news.highwinds-media.com!z50no398016qtj.0!news-out.google.com!p42ni872qtp.0!nntp.google.com!z50no398015qtj.0!postnews.google.com!glegroupsg2000goo.googlegroups.com!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> <oror44$t12$1@node1.news.atman.pl>

Show Full Headers
Complete Article Headers
X-Received: by 10.200.6.141 with SMTP id f13mr558730qth.60.1507887701199;        Fri, 13 Oct 2017 02:41:41 -0700 (PDT)
X-Received: by 10.31.201.3 with SMTP id z3mr63679vkf.0.1507887701150; Fri, 13 Oct 2017 02:41:41 -0700 (PDT)
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.icm.edu.pl!news.nask.pl!news.nask.org.pl!news.unit0.net!peer03.am4!peer.am4.highwinds-media.com!peer03.iad!feed-me.highwinds-media.com!news.highwinds-media.com!z50no398016qtj.0!news-out.google.com!p42ni872qtp.0!nntp.google.com!z50no398015qtj.0!postnews.google.com!glegroupsg2000goo.googlegroups.com!not-for-mail
Newsgroups: pl.comp.lang.c
Date: Fri, 13 Oct 2017 02:41:40 -0700 (PDT)
In-Reply-To: <oror44$t12$1@node1.news.atman.pl>
Complaints-To: groups-abuse@google.com
Injection-Info: glegroupsg2000goo.googlegroups.com; posting-host=77.254.46.255; posting-account=xjvq9QoAAAATMPC2X3btlHd_LkaJo_rj
NNTP-Posting-Host: 77.254.46.255
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> <oror44$t12$1@node1.news.atman.pl>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <041ef4ae-9bf0-40af-9afb-e2c4cd627457@googlegroups.com>
Subject: Re: Zabrali kopiowanie przy zapisie...
From: "M.M." <mmarszik@gmail.com>
Injection-Date: Fri, 13 Oct 2017 09:41:41 +0000
X-Received-Bytes: 16232
X-Received-Body-CRC: 913295466
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit