• naurM
    link
    fedilink
    Polski
    arrow-up
    1
    ·
    edit-2
    3 months ago

    To są bardzo dobre porady, ale mam obawy, że w większości współczesnych projektów nie da się ich zaaplikować.

    Muratori przedstawia problem z perspektywy developera gier, gdzie zazwyczaj zarządza się tysiącami/milionami obiektów (wierzchołków, tekstur, kości, aktorów, itp.) i rezultat musi być powtarzalnie wygenerowany w 16ms.
    Rzecz w tym, że dzisiaj większość developerów pracuje nad aplikacjami, gdzie model przetwarzania danych i ich skala są zupełnie inne.

    Pierwszy z brzegu przykład - backend webowy. Dostajesz zapytanie i od razu przerzucasz problem na bazę danych.
    Ładujesz z niej kilkadziesiat, max kilkaset rekordów. Taką ilość danych można obrabiać w jakikolwiek sposób i będzie to wystarczająco wydajne. W 99% przypadków skończy się zresztą przekazaniem wszystkiego do biblioteki, która ulepi z tego JSONa.
    Gdzie tu jest potrzeba utrzymywania jakiegoś złożonego stanu w pamięci?
    Oczywiście można użyć alokatora pulowego do drzewa dokumentu JSON albo stron w silniku bazy danych, ale znowu - to jest szczegół implementacyjny jakiejś gotowej biblioteki.

    Przydałaby się jakaś analiza, ile MB pamięci musi odczytać gra do wygenerowania 1 klatki i serwer webowy do 1 requestu.
    Pewnie jest to różnica o kilka rzędów wielkości.

    Odrębną kwestią jest też konieczność izolacji danych różnych użytkowników w serwerach. W grach ten problem praktycznie nie występuje.

    Nawiasem mówiąc, zastanawiam się, czy to podejście gamedevu nie jest efektem tragicznej (przynajmniej do niedawna) wydajności domyślnego alokatora pod Windows.
    Czytałem niedawno zachwyty jakiegoś developera, który nie mógł uwierzyć, że pod Linuksem może po prostu zrobić new/malloc i nie widzi istotnego spadku szybkości gry.

    • サぺルOP
      link
      fedilink
      Polski
      arrow-up
      1
      ·
      3 months ago

      On nie kieruje tego do “większości współczesnych projektów”. Backend webowy to zupełnie inna warstwa abstrakcji.

      Mnie to rozśmieszyło, bo kiedyś żartowałem, że aplikacja powinna na starcie alokować 64k i się tego trzymać do końca.

      Rozważ to pisząc sewer, lub bazę danych. Wtedy pewnie masz zarządzanie dużą ilością stringów. Rozważ to pisząc coś na kontroler, gdzie używasz za dużo dynamicznej pamięci i masz ciągle problemy z fragmentacją. Przez co alokator nie może znaleźć odpowiednio długiego pustego obszaru. Zobacz jakie problemy mieli i mają twórcy przeglądarek. Pewnie rozbudowane aplikacje na stację roboczą mają podobnie. To wymaga zmiany podejścia do zarządzania pamięcią.

      Pod Linuksem to chyba sprawa sztuczki w glibc, który ma własny alokator i leniwe zwracanie pamięci do jądra. Ale też były problemy skoro proszono o huge pages i rozwiązania z brakiem jawnej ochrony.

      Jeśli chodzi o budowę gier. Dzisiaj to jest podzielone między GPU i CPU. W najbardziej wydajnych rozwiązaniach CPU pewnie przetwarza ciągle jakąś strukturę tablic z atrybutami. Co jakiś czas się ją sortuje, żeby zadowolić przewidywanie rozgałęzień. GPU pewnie możliwie dużo z tego stara się wizualizować na podstawie atrybutów i własnych danych.

      • naurM
        link
        fedilink
        Polski
        arrow-up
        1
        ·
        edit-2
        3 months ago

        Ja nie mam problemu z tymi technikami, tylko ze sposóbem ich prezentacji jako “programowanie N+1”. Muratori nie zastrzegł, że to są techniki specyficzne dla gamedevu, embedded czy kodu high-performance.
        On po prostu wartościuje na zasadzie “jeśli piszesz tak kod, jesteś lepszym programistą”.

        W praktyce, dla 80% współczesnych developerów pracujących w “biznesowych” projektach, takie porady nie są przydatne. Co gorsze, mogą budować u ludzi poczucie winy, że nie potrafią tego wdrożyć w swoim kodzie i przejść na “wyższy poziom programowania”.
        Rzecz w tym, że takie projekty są optymalizowane pod kątem łatwej rozbudowy, a nie wyciśnięcia 100% możliwości ze specyficznego hardware’u.

        Niektóre z tych technik byłyby w wielu projektach wręcz szkodliwe. Zwracanie współdzielonego obiektu (stubu) w przypadku wyczerpania zasobów to prosta droga do utraty albo wycieku danych.

        Microsoft też zrobił później normalny alokator (mimalloc). Nic nie stało na przeszkodzie, żeby zaimplementować lepsze zarządzanie pamięcią w domyślnym runtime.
        Jeśli obawiali się, że popsuje to aplikacje, wystarczyło warunkowo włączać nową implementacją jakimś API albo zmienną środowiskową.
        Zresztą MS robił odważniejsze fikołki w tej bibliotece. Parę lat temu zmienili sposób alokacji pamięci z prywatnej sterty dla każdej biblioteki na współdzieloną przez cały proces.

        Z huge pages jeśli dobrze pamiętam chodziło raczej o wyczerpywanie TLB nadmierną liczbą stron i koszt obsługi pagefaultów.

        • サぺルOP
          link
          fedilink
          Polski
          arrow-up
          1
          ·
          3 months ago

          Bo on chce, żeby każdy program był traktowany jako wydajny. On chce, żeby programiści mieli poczucie winy. Bo zdaniem tego ruchu kod (jak kiedyś pisaliśmy) “ęnterprajs”, przenika na niższe warstwy. Przez co programy od których powinieneś oczekiwać więcej, oferują mniej, są powolne i zabierają dużo zasobów.

          Jeśli ktoś pracuje w “biznesowym” projekcie, niech koduje jak mu przełożony zagra. Niech się potem nie dziwi, że pół internetu szydzi z projektu w którym uczestniczy.

          Jeśli ktoś sili się na rzemiosło, powinien dać z siebie wszystko. Ale jeśli ktoś uważa, że powinien w rzemiośle stosować strategie biznesowe rozwoju oprogramowania, to raczej niszczy. Przecież są ludzie, którzy przekonują cały świat, że tak powinno być. Szydzą z ambitnych projektów. Szydzą z innych strategii rozwoju. Szydzą z innego podejścia do kontroli wersji.

          Zawsze byliśmy dumni z tego, że otwarty kod oferuje więcej możliwości. Bo to wtedy było rzemiosło.

          Pagefault odpowiada za wiele rzeczy.