Zwykle nie mam problemu z podjęciem decyzji, czy niektóre dane muszą być globalne, statyczne czy na stosie (tutaj nie ma alokacji dynamicznej, więc nie ma zastosowania sterty). Przeczytałem również kilka pytań / odpowiedzi, takich jak to, ale moje pytanie jest bardziej szczegółowe, ponieważ wymaga ogromnej ilości danych, ogromnych w porównaniu do pamięci systemowej.
Pracuję nad istniejącym kodem, który próbuję ulepszyć (projekt, możliwe problemy, wydajność itp.). Ten kod działa na starym 8-bitowym MCU z tylko 4KB pamięci RAM . W tym kodzie mam do czynienia z tablicą o wielkości prawie 1 KB (tak, 1 KB w systemie RAM 4KB ). Używany jest każdy bajt tej tablicy, to nie jest pytanie. Problem polega na tym, że tablica ta jest statyczną tablicą w pliku, w którym została zadeklarowana, więc jej cykl życia jest taki sam jak w programie (tzn. Można ją uznać za nieskończoną).
Jednak po przeczytaniu kodu właśnie dowiedziałem się, że ta tablica nie potrzebuje nieskończonego cyklu życia, jest zbudowana i obsługiwana w sposób w pełni proceduralny, więc powinniśmy być w stanie zadeklarować ją tylko w funkcji, w której jest używana, w ten sposób byłby na stosie, a zatem zapisalibyśmy ten 1KB pamięci RAM.
Teraz pytanie: czy to byłby dobry pomysł? Z punktu widzenia projektu, jeśli nie potrzebuje nieskończonego / globalnego cyklu życia, należy do stosu. Ale hej, to 1KB na 4KB, czy nie ma żadnej wady przydzielania 25% pamięci RAM w ten sposób? (może to być 50% lub więcej stosu)
Czy ktoś może podzielić się doświadczeniem z tego rodzaju sytuacją, czy może ktoś pomyśli o jakimkolwiek ważnym celu, aby nie umieszczać tej tablicy na stosie? Szukam wad technicznych, a także komentarzy do projektu.
Jedyne, co mam świadomość, to to, że muszę upewnić się, że faktycznie mam 1 KB wolnego stosu, wchodząc w tę funkcję. Może to wszystko, co muszę zrobić, może nie.
Odpowiedzi:
Tak, i to jest silne ograniczenie. Lepiej statystycznie upewnij się, że na stosie jest tak dużo miejsca. Jeśli kod jest mały, jeśli używasz najnowszego GCC do kompilacji kodu, zobacz to .
BTW, niektóre tanie mikroprocesory mogą wykorzystywać „dużą” ramkę wywoławczą bardziej kosztowną niż „normalną” (np. Ponieważ ich zestaw instrukcji wolałby przesunięcie o jeden bajt od wskaźnika stosu). YMMV.
Ponadto, jeśli kodujesz w C i uważasz, że twoja duża tablica może zostać ponownie wykorzystana do innych celów, możesz rozważyć utworzenie jej elementu unii (z globalną zmienną
union
typu). Tak, to jest dość brzydkie.Alternatywnie, możesz rozważyć kodowanie jakiegoś prymitywnego alokatora sterty odpowiedniego dla twojej aplikacji (i może mieć interfejs API inny niż
malloc
&free
...).źródło
gcc -flto -Os
? ) i możesz zyskać trochę pamięci ...Ludzie często zachowują ostrożność przy dużym stosie, ponieważ rośnie on w pamięci RAM i zastępuje wartości zmiennych, co prowadzi do niewyjaśnionego zachowania. Jest jeszcze gorzej, ponieważ musisz znać najniższy możliwy adres wskaźnika stosu i odjąć rozmiar, który chcesz przydzielić, wchodząc w procedurę.
To wszystko jest zadaniem zarządzania pamięcią sprzętową (powinno generować pułapki lub błędy, gdy nastąpi przepełnienie stosu) lub kompilatora, biorąc pod uwagę, że ma on funkcje tego rodzaju analizy.
W przeciwnym razie możesz robić, co chcesz z pamięcią RAM.
źródło
Jak wskazano w poprzednich odpowiedziach, najpierw poleciłbym pozostawić tablicę statyczną, jeśli pasuje do pamięci. W większości przypadków znacznie ważniejsze jest, aby mieć deterministyczny ślad pamięci, nawet jeśli oznacza to, że „marnujesz” pamięć na zmienne, które nie są używane przez cały czas. Umieszczenie dużych tablic na stosie zdecydowanie zbyt łatwo go wysadzi, a przepełnienie stosu może powodować trudne do znalezienia i trudne do odtworzenia problemy (jeśli nie możesz użyć MMU do ochrony stosu).
Sugestia dzielenia bloku z niektórymi innymi danymi ze związkiem jest poprawna przez IMO, chociaż może również być źródłem trudnych do znalezienia problemów, jeśli znajdziesz w nim nieprawidłowe zmienne.
Jeśli kończy Ci się pamięć i desperacko potrzebujesz stworzyć zmienne o krótszym czasie życia, aby ją udostępnić, zanim przeniesiesz tablicę na stos, rozważę dodanie dynamicznej alokacji pamięci, nawet jeśli ma ona swoje wady. W tym przypadku może to nie być odpowiedź, ponieważ tablica brzmi dość duża w porównaniu do dostępnej pamięci.
źródło
Masz jeszcze jedną opcję, jeśli masz jakąś pamięć flash. Możesz obniżyć prędkość dostępu do pamięci RAM, przechowując dane w pamięci flash oraz tam odczytując i wyszukując. Będziesz musiał załadować tylko jeden rekord na raz do pamięci RAM. Będzie to nieco bardziej skomplikowane, jeśli będziesz w stanie zaktualizować rekordy. Będziesz potrzebował segmentowego mechanizmu wyrównującego zużycie. Zrobiłem to w przeszłości i załączyłem indeks, aby przyspieszyć dostęp.
źródło
Zwłaszcza podczas pracy z systemami osadzonymi chcesz, aby jak najwięcej potencjalnych awarii miało miejsce w czasie kompilacji i nic nigdy nie zawiedzie w czasie wykonywania (choć fajnie, jeśli moglibyśmy to osiągnąć ...).
Tworzenie dużych tablic, które mogą być potrzebne w dowolnych stanach programu przydzielonego statycznie, robi dokładnie to - Linker ostatecznie ostrzeże Cię, że „to nie pasuje do pamięci RAM”, podczas gdy przydzielenie stosu po prostu spowodowałoby awarię programu z trudnym do debugowania stosem przelewy.
źródło