Wydaje mi się, że wszystko, co można zrobić za pomocą stosu, można zrobić za pomocą stosu, ale nie wszystko, co można zrobić za pomocą stosu, można wykonać za pomocą stosu. Czy to jest poprawne? Zatem dla uproszczenia, a nawet jeśli stracimy trochę wydajności przy niektórych obciążeniach, czy nie lepiej byłoby po prostu użyć jednego standardu (tj. Sterty)?
Pomyśl o kompromisie między modułowością a wydajnością. Wiem, że nie jest to najlepszy sposób na opisanie tego scenariusza, ale ogólnie wydaje się, że prostota zrozumienia i projektowania może być lepszą opcją, nawet jeśli istnieje potencjał do lepszej wydajności.
Odpowiedzi:
Sterty są złe w szybkim przydzielaniu i zwalnianiu pamięci. Jeśli chcesz pobrać wiele niewielkich ilości pamięci przez ograniczony czas, kupa nie jest najlepszym wyborem. Stos z bardzo prostym algorytmem alokacji / dezalokacji, naturalnie przoduje w tym (nawet bardziej, jeśli jest wbudowany w sprzęt), dlatego ludzie używają go do przekazywania argumentów do funkcji i przechowywania zmiennych lokalnych - najbardziej ważnym minusem jest to, że ma ograniczoną przestrzeń, a więc trzymanie w niej dużych obiektów lub próba użycia go do obiektów długowiecznych, są złymi pomysłami.
Całkowite pozbycie się stosu w celu uproszczenia języka programowania jest niewłaściwym sposobem IMO - lepszym rozwiązaniem byłoby wyodrębnienie różnic, pozwól kompilatorowi dowiedzieć się, jakiego rodzaju pamięci użyć, podczas gdy programista składa razem wyższe- konstrukcje poziomów, które są bliższe myśleniu ludzi - i tak naprawdę robią to języki wysokiego poziomu, takie jak C #, Java, Python itp. Oferują one prawie identyczną składnię dla obiektów przydzielanych na stercie i operacji podstawowych przydzielanych na stosie („typy referencyjne” vs. „typy wartości” w języku .NET), albo w pełni przejrzyste, albo z kilkoma różnicami funkcjonalnymi, które należy zrozumieć, aby używać języka poprawnie (ale tak naprawdę nie musisz wiedzieć, jak stos i stos działają wewnętrznie).
źródło
Mówiąc najprościej, stos nie jest odrobiną wydajności. Jest setki lub tysiące razy szybszy niż kupa. Ponadto większość nowoczesnych maszyn ma obsługę sprzętową stosu (jak x86) i tej funkcji sprzętowej, np. Stosu wywołań, nie można usunąć.
źródło
Nie
Obszar stosu w C ++ jest niezwykle szybki w porównaniu. Podejrzewam, że żaden doświadczony programista C ++ nie byłby otwarty na wyłączenie tej funkcjonalności.
Dzięki C ++ masz wybór i masz kontrolę. Projektanci nie byli szczególnie skłonni do wprowadzania funkcji, które zwiększyły czas wykonania lub przestrzeń.
Korzystam z tego wyboru
Jeśli chcesz zbudować bibliotekę lub program, który wymaga dynamicznego przydzielania każdego obiektu, możesz to zrobić za pomocą C ++. Wykonałby się stosunkowo wolno, ale można by wtedy mieć taką „modułowość”. Dla reszty z nas modułowość jest zawsze opcjonalna, wprowadzaj ją w razie potrzeby, ponieważ oba są wymagane do dobrych / szybkich wdrożeń.
Alternatywy
Istnieją inne języki, które wymagają utworzenia pamięci dla każdego obiektu na stercie; jest dość powolny, tak że kompromituje projekty (programy ze świata rzeczywistego) w sposób gorszy niż konieczność uczenia się obu (IMO).
Oba są ważne, a C ++ zapewnia efektywne wykorzystanie mocy w obu scenariuszach. Powiedziawszy to, język C ++ może nie być idealny do twojego projektu, jeśli te czynniki w twoim OP są dla ciebie ważne (na przykład, przeczytaj o językach wyższego poziomu).
źródło
W rzeczywistości wydajność może być znaczna!
Jak zauważyli inni, stosy są niezwykle wydajną strukturą do zarządzania danymi, która jest zgodna z zasadami LIFO (ostatni na pierwszy raz). Alokacja / zwalnianie pamięci na stosie jest zwykle tylko zmianą rejestru w CPU. Zmiana rejestru jest prawie zawsze jedną z najszybszych operacji, jakie procesor może wykonać.
Sterta jest zwykle dość złożoną strukturą danych, a przydzielanie / zwalnianie pamięci zajmie wiele instrukcji, aby wykonać całą powiązaną księgowość. Co gorsza, w typowych implementacjach każde wywołanie do pracy ze stertą może spowodować wywołanie systemu operacyjnego. Wywołania systemu operacyjnego są bardzo czasochłonne! Program zwykle musi przełączać się z trybu użytkownika do trybu jądra, a gdy to się stanie, system operacyjny może zdecydować, że inne programy mają pilniejsze potrzeby i że Twój program będzie musiał poczekać.
źródło
Simula wykorzystała kupę do wszystkiego. Umieszczenie wszystkiego na stosie zawsze wywołuje jeszcze jeden poziom pośredni dla zmiennych lokalnych, a to wywiera dodatkową presję na Garbage Collector (musisz wziąć pod uwagę, że Garbage Collectors naprawdę wtedy zasysali). Po części dlatego Bjarne wynalazł C ++.
źródło
Stosy są niezwykle wydajne w przypadku danych LIFO, takich jak na przykład metadane związane z wywołaniami funkcji. Stos wykorzystuje również nieodłączne cechy konstrukcyjne procesora. Ponieważ wydajność na tym poziomie ma fundamentalne znaczenie dla niemal wszystkiego innego w procesie, przyjęcie tego „małego” trafienia na tym poziomie będzie się bardzo rozpowszechniać. Ponadto pamięć sterty może być przenoszona przez system operacyjny, co byłoby śmiertelnie niebezpieczne dla stosów. Chociaż stos może być zaimplementowany w stercie, wymaga narzutu, który wpłynie dosłownie na każdy kawałek procesu na najbardziej szczegółowym poziomie.
źródło
„efektywny” pod względem pisania kodu, ale na pewno nie pod względem wydajności oprogramowania. Przydziały stosu są zasadniczo wolne (potrzeba tylko kilku instrukcji maszyny, aby przenieść wskaźnik stosu i zarezerwować miejsce na stosie dla zmiennych lokalnych).
Ponieważ alokacja stosu nie zajmuje prawie czasu, alokacja nawet na bardzo wydajnym stosie będzie 100k (jeśli nie 1M +) razy wolniejsza.
Teraz wyobraź sobie, ile lokalnych zmiennych i innych struktur danych używa typowa aplikacja. Każde małe „i”, którego używasz jako licznika pętli, jest przydzielane milion razy wolniej.
Pewnie, jeśli sprzęt jest wystarczająco szybki, możesz napisać aplikację, która używa tylko sterty. Ale teraz wyobrażam sobie, jaką aplikację możesz napisać, jeśli skorzystasz ze sterty i użyjesz tego samego sprzętu.
źródło
Być może interesuje Cię „Śmieci jest szybkie, ale stos jest szybszy”.
http://dspace.mit.edu/bitstream/handle/1721.1/6622/AIM-1462.ps.Z
Jeśli przeczytam go poprawnie, ci faceci zmodyfikowali kompilator C, aby przydzielić „stos ramek” na stosie, a następnie za pomocą odśmiecania pamięci oddzielić ramki zamiast usuwać stos.
„Ramki stosu” przydzielone stosowi zdecydowanie przewyższają „ramki stosu” przydzielone do stosu.
źródło
Jak stos wywołań będzie działał na stercie? Zasadniczo musiałbyś przydzielić stos na stosie w każdym programie, więc dlaczego nie masz sprzętu OS + dla ciebie?
Jeśli chcesz, aby rzeczy były naprawdę proste i wydajne, po prostu daj użytkownikowi swoją pamięć i pozwól sobie z tym poradzić. Oczywiście nikt nie chce wdrażać wszystkiego samodzielnie i dlatego mamy stos i stos.
źródło
Wymagane są zarówno stos, jak i stos. Są używane w różnych sytuacjach, na przykład:
Zasadniczo mechanizmów nie można w ogóle porównać, ponieważ tak wiele szczegółów jest różnych. Jedyną wspólną cechą jest to, że oboje w jakiś sposób radzą sobie z pamięcią.
źródło
Nowoczesne komputery mają kilka warstw pamięci podręcznej oraz duży, ale wolny system pamięci głównej. Można uzyskać dziesiątki dostępów do najszybszej pamięci podręcznej w czasie wymaganym do odczytania lub zapisu jednego bajtu z głównego systemu pamięci. Zatem dostęp do jednej lokalizacji tysiąc razy jest znacznie szybszy niż dostęp do 1000 (lub nawet 100) niezależnych lokalizacji raz. Ponieważ większość aplikacji wielokrotnie przydziela i zwalnia małe ilości pamięci w górnej części stosu, lokalizacje na górze stosu są wykorzystywane i ponownie wykorzystywane ogromną ilość, tak że ogromna większość (99% + w typowej aplikacji) dostęp do stosu może być obsługiwany przy użyciu pamięci podręcznej.
Natomiast jeśli aplikacja będzie wielokrotnie tworzyć i porzucać obiekty sterty w celu przechowywania informacji o kontynuacji, każda wersja każdego obiektu stosu, który kiedykolwiek został utworzony, musiałaby zostać zapisana w pamięci głównej. Nawet jeśli ogromna większość takich obiektów byłaby całkowicie bezużyteczna, zanim procesor zechciałby ponownie przetworzyć strony pamięci podręcznej, w których zaczęły, procesor nie byłby w stanie tego wiedzieć. W rezultacie procesor musiałby tracić dużo czasu na powolne zapisywanie w pamięci bezużytecznych informacji. Nie do końca przepis na szybkość.
Inną rzeczą do rozważenia jest to, że w wielu przypadkach warto wiedzieć, że odwołanie do obiektu przekazane do procedury nie będzie używane po jej zakończeniu. Jeśli parametry i zmienne lokalne są przekazywane przez stos, a kontrola kodu procedury ujawnia, że nie zachowuje ona kopii przekazywanego odwołania, wówczas kod wywołujący procedurę może być pewien, że jeśli nie będzie zewnętrznego odwołania do obiekt istniał przed wywołaniem, żaden nie będzie istniał później. Natomiast jeśli parametry byłyby przekazywane przez obiekty sterty, pojęcia takie jak „po zwróceniu procedury” stają się nieco bardziej mgliste, ponieważ gdyby kod zachował kopię kontynuacji, procedura mogłaby „wrócić” więcej niż raz po pojedyncze połączenie.
źródło