Zliczanie referencji vs Mark and Swap

Zliczanie referencji vs Mark and Swap
nalik
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1039
3
Afish napisał(a):
hauleth napisał(a):

Tak, tylko to wymaga runtime, którego Rust stara się unikać. Dodatkowo przywiązuje ciebie do jednej implementacji asynchroniczności.

Ani jedno, ani drugie. Da się to zrobić na samej bibliotece standardowej (i nie trzeba mieć wsparcia systemu operacyjnego), ponadto może być wiele implementacji.

Gdy scheduling odbywa się bez wsparcia kompilatora bądź runtime, to obowiązek wywłaszczania rutyn spada na dewelopera. W takim wypadku wywołanie blokującej funkcji systemowej uśpi cały wątek, nieważne czy są inne rutyny, które mogłyby z niego skorzystać.
Gdy na ścieżce wywołania systemowego znajduje się scheduler rutyn, to takich sytuacji można uniknąć i przydzielić wątek innej rutynie. Owszem, dałoby się to zrobić tylko przy pomocy biblioteki, pod warunkiem, że wszystkie wywołania systemowe (oraz funkcje, które pośrednio z nich korzystają), także te dodane przez użytkowników bibliotek zewnętrznych, będą wywoływały scheduler. Pytanie, czy można w takim wypadku mówić braku wsparcia runtime.

AF
  • Rejestracja: dni
  • Ostatnio: dni
0
hauleth napisał(a):

Ale dokładnie tak działa Rust. Mamy "monadę" Future<Output = T> oraz kompilator to ładnie opakowuje w "do-syntax" w miejscach gdzie używamy async. Następnie musisz takie Future<Output = T> przekazać jawnie "na samej górze" do jakiegoś runtime, które wywoła "map" (w tym przypadku Future::poll).

Nie zrozumiałeś. Ja chcę mieć kompletnie bezkolorową funkcję bez żadnego async, bez specjalnego słowa na czekanie i bez lambd na każdym kroku.

Co wymaga konkretnego systemu operacyjnego w takim wypadku. Czyli de facto wymieniamy runtime na wymaganie całego OSa. Dalej w "niskopoziomowych" zastosowaniach jest to trudne.

Nie, fibery możesz zrobić w przestrzeni użytkownika.

Gdy na ścieżce wywołania systemowego znajduje się scheduler rutyn, to takich sytuacji można uniknąć i przydzielić wątek innej rutynie. Owszem, dałoby się to zrobić tylko przy pomocy biblioteki, pod warunkiem, że wszystkie wywołania systemowe (oraz funkcje, które pośrednio z nich korzystają), także te dodane przez użytkowników bibliotek zewnętrznych, będą wywoływały scheduler. Pytanie, czy można w takim wypadku mówić braku wsparcia runtime.

Kwestia gustu i trudno teoretyzować bez konkretów. Zazwyczaj funkcje systemowe i tak są przykryte biblioteką standardową, bo coś musi zrobić marshaling parametrów, ale to nie jest wsparcie ani kompilatora ani runtime'u, tylko od platformy, ale to ostatnie zawsze masz.

nalik
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1039
1
Afish napisał(a):

Gdy na ścieżce wywołania systemowego znajduje się scheduler rutyn, to takich sytuacji można uniknąć i przydzielić wątek innej rutynie. Owszem, dałoby się to zrobić tylko przy pomocy biblioteki, pod warunkiem, że wszystkie wywołania systemowe (oraz funkcje, które pośrednio z nich korzystają), także te dodane przez użytkowników bibliotek zewnętrznych, będą wywoływały scheduler. Pytanie, czy można w takim wypadku mówić braku wsparcia runtime.

Kwestia gustu i trudno teoretyzować bez konkretów. Zazwyczaj funkcje systemowe i tak są przykryte biblioteką standardową, bo coś musi zrobić marshaling parametrów, ale to nie jest wsparcie ani kompilatora ani runtime'u, tylko od platformy, ale to ostatnie zawsze masz.

Niebardzo. Syscall i tak zablokuje wątek, chyba, że nagle system zacznie dostarczać ABI dla asynchronicznych syscalli .... To co można zrobić to stworzyć po cichu następny wątek i kontynuować wykonywanie innych rutyn. Go nazywa to hand-off i jest to jak najbardziej zadanie runtime, a nie biblioteki czy platformy.

hauleth
  • Rejestracja: dni
  • Ostatnio: dni
1

Ja chcę mieć kompletnie bezkolorową funkcję bez żadnego async, bez specjalnego słowa na czekanie i bez lambd na każdym kroku.

Czyli język musi to wspierać bezpośrednio (bo musi dodać miejsca gdzie można "wywłaszczyć" proces). W takim wypadku tracisz główne założenie Rusta - czyli bezkosztową abstrakcję.

Nie, fibery możesz zrobić w przestrzeni użytkownika.

Ale to wymaga runtime, które jest kolejnym założeniem Rusta, zwłaszcza jeśli używamy tylko core. Ciężko byłoby napisać np. moduł jądra jeśli język wymaga runtime, którego jądro używać nie może.


Więc tak, dałoby się to zrobić, ale trzeba by odrzucić podstawowe założenia Rusta, co całkowicie mija się z celem. Nie można mieć ciastka i zjeść ciastka. Gdzieś musi być scheduler i jakoś musi wiedzieć gdzie może wywłaszczyć procesy.

AF
  • Rejestracja: dni
  • Ostatnio: dni
0
hauleth napisał(a):

Czyli język musi to wspierać bezpośrednio (bo musi dodać miejsca gdzie można "wywłaszczyć" proces). W takim wypadku tracisz główne założenie Rusta - czyli bezkosztową abstrakcję.

Przy obecnych zastosowaniach już masz dodane. W kodzie async jest to miejsce z await, a jeżeli poszedłbyś moją sugestią, to mógłbyś wywłaszczyć proces w momencie pobierania wartości z monady i zauważeniu, że tej wartości nie ma. Kontynuacja i tak jest skonfigurowana, tym w końcu zajmuje się future, więc jak dane będą dostępne (dostarczone czy to przez APC, czy to przez sterownik, czy przez coś innego), to ponownie wiesz, jak przestawić "wątki".

Ale to wymaga runtime, które jest kolejnym założeniem Rusta, zwłaszcza jeśli używamy tylko core. Ciężko byłoby napisać np. moduł jądra jeśli język wymaga runtime, którego jądro używać nie może.

Wątek w aplikacji siłą rzeczy jest przykryty przez API Twojego języka. Czy to new Thread(), czy coś innego, to nie ma znaczenia, nie wołasz jakiegoś CreateThreadEx albo fork bezpośrednio z WinAPI/POSIX-a. Biblioteka standardowa może w tym miejscu dostarczyć najprostszą pętlę wiadomości, do której kontynuacje byłyby wrzucane czy to przez monadę, czy to przez jakieś wywołanie Promise.Result które robiłoby Wait() pod spodem. To nie wymaga maszyny wirtualnej ani zaawansowanego runtime'u, potrzebny jest kawałek kodu dookoła pętli komunikatów.

Jeżeli zaś skupiasz się na zastosowaniach niskopoziomowych, to oczywistym jest, że tam lepiej nie przesadzać z magią, ale nie tego dotyczyła się moja wstawka o asyncu. Ja zwracam uwagę, że kolorowanie funkcji jest słabe i da się zrobić async bez niego. C# woli kolorować, inne języki zgapiają implementację, ale to nie znaczy, że jest to dobry sposób. Nie wiem, czy kolorowanie dla move/borrow jest tak samo słabe, ale widzę te same argumenty.

hauleth
  • Rejestracja: dni
  • Ostatnio: dni
1

W kodzie async jest to miejsce z await, a jeżeli poszedłbyś moją sugestią, to mógłbyś wywłaszczyć proces w momencie pobierania wartości z monady i zauważeniu, że tej wartości nie ma.

W Ruscie async/await działa dokładnie tak jak opisałeś, tylko używa innego API, bo nie ma ogólnego do-notation. Więc jeśli idziemy tą drogą, to tak, masz rację, to jest dobre rozwiązanie.

AF
  • Rejestracja: dni
  • Ostatnio: dni
1
hauleth napisał(a):

W Ruscie async/await działa dokładnie tak jak opisałeś

Przeczytałem tutorial do rusta i widzę, że robi normalne kolorowanie funkcji i zwracanie promesy, czyli robi to zupełnie inaczej, niż ja opisuję.

Edycja: Kurczę, jakieś "nie" mi wpadło zamiast "zupełnie" i kompletnie zmieniło sens zdania... Tak się kończy pisanie z telefonu.

hauleth
  • Rejestracja: dni
  • Ostatnio: dni
0

robi normalne kolorowanie funkcji i zwracanie promesy, czyli nie robi to inaczej, niż ja opisuję

No właśnie nie, ma tylko na taki zapis cukier nałożony, bo mało komu chce się pisać całe state-machine ręcznie za każdym razem.

AF
  • Rejestracja: dni
  • Ostatnio: dni
0
hauleth napisał(a):

robi normalne kolorowanie funkcji i zwracanie promesy, czyli nie robi to inaczej, niż ja opisuję

No właśnie nie

O ile dobrze widzę, to ten przykład pokazuje, jak zwracasz promesę z funkcji i potem na niej blokujesz w metodzie main. To, że nie używasz składni async nie sprawia, że nie masz kolorowania funkcji. C# i JS też pozwalają na robienie asynciem albo zwracanie promesy ręcznie, to nie jest clue.

KR
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 2964
0

Prawdopodobnie mógłbyś wspomniany problem rozjazdu między sync a async rozwiązać genericsami i monadami. Na pewno w Scali się da napisać kod działający uniwersalnie pod różnymi monadami (efektami). Nie jestem pewien, czy traity Rusta są już wystarczająco silne aby to zrobić (chyba nie), ale nie widzę raczej problemu aby w przyszłości było to możliwe, np. gdy Rust będzie mieć HKT.

Wtedy każda funkcja zwracałaby M[T] i była parametryzowana typem M. I teraz wystarczyłoby na najwyższym poziomie określić że np. M to Future i cały kod stałby się asynchroniczny.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.