Czy istnieją alternatywy dla modelu stosu + sterty + pamięci statycznej?

9

Wszystkie programy, które widziałem, organizują pamięć danych w jeden lub więcej stosów wywołań (zwykle o ustalonym rozmiarze, ale czasem nie), stertę i pamięć statyczną. Ostatnio dodano do tego również statyczne przechowywanie wątków lokalne.

Czy próbowano zorganizować układ pamięci danych w zupełnie inny sposób, np. Bez stosu wywołań? Lub uporządkować pamięć w inny sposób, który osiąga to samo?

ikh
źródło
Zależy od tego, co rozumiesz przez „stos”. Możesz umieścić ramki stosu wywołań na stosie (połączyć je ze wskaźnikami). Wtedy nie masz dedykowanego liniowego obszaru pamięci dla stosu, ale koncepcyjnie nadal masz stos wywołań.
i zależy od tego, co rozumiesz przez „ostatnio”. Myślę, że lokalna pamięć wątków jest tak stara jak wątki. Ale wcześniej był dostępny za pośrednictwem wywołań systemowych, a teraz nowsze języki dają ci bezpośredni dostęp do niego.
DXM,
Stos wywołań jest konieczny, ponieważ funkcje proceduralne muszą wiedzieć, kto je wywołał, aby mogły zwracać wyniki i kontynuować wykonywanie. Obecny mechanizm, jeśli robi to, jest w rzeczywistości dość tani pod względem cykli procesora, a przy przynajmniej x64 prawie wszystkie argumenty funkcji są przekazywane przez rejestry
James
2
Możesz znaleźć post Erica Lipperta pt. Dlaczego masz stos? zainteresowań. Jego głównym punktem jest to, że stos zapewnia wydajny, prosty sposób śledzenia lokalizacji pamięci. Omawia jedną alternatywę w kilku znacznie starszych postach, Continuation Passing Style .
Brian

Odpowiedzi:

8

Możesz cofnąć się i zobaczyć, skąd i dlaczego pochodzą te istniejące modele. Kiedy proces jest tworzony, otrzymuje się po prostu płaski obszar pamięci, który jest po prostu indeksowany od 0 do N. Ponieważ ten obszar pamięci (tutaj mówimy o pamięci RAM) jest wspierany przez dedykowany sprzęt i niektóre fantazyjne półprzewodniki, zdarza się, że jest dość szybki, ale to nie jedyny w swoim rodzaju. Inne urządzenia, takie jak dyski twarde, są w gruncie rzeczy tym samym, płaską przestrzeń adresowaną przez indeks, ale o wiele rzędów wielkości wolniejszą.

Powodem istnienia „sterty” jest to, że dla każdej aplikacji próba samodzielnego zarządzania wykorzystaniem pamięci RAM byłaby niepraktyczna. Dawno temu, dokładnie tak się stało, programiści zaplanowali z wyprzedzeniem dokładnie to, do czego będzie używana każda lokalizacja pamięci RAM. Gdy oprogramowanie stało się bardziej złożone, ktoś powiedział: czy nie byłoby miło, gdybym mógł przejść do jakiejś czarnej skrzynki i powiedzieć „Potrzebuję 10 bajtów, więc daj mi” i nie martwić się o wszystkie skomplikowane szczegóły dotyczące tego, gdzie i jak te 10 bajtów pochodzą lub w jaki sposób są odzyskiwane. Taka jest kupa, tak naprawdę nie staje się bardziej podstawowa.

Za każdym razem, gdy tworzony jest wątek, istnieją pewne struktury danych (i stos), które są pozyskiwane przy użyciu tej samej „operacji gimme”, którą właśnie opisałem. Stos prawie powszechnie używany, ponieważ idealnie pasuje do ramek stosów wywołań funkcji i ich natury LIFO. Teoretycznie każde wywołanie funkcji i zmienne lokalne mogą być przydzielone na stercie, ale byłoby to po prostu zbyt drogie, w porównaniu z zaledwie kilkoma instrukcjami asemblacji potrzebnymi do aktualizacji rejestru wskaźnika stosu (ESP na x86).

Lokalna pamięć wątków (TLS) jest również budowana na szczycie stosu. Gdy wątek jest tworzony, jako część podróży na stertę w celu alokacji pamięci dla struktur zarządzania, oddzielna przestrzeń dla TLS jest również przydzielana ze sterty.

Tak więc ostatecznie wszystko, co naprawdę masz, to ogólny przydział pamięci (tj. Sterta), a wszystko inne to wyspecjalizowana forma. Innymi słowy, jeśli chcesz zrezygnować z jakiegoś aspektu „Chcę przeznaczyć tyle (lub tak mało), ile chcę, zachowaj to tak długo, jak chcę i za darmo, kiedy chcę”, możesz uniknąć handlu wyłącza ogólny rozdzielacz sterty dla innego modelu, który oferuje prędkość, ale kosztem innych ograniczeń.

Weź stos. Jest niesamowicie szybki w porównaniu ze stertą, ale dwa kompromisy to 1) nie kontrolujesz, kiedy pamięć zostanie zwolniona; zamiast tego po wyjściu z funkcji, cokolwiek przydzielono, zniknęło i 2), ponieważ stosy są zwykle ograniczone, powinieneś ostrożnie alokować duże ilości danych bezpośrednio na stosie.

Innym rodzajem „modelu pamięci” jest Virtual Memory Manager (VMM) oferowany przez prawie każdy główny system operacyjny za pośrednictwem wywołań systemowych. VMM jest bardzo podobny do sterty w tym sensie, że możesz poprosić o dowolną ilość pamięci i zachować ją tak długo, jak chcesz. Ograniczeniem jest jednak to, że pamięć można alokować tylko w wielokrotnościach wielkości strony (np. 4KB), więc bezpośrednie użycie VMM spowodowałoby duże obciążenie w typowej aplikacji, która często alokuje 8-24 bajtów na raz. W rzeczywistości prawie każda implementacja sterty jest zbudowana na VMM specjalnie w celu umożliwienia bardzo ogólnego, niewyspecjalizowanego przydzielania małych bloków. Sterta trafia do VMM, gdy potrzebuje więcej pamięci, a następnie wykleja wiele małych fragmentów tej pamięci do aplikacji.

Jeśli masz aplikację, która wymaga alokacji dużych bloków, możesz rozważyć przejście bezpośrednio do VMM, chociaż niektóre sterty zawierają instrukcję if wewnątrz malloc (), a jeśli rozmiar bloku jest większy niż pewien próg, po prostu przechodzą do VMM dla Was.

Inną formą alokatorów zamiast bezpośredniego używania sterty byłyby pule. Pula to specjalistyczny alokator, w którym wszystkie bloki mają ten sam rozmiar. Pule (podobnie jak stos i TLS) są zbudowane na stercie lub VMM. Baseny są przydatne w miejscach, w których przeznaczasz dużo (miliony) krótkotrwałych, małych obiektów o tej samej wielkości. Pomyśl o usłudze sieciowej przetwarzającej przychodzące żądania. Każde żądanie klienta może spowodować przydzielenie tej samej N-bajtowej struktury do obsługi tego żądania. Kompromis z używaniem pul polega na tym, że każda pula obsługuje tylko jeden rozmiar bloku (ale można utworzyć wiele pul). Zaletą pul jest to, że ponieważ wszystkie obiekty mają taki sam rozmiar, nie wymaga skomplikowanej logiki. Zamiast tego, ilekroć potrzebujesz nowego bloku, po prostu daje ci ten, który został niedawno uwolniony.

I na koniec, pamiętaj o tym dysku twardym, o którym wspominałem. Możesz mieć model pamięci, który zachowuje się jak system plików i powiela to samo pojęcie pozycji katalogu i i-węzłów, aby umożliwić hierarchiczną alokację bloków danych, w których każdy blok danych jest adresowany ścieżką. To właśnie robi tmpfs .

Poza rzeczami, o których wspomniałem, jestem pewien, że istnieją inne bardziej wyspecjalizowane modele, ale w końcu, ponieważ wszystko opiera się na płaskiej przestrzeni adresowej (to jest do czasu, aż jakieś geneza wymyśli jakieś dziwne - $$ niepłaska przestrzeń ), wszystko wraca do tego ogólnego alokatora „gimme”, którym jest VMM lub sterta.

DXM
źródło
1

Jedyne przypadki, o których myślę, to wyspecjalizowany sprzęt, w którym wszystko może działać w ustalonych miejscach w pamięci. Prawie wszystko w obecnym modelu pamięci jest wymagane, jeśli chcesz w pełni elastycznych programów.

Bez stosu nie można mieć zmiennych lokalnych, stosów wywołań itp. Wszystko, co napiszesz do implementacji, będzie wyglądać podobnie do stosu.

Pamięć statyczna i sterty, które potencjalnie możesz upuścić dla niektórych aplikacji, ale znowu będziesz potrzebować ich z powrotem w takiej lub innej formie, aby zrobić coś bardziej zaawansowanego.

Więc wszystko, co wymyślisz, aby zastąpić jedną z tych trzech, ostatecznie będzie wyglądać podobnie jak jedna z tych trzech w końcu ...

Aby podejść do tego z innej strony, co możesz dodać, że jest nowy? Możesz potencjalnie argumentować, że takie elementy jak procesory graficzne / fizyczne / pamięci podręczne procesorów / etc są nową lokalizacją pamięci, ale tak naprawdę to tylko oddzielna instancja lub sposób na przyspieszenie dostępu do istniejących modeli.

... więc dopóki ktoś nie wymyśli jakiegoś gigantycznego skoku koncepcyjnego, myślę, że przez długi czas nie zobaczymy żadnych poważnych zmian w tym obszarze ...

Tim B.
źródło
4
Większość ludzi ma tendencję do zakładania, że ​​obecny sposób jest najlepszym / jedynym sposobem, a jeśli otrzyma pustą tablicę, po prostu skopiuje to, co już istnieje. W innych ludzie są tymi, którzy rzeczywiście wyprzedzeniem postęp technologiczny. Nie mówiąc już, że osobiście znam jakieś poważne konkurencyjne modele (chyba, że ​​liczysz komputery kwantowe), ale twierdzenie, że wszystko, co można by wymyślić, wyglądałoby tak samo, jak to, co już istnieje, jest zasadniczo formą okrągłego rozumowania.
Aaronaught,
@Aaronaught: druga strona twojego argumentu polega na tym, że inni ludzie spędzają mnóstwo czasu, pieniędzy i energii, myśląc nieszablonowo i za każde 1000 (może znacznie więcej) z nich można w końcu posunąć postęp technologiczny, podczas gdy reszta nic nie osiągnie . Podczas gdy pierwsza grupa, którą można by uznać za bardziej praktyczną, bierze te istniejące modele bez zmian i wprowadza na nie innowacje :)
DXM,
@aronaught Myślę, że opisałem to słowem „dopóki ktoś nie wymyśli jakiegoś gigantycznego skoku koncepcyjnego”;) Jeśli masz lepszy alternatywny model, możesz go zasugerować ... jeśli nie, to trochę hipokryzją jest narzekać „niektórzy ludzie”, gdy jesteś jednym z nich :)
Tim B
1
@DXM: Więc? Czy powiedziałem, że wszyscy powinniśmy poświęcać czas na badanie nowych modeli pamięci? Wskazałem tylko (istotną) wadę twierdzenia, że ​​człowiek może wymyślić tylko rzeczy, które zostały już wynalezione.
Aaronaught
Twierdzenie, którego nigdy nie złożyłem ...
Tim B,