Za każdym razem, gdy masz aplikację, która ma ścieżkę krytyczną wymagającą dużej wydajności, powinieneś się martwić, jak traktujesz pamięć. Większość aplikacji klienckich użytkowników końcowych nie należy do tej kategorii, ponieważ są one oparte głównie na zdarzeniach, a większość zdarzeń pochodzi z interakcji z użytkownikiem i nie ma tak wielu (jeśli w ogóle) ograniczeń wydajności.
Jednak wiele oprogramowania zaplecza powinno skupić się na sposobie obsługi pamięci, ponieważ wiele z tego oprogramowania można skalować w celu obsługi większej liczby klientów, większej liczby transakcji, większej liczby źródeł danych ... Po uruchomieniu przekraczając granice, możesz zacząć analizować, w jaki sposób pamięć użytkowników oprogramowania i pisać niestandardowe schematy alokacji dostosowane do Twojego oprogramowania, zamiast polegać na całkowicie ogólnym alokatorze pamięci, który został napisany, aby obsłużyć każdy możliwy do wyobrażenia przypadek użycia.
Aby podać kilka przykładów ... w mojej pierwszej firmie pracowałem nad pakietem Historian, oprogramowaniem odpowiedzialnym za gromadzenie / przechowywanie / archiwizację danych kontroli procesu (pomyśl o fabryce, elektrowni jądrowej lub rafinerii ropy naftowej z 10 milionami czujników, będziemy przechowywać te dane). Za każdym razem, gdy analizowaliśmy jakiekolwiek wąskie gardło wydajności, które uniemożliwiało Historianowi przetwarzanie większej ilości danych, przez większość czasu problem polegał na sposobie obsługi pamięci. Dokładamy wszelkich starań, aby upewnić się, że malloc / free nie są nazywane, chyba że są absolutnie konieczne.
W mojej obecnej pracy pracuję nad cyfrowym rejestratorem wideo i pakietem analizy. Przy 30 fps każdy kanał odbiera ramkę wideo co 33 milisekundy. Na sprzedawanym przez nas sprzęcie możemy z łatwością nagrać 100 kanałów wideo. To kolejny przypadek, aby upewnić się, że na ścieżce krytycznej (połączenie sieciowe => przechwytywanie komponentów => oprogramowanie do zarządzania rejestratorem => komponenty pamięci => dysk) nie ma żadnych dynamicznych przydziałów pamięci. Mamy niestandardowy alokator ramek, który zawiera koszyki buforów o stałej wielkości i używa LIFO do ponownego wykorzystania wcześniej przydzielonych buforów. Jeśli potrzebujesz 600 KB pamięci, możesz skończyć z buforem 1024 KB, który marnuje miejsce, ale ponieważ jest on specjalnie dostosowany do naszego użytku, gdzie każda alokacja jest bardzo krótkotrwała, działa bardzo dobrze, ponieważ bufor jest używany,
W typie aplikacji, które opisałem (przenoszenie dużej ilości danych z A do B i obsługa dużej liczby żądań klientów) przechodzenie na stos i z powrotem jest głównym źródłem wąskich gardeł wydajności procesora. Ograniczenie fragmentacji sterty do minimum jest dodatkową korzyścią, jednak, o ile mogę powiedzieć, większość współczesnych systemów operacyjnych już implementuje stosy o niskiej fragmentacji (przynajmniej wiem, że Windows to robi i mam nadzieję, że inni też to zrobią). Osobiście od ponad 12 lat pracując w tego typu środowiskach dość często widziałem problemy z wykorzystaniem procesora związane ze stertą, podczas gdy nigdy nie widziałem systemu, który faktycznie cierpiał na rozdrobnioną stertę.