Uuu, a które cudo tak robi? Nie ironicznie pytam, bo ja się zatrzymałem na wariacjach javowej ConcurrentHashMap
gdzie, w uproszczeniu, blokujemy pojedyńcze kubełki zamiast całej kolejkcji, oraz takie, które robią użytek z dzielonych muteksów/blokad - shared mutex/lock
.
Tu mogę się odnieść - bo temat jest mi znany z praktyki. W programowaniu funkcyjnym od dawna używa się Functional Data Structures - niestety trzeba poczytać, żeby ogarnąć (jest dużo prezentacji i video).
Paradoks tych struktur danych polega na tym, że w dowolnym małym benchmarku wyjdą oczywiście gorzej od mutowalnych (klasycznych) wersji (lista, hashmapa, etc.). Jeśli jednak wprowadzi się je w większym kodzie (np, aplikacji bankowej w javie) to okazuje się, że dostajemy zysk i na CPU i na RAM. Pierwszy raz jak to zobaczyłem to aż się zdziwiłem :-). Magii aczkolwiek nie ma.
Normalnie te struktury dają lekki narzut - powiedzmy 5-30% (w złośliwym benchmarku to i wiecej niż 200%) - tu mocno zależy co robimy.
Co ważne, wbrew powszechnym wyobrażeniom, niemutowalna lista nie oznacza, że jak chcemy coś dodać to się nie da. Da się, po prostu dostajemy nową listę. A oryginalna się nie zmienia. I nie, nie oznacza to, że kopiujemy dane. Magia?! Nie, zachęcam do poczytania.
I teraz dowcip. W praktycznie każdej większej aplikacji, gdzie mamy klasyczne mutowalne listy, mapy, gdzie miesiącami (latami) pracuje wielu ludzi, używa się defensive copying
. Człowiek się szybko uczy, że bez tego tylko śledzi dziwne błędy, bywa, że dc jest wręcz wymuszane na review.
Przykład: Tworzymy nowy obiekt z listą (jako pole), który dostaje ją jako arg konstruktora - najpierw kopia, bo cholera wie co to za lista, i co ktoś z nią zaraz zrobi. Przy zwracaniu tej listy (getter) da się zwykle załatwić bez kopiowania (widok), ale zwykle też zaraz ktoś to kopiuje. W ten sposób aplikacja ciągle coś kopiuje.
(Tu się zgadzam, że to problem bezpieczeństwa, ale w znaczeniu poprawności programu).
Przejście na persistent (functional) data structures powoduje, że całe to kopiowanie nie jest już potrzebne, można bezpiecznie sobie wszędzie przekazywać struktury (szczególnie jeśli obiekty w nich są niemutowalne). I nagle wychodzi, że benchmarki o które się obawialiśmy - po wprowadzeniu niemutowalności zamiast się pogorszyć - poprawiają się istotnie.
Trudniej mi to odnieść do GC. W zasadzie to funkcyjne struktury danych wymagają jakiegoś GC, bo nie wiemy kto obiekt (listę na przykład) trzyma i możemy zwolnić dopiero jak nikt nie korzysta (oczywiście możemy użyć Rust, ale używanie niemutowalnych struktur w Rust to coś czego jeszcze sensownie nie poćwiczyłem).
Mogę zgadywać, że autor cytowany przez chinkę (:-)) odnosić się może do sytuacji - defensive copy bez GC
- vs functional data structures + GC
(ale to nie jest przykład, który miałem, bo moje doświadczenie dotyczyło kobył javowych z GC w obu przypadkach).