Bardzo wydajna mapa skrótów C / C ++ (tabela, słownik) [zamknięte]

84

Muszę zmapować klucze pierwotne (int, może long) do wartości struktur w strukturze danych o wysokiej wydajności mapy skrótów.

Mój program będzie zawierał kilkaset takich map, a każda mapa będzie miała zazwyczaj najwyżej kilka tysięcy wpisów. Jednak mapy będą stale „odświeżać” lub „burzyć”; wyobraź sobie, że przetwarzasz miliony addi deletewiadomości na sekundę.

Które biblioteki w C lub C ++ mają strukturę danych, która pasuje do tego przypadku użycia? Albo jak poleciłbyś zbudowanie własnego? Dzięki!

Haywood Jablomey
źródło
1
Czy musisz przetwarzać wyszukiwanie według kluczy w swoich danych?
Guillaume Lebourgeois
3
czy aktualizacje lub pobieranie będą częstsze? (dodaj / usuń lub przeczytaj / zaktualizuj, który nie zmienia klucza)
falstro
stackoverflow.com/questions/266206/… . To może być dobre miejsce na rozpoczęcie.
DumbCoder,
2
@roe:Operacje dodawania / usuwania są znacznie (100x) częstsze niż operacje pobierania.
Haywood Jablomey
1
Po czterech i pół roku ciekawie byłoby wiedzieć, co najlepiej odpowiada Twoim potrzebom. Jeśli żadna z obecnych odpowiedzi nie była zadowalająca, możesz napisać własną i ją zaakceptować.
Walter Tross

Odpowiedzi:

31

Polecam wypróbować Google SparseHash (lub wersję C11 Google SparseHash-c11 ) i sprawdzić, czy odpowiada Twoim potrzebom. Mają implementację wydajną pod względem pamięci, a także zoptymalizowaną pod kątem szybkości. Test porównawczy wykonałem dawno temu, była to najlepsza implementacja do haszowania dostępna pod względem szybkości (jednak z wadami).

Scharron
źródło
16
Czy możesz wyjaśnić, jakie były wady?
Haywood Jablomey
IIRC, to był problem z pamięcią, podczas usuwania elementu element został zniszczony, ale jego pamięć nadal była żywa (chyba używana jako pamięć podręczna).
Scharron
4
@Haywood Jablomey: Główną wadą jest to, że wymaga oddzielenia jednej lub dwóch wartości (jeśli kiedykolwiek usuniesz elementy) i nigdy ich nie używaj. W niektórych przypadkach jest to łatwe do zrobienia, np. Ujemne ints lub podobne, ale w innych przypadkach nie do końca.
doublep
3
Czy podtrzymałbyś dziś tę rekomendację?
einpoklum
11

Które biblioteki w C lub C ++ mają strukturę danych, która pasuje do tego przypadku użycia? Albo jak poleciłbyś zbudowanie własnego? Dzięki!

Sprawdź macierze Judy na licencji LGPL . Nigdy się nie wykorzystywałem, ale kilka razy był mi reklamowany.

Możesz także spróbować przetestować kontenery STL (std :: hash_map itp.). W zależności od platformy / implementacji i dostrojenia kodu źródłowego (przydział wstępny tak dużo, jak to tylko możliwe, dynamiczne zarządzanie pamięcią jest kosztowne) mogą być wystarczająco wydajne.

Ponadto, jeśli wydajność ostatecznego rozwiązania przewyższa koszt rozwiązania, możesz spróbować zamówić system z wystarczającą ilością pamięci RAM, aby umieścić wszystko w zwykłych tablicach. Wydajność dostępu według indeksu jest bezkonkurencyjna.

Operacje dodawania / usuwania są znacznie (100x) częstsze niż operacje pobierania.

To podpowiada, że ​​możesz najpierw skoncentrować się na ulepszaniu algorytmów. Jeśli dane są tylko zapisywane, a nie czytane, to po co je w ogóle zapisywać?

Dummy00001
źródło
11

Po prostu użyj boost::unordered_map(lub tr1itp.) Domyślnie. Następnie sprofiluj swój kod i zobacz, czy ten kod jest wąskim gardłem. Dopiero wtedy radziłbym dokładnie przeanalizować swoje wymagania, aby znaleźć szybszy zamiennik.

Mark B.
źródło
15
To jest. VS2013 zajmuje ponad std::unordered_map90% mojego całego czasu wykonywania, mimo że używam map tylko do stosunkowo niewielkiej części przetwarzania.
Cameron
3

ze źródeł Androida (stąd licencja Apache 2)

https://github.com/CyanogenMod/android_system_core/tree/ics/libcutils

spójrz na hashmap.c, wybierz include / cutils / hashmap.h, jeśli nie potrzebujesz bezpieczeństwa wątków, możesz usunąć kod mutex, przykładowa implementacja znajduje się w libcutils / str_parms.c

sherpya
źródło
2

Najpierw sprawdź, czy istniejące rozwiązania, takie jak libmemcache, odpowiadają Twoim potrzebom.

Jeśli nie ...

Mapy z haszowaniem wydają się być ostateczną odpowiedzią na Twoje wymagania. Zapewnia wyszukiwanie o (1) na podstawie kluczy. Większość bibliotek STL udostępnia obecnie pewnego rodzaju skróty. Skorzystaj więc z tego, który zapewnia Twoja platforma.

Po wykonaniu tej części musisz przetestować rozwiązanie, aby sprawdzić, czy domyślny algorytm haszowania jest wystarczająco dobry pod względem wydajności dla Twoich potrzeb.

Jeśli tak nie jest, powinieneś zapoznać się z dobrymi algorytmami szybkiego haszowania znalezionymi w sieci

  1. dobra stara liczba pierwsza pomnóż algo
  2. http://www.azillionmonkeys.com/qed/hash.html
  3. http://burtleburtle.net/bob/
  4. http://code.google.com/p/google-sparsehash/

Jeśli to nie wystarczy, możesz samodzielnie rzucić moduł haszujący, który rozwiązuje problem, który widziałeś z przetestowanymi kontenerami STL i jednym z algorytmów haszujących powyżej. Pamiętaj, aby gdzieś opublikować wyniki.

Aha i to ciekawe, że masz wiele map ... być może możesz uprościć, mając swój klucz jako 64-bitową liczbę z wysokimi bitami używanymi do rozróżnienia, do której mapy należy, i dodania wszystkich par klucz-wartość do jednego gigantycznego skrótu. Widziałem hashe, które mają około stu tysięcy symboli, które działają doskonale na podstawowym algorytmie haszowania liczb pierwszych.

Możesz sprawdzić, jak to rozwiązanie działa w porównaniu z setkami map ... myślę, że to mogłoby być lepsze z punktu widzenia profilowania pamięci ... proszę, opublikuj gdzieś wyniki, jeśli wykonasz to ćwiczenie

Uważam, że czymś więcej niż algorytmem haszowania może być ciągłe dodawanie / usuwanie pamięci (czy można tego uniknąć?) I profil użycia pamięci podręcznej procesora, które mogą być bardziej kluczowe dla wydajności aplikacji

powodzenia

życie komputerowe
źródło
2

Wypróbuj tabele skrótów z różnych szablonów kontenerów . Ma closed_hash_mapmniej więcej taką samą prędkość jak Google dense_hash_map, ale jest łatwiejszy w użyciu (brak ograniczeń dotyczących zawartych wartości) i ma również inne zalety.

doublep
źródło
2

Proponuję uthash . Po prostu #include "uthash.h"dołącz, a następnie dodaj UT_hash_handledo struktury i wybierz jedno lub więcej pól w swojej strukturze, które będą działać jako klucz. Słowo o wydajności tutaj .

saharsh-jain
źródło
1

http://incise.org/hash-table-benchmarks.html gcc ma bardzo dobrą implementację. Pamiętaj jednak, że musi uwzględniać bardzo złą standardową decyzję:

Jeśli nastąpi ponowne zhaszowanie, wszystkie iteratory są unieważniane, ale odniesienia i wskaźniki do poszczególnych elementów pozostają ważne. Jeśli nie nastąpi rehash, żadnych zmian.

http://www.cplusplus.com/reference/unordered_map/unordered_map/rehash/

Oznacza to zasadniczo, że norma mówi, że wdrożenie MUSI BYĆ oparte na połączonych listach. Zapobiega otwartemu adresowaniu, które ma lepszą wydajność.

Myślę, że Google rzadko używa otwartego adresowania, chociaż w tych testach porównawczych tylko wersja gęsta przewyższa konkurencję. Jednak rzadka wersja przewyższa całą konkurencję pod względem użycia pamięci. (również nie ma żadnego plateau, czysta linia prosta z liczbą elementów)

v.oddou
źródło
1
Zobacz także to , w którym omówiono, w jaki sposób interfejs zasobnika również wymaga tworzenia łańcuchów. Punkt dotyczący referencji jest bardzo dobry. Kuszące jest argumentowanie i mówienie, że to przydatna gwarancja, ale w wielu przypadkach chcemy tylko odwołań, aby uniknąć ponownego wyszukiwania elementów, a typowym powodem jest to, że wyszukiwanie jest zbyt wolne ... co by nie było, gdyby nie było muszą utrzymywać prawidłowe odniesienia i dlatego mogą używać otwartego adresowania! Wydaje się więc, że to trochę kura i jajko. Ten cytuje propozycję w 2003 roku, wyraźnie Omawiając wyboru.
underscore_d