Czy zarządzanie pamięcią w programowaniu staje się nieistotne?
Zarządzanie pamięcią (lub sterowanie) jest właściwie głównym powodem, dla którego używam C i C ++.
Pamięć jest teraz stosunkowo tania.
Nie szybka pamięć. Nadal patrzymy na niewielką liczbę rejestrów, coś w rodzaju pamięci podręcznej 32 KB dla L1 na i7, 256 KB dla L2 i 2 MB dla L3 / rdzenia. To mówi:
Jeśli nie mówimy o platformach docelowych z surowymi ograniczeniami pamięci operacyjnej (tj. Systemy wbudowane i tym podobne), czy użycie pamięci powinno być problemem przy wyborze dzisiaj języka ogólnego przeznaczenia?
Zużycie pamięci na poziomie ogólnym, może nie. Jestem trochę niepraktyczny, ponieważ nie podoba mi się pomysł notatnika, który zabiera, powiedzmy, 50 megabajtów pamięci DRAM i setki megabajtów miejsca na dysku twardym, mimo że mam to do stracenia i więcej. Jestem tu od dłuższego czasu i wydaje mi się to dziwne i trochę obrzydliwe, gdy widzę, że tak prosta aplikacja zajmuje stosunkowo dużo pamięci na to, co powinno być możliwe w przypadku kilobajtów. To powiedziawszy, mógłbym być w stanie żyć ze sobą, gdybym spotkał się z taką rzeczą, jeśli nadal byłaby miła i wrażliwa.
Powodem, dla którego zarządzanie pamięcią jest dla mnie ważne w mojej dziedzinie, nie jest ogólne zmniejszenie zużycia pamięci. Setki megabajtów pamięci nie musi spowalniać aplikacji w żaden nietrywialny sposób, jeśli żadna z tych pamięci nie jest często uzyskiwana (np. Tylko po kliknięciu przycisku lub innej formie wprowadzania danych przez użytkownika, co jest niezwykle rzadkie, chyba że użytkownik mówią o koreańskich graczach Starcraft, którzy mogą kliknąć przycisk milion razy na sekundę).
Powodem, dla którego jest to ważne w mojej dziedzinie, jest ścisłe połączenie pamięci, które jest bardzo często dostępne (np. Zapętlanie każdej ramki) w tych krytycznych ścieżkach. Nie chcemy przegapić pamięci podręcznej za każdym razem, gdy uzyskujemy dostęp tylko do jednego z miliona elementów, do których wszystkie muszą być dostępne w pętli przy każdej klatce. Kiedy przenosimy pamięć w dół hierarchii od wolnej pamięci do szybkiej pamięci w dużych porcjach, powiedzmy 64 bajtowe linie pamięci podręcznej, naprawdę pomocne jest, jeśli te 64 bajty zawierają odpowiednie dane, jeśli możemy zmieścić wiele elementów o wartości danych w tych 64 bajtach, i jeśli nasze wzorce dostępu są takie, że wykorzystamy to wszystko przed eksmisją danych.
Te często używane dane dla miliona elementów mogą zajmować jedynie 20 megabajtów, mimo że mamy gigabajty. Nadal robi różnicę w liczbie klatek na sekundę zapętlającą się wokół tych danych, co każdą narysowaną klatkę, jeśli pamięć jest szczelna i blisko siebie, aby zminimalizować straty pamięci podręcznej, i tam właśnie zarządzanie / kontrola pamięci jest tak przydatna. Prosty przykład wizualny na kuli z kilkoma milionami wierzchołków:
Powyższe jest w rzeczywistości wolniejsze niż moja zmienna wersja, ponieważ testuje trwałą reprezentację struktury danych siatki, ale z tym bokiem starałem się osiągnąć takie szybkości klatek nawet przy połowie tych danych (co prawda sprzęt był szybszy od moich zmagań ), ponieważ nie rozumiem, jak minimalizować błędy pamięci podręcznej i zużycie pamięci dla danych siatki. Siatki to jedne z najtrudniejszych struktur danych, z którymi miałem do czynienia w tym względzie, ponieważ przechowują tak wiele współzależnych danych, że muszą być zsynchronizowane, takie jak wielokąty, krawędzie, wierzchołki, tyle map tekstur, ile użytkownik chce dołączyć, masy kości, mapy kolorów, zestawy wyboru, cele przekształcenia, grubości krawędzi, materiały wielokątów itp. itd. itd.
W ciągu ostatnich kilku dekad zaprojektowałem i wdrożyłem wiele systemów siatkowych, a ich prędkość była często bardzo proporcjonalna do wykorzystania pamięci. Mimo że pracuję z tym, o wiele więcej pamięci niż na początku, moje nowe systemy siatki są ponad 10 razy szybsze niż mój pierwszy projekt (prawie 20 lat temu) i w dużym stopniu, ponieważ zużywają około 1/10 pamięć. Najnowsza wersja wykorzystuje nawet indeksowaną kompresję do wtłoczenia jak największej ilości danych, i pomimo narzutu związanego z przetwarzaniem dekompresji, kompresja faktycznie poprawiła wydajność, ponieważ znowu mamy tak mało cennej szybkiej pamięci. Mogę teraz dopasować milion siatki wielokątów ze współrzędnymi tekstury, bigowaniem krawędzi, przypisaniami materiałów itp. Wraz z indeksem przestrzennym dla tego w około 30 megabajtach.
Oto modyfikowalny prototyp z ponad 8 milionami czworokątów i wielozadaniowym schematem podziału na i3 z GF 8400 (to było kilka lat temu). Jest szybszy niż moja niezmienna wersja, ale nie jest używana w produkcji, ponieważ uważam, że wersja niezmienna jest o wiele łatwiejsza w utrzymaniu, a wydajność nie jest taka zła. Zauważ, że szkielet nie wskazuje faset, ale łatki (druty są w rzeczywistości krzywymi, w przeciwnym razie cała siatka byłaby jednolita czarna), chociaż wszystkie punkty w elemencie są modyfikowane przez pędzel.
Tak czy inaczej, chciałem tylko pokazać niektóre z powyższych powyżej, aby pokazać konkretne przykłady i obszary, w których zarządzanie pamięcią jest tak pomocne, a także mam nadzieję, że ludzie nie myślą, że po prostu mówię poza moim tyłkiem. Często denerwuję się, gdy ludzie mówią, że pamięć jest tak obfita i tania, ponieważ chodzi o wolną pamięć, taką jak DRAM i dyski twarde. Jest wciąż tak mały i tak cenny, gdy mówimy o szybkiej pamięci, a wydajność dla naprawdę krytycznych (tj. Często spotykanych przypadków, nie dla wszystkiego) ścieżek odnosi się do grania z tak małą ilością szybkiej pamięci i wykorzystywania jej tak skutecznie, jak to możliwe .
W przypadku tego rodzaju rzeczy bardzo pomocna jest praca z językiem, który pozwala na przykład projektować obiekty wysokiego poziomu, takie jak C ++, jednocześnie zachowując te obiekty w jednej lub kilku sąsiadujących tablicach z gwarancją, że pamięć wszystkie takie obiekty będą reprezentowane w sposób ciągły i bez niepotrzebnego narzutu pamięci na obiekt (np. nie wszystkie obiekty wymagają refleksji lub wirtualnej wysyłki). Kiedy faktycznie przenosisz się do obszarów krytycznych pod względem wydajności, staje się to wzrostem wydajności, aby mieć taką kontrolę nad pamięcią, powiedzmy, majstrowanie przy pulach obiektów i używanie prymitywnych typów danych, aby uniknąć narzutu obiektu, kosztów GC i aby często uzyskiwać dostęp do pamięci razem przylegające.
Tak więc zarządzanie pamięcią / kontrola (lub jej brak) jest w moim przypadku dominującym powodem wyboru języka, który najbardziej produktywnie pozwala mi rozwiązywać problemy. Zdecydowanie piszę swoją część kodu, który nie jest krytyczny pod względem wydajności, i do tego zwykle używam Lua, którą dość łatwo można osadzić w C.