Nie wiem, z góry mojego kapelusza, o jakiejkolwiek puszkowanej implementacji, z której można korzystać. Jednak wydaje się, że nie jest to szczególnie trudne do zrealizowania, używając tylko kontenerów odmiany ogrodowej w standardowej bibliotece C++.
Polecam proste podejście, które używa dwóch std::map
s i jednego std::multimap
. Powiedzmy, że bufaddr_t
jest nieprzezroczystą liczbą całkowitą, która reprezentuje adres w zewnętrznym buforze. Ponieważ mówimy o buforze 16-gigabitowym, musi to być adres 64-bitowy:
typedef uint64_t memblockaddr_t;
Podobnie jak rozmiar przydzielonego bloku.
typedef uint64_t memblocksize_t;
Można by, jak sądzę, należy użyć coś innego dla memblockaddr_t
, dopóki nieprzezroczysty typ danych ma ścisły słaby zamówieniu.
Pierwsza część jest łatwa. Śledzenie wszystkich przydzielonych bloków:
std::map<memblockaddr_t, memblocksize_t> allocated;
Po pomyślnym przydzieleniu fragmentu pamięci w buforze zewnętrznym należy go wstawić tutaj. Jeśli chcesz zwolnić część pamięci, wyszukaj tutaj rozmiar przydzielonego bloku i usuń wpis mapy. Wystarczająco proste.
Ale to oczywiście nie jest cała historia. Teraz musimy śledzić dostępne, nieprzydzielone bloki pamięci. Zróbmy to w ten sposób:
typedef std::multimap<memblocksize_t, memblockaddr_t> unallocated_t;
unallocated_t unallocated;
std::map<memblockaddr_t, unallocated_t::iterator> unallocated_lookup;
unallocated
to zbiór wszystkich nieprzypisanych kawałki w buforze zewnętrznego zamocowanej przez wielkość porcji. Kluczem jest rozmiar porcji.Tak więc, kiedy potrzebujesz przydzielić kawałek pamięci o określonym rozmiarze, możesz po prostu użyć metody lower_bound()
(lub upper_bound()
, jeśli wolisz), aby od razu znaleźć pierwszą porcję, której rozmiar jest najmniejszy, jak chcesz przydzielić.
A ponieważ możesz oczywiście mieć wiele kawałków o tym samym rozmiarze, unallocated
musi być std::multimap
.
Ponadto, unallocated_lookup
jest mapą wpisaną przez adres każdej nieprzydzielonej porcji, która daje iterator dla wpisu tego kawałka w unallocated
. Dlaczego tego potrzebujesz, stanie się jasne za chwilę.
Tak więc:
Inicjalizacja nowy, całkowicie nieprzydzielone bufor z jednej pozycji:
memblockaddr_t beginning=0; // Or, whatever represents the start of the buffer.
auto p=unallocated.insert(std::make_pair(BUFFER_SIZE, beginning)).first;
unallocated_lookup.insert(std::make_pair(beginning, p));
następnie:
przydzielić bloku użyć LOWER_BOUND()/UPPER_BOUND() podejście, jak wspomniałem powyżej, aby znaleźć pierwszy nieprzydzielony fragment, który jest co najmniej tak duży i usunąć jego wpis z unallocated
i unallocated_lookup
. Jeśli jest to coś więcej niż to, czego potrzebujesz, zwróć nadmiar z powrotem do puli, tak jakby dodatkowa kwota, której nie potrzebujesz, jest zwalniana (krok 3 poniżej). Na koniec wstaw tę tablicę do tablicy allocated
, aby zapamiętać, jak dużą jest przydzieloną porcję.
Aby deallocate blok, szukać go w tablicy allocated
, aby uzyskać jego rozmiar, należy go usunąć z tablicy allocated
, a następnie:
Włóż go do unallocated
i unallocated_lookup
, podobnie jak początkowe nieprzydzielony fragment został wstawiony, patrz wyżej.
Ale jeszcze nie skończyłeś. Następnie należy użyć unallocated_lookup
, aby w sposób trywialny wyszukać poprzednią nieprzydzieloną porcję i następujący nieprzydzielony fragment w buforze pamięci. Jeśli którykolwiek z nich albo sąsiaduje bezpośrednio z nowo uwolnionym fragmentem, musisz połączyć je razem. To powinien być bardzo oczywisty proces. Możesz po prostu przejść przez proces oficjalnego usuwania sąsiadujących fragmentów pojedynczo, od unallocated
i unallocated_lookup
, a następnie zwalniając pojedynczą, scaloną porcję.
To jest prawdziwy cel unallocated_lookup
, aby móc łatwo łączyć sąsiadujące nieprzydzielonych kawałki.
O ile mi wiadomo, wszystkie powyższe operacje mają logarytmiczną złożoność. Są one całkowicie oparte na metodach std::map
i std::multimap
, które mają złożoność logarytmiczną i nic więcej.
Wreszcie:
zależności zachowanie swojej aplikacji, można łatwo dostosować do wdrażania wewnętrznie zaokrąglić w górę rozmiaru przydzielonego fragmentu do wielokrotnego cokolwiek chcesz. Lub dostosuj strategię alokacji - przydzielaj od najmniejszej porcji, która jest wystarczająco duża, aby spełnić żądanie alokacji, lub po prostu przydzielaj z dużej nieprzydzielonej porcji (proste, użyj end()
, aby ją znaleźć), itd ...
Jest to jedna z korzyści w rozwijaniu własnej implementacji - zawsze będziesz mieć znacznie większą elastyczność w dostosowywaniu własnej implementacji, wtedy zwykle będziesz mieć dostępną bibliotekę zewnętrzną.
Jak duży (lub mały) jest ten bufor? Czy może być * bardzo * duży? –
W szczególności chciałbym móc zarządzać zewnętrznym buforem 16 GB z rozmiarami alokacji od 128 bajtów aż do 512 MB. Czy to * bardzo * duże? –
Na jakim komputerze? Na superkomputerze z terabajtem RAM-u nie byłoby to aż tak duże! Powinieneś edytować swoje pytanie, a nie dodawać informacji w komentarzach. –