2008-10-21 4 views
6

In unserer Desktop-Anwendung haben wir eine einfache Suchmaschine mit einem inverted index implementiert.In-Memory-Suchindex für die Anwendung nimmt zu viel Speicher auf - irgendwelche Vorschläge?

Leider können einige der Datensätze unserer Benutzer sehr groß werden, z. Aufnahme von ~ 1 GB Speicher, bevor der invertierte Index erstellt wurde. Der invertierte Index selbst beansprucht viel Speicher, fast genauso viel wie die Daten, die indiziert werden (weitere 1 GB RAM).

Offensichtlich verursacht dies Probleme mit Arbeitsspeicherfehlern, da das 32-Bit-Windows-Limit von 2 GB Speicher pro Anwendung erreicht wird oder Benutzer mit weniger spezifizierten Computern Schwierigkeiten haben, die Speicheranforderungen zu bewältigen.

Unser invertierter Index wird als gespeichert:

Dictionary<string, List<ApplicationObject>> 

Und dies wird während der Datenlast erzeugt, wenn jedes Objekt so verarbeitet wird, dass die Schlüsselfolge und Beschreibung des applicationObject Wort in dem invertierten Index gespeichert ist.

Meine Frage ist also: Ist es möglich, den Suchindex effizienter im Raum zu speichern? Vielleicht muss eine andere Struktur oder Strategie verwendet werden? Alternativ ist es möglich, eine Art CompressedDictionary zu erstellen? Da es viele Strings speichert, würde ich erwarten, dass es stark komprimierbar ist.

Antwort

3

Wenn es 1 GB wird ... legen Sie es auf die Festplatte. Verwenden Sie etwas wie Berkeley DB. Es wird immer noch sehr schnell sein.

Hier ist ein Projekt, das eine .net-Schnittstelle bietet es:

http://sourceforge.net/projects/libdb-dotnet

+0

Ich möchte dies wenn möglich vermeiden, da es einfacher ist, den In-Memory-Suchindex zu haben. Aber vielleicht ist es nicht möglich, aber es scheint mir nur möglich zu sein. – RickL

1

ich mit bobwienholt zustimmen, aber Wenn Sie Datensätze indizieren Ich gehe davon aus diese aus einer Datenbank irgendwo kam. Wäre es sinnvoll, das nur mit einer Suchmaschine wie DTSearch oder Lucene.net zu suchen?

+0

Vielleicht, aber ich denke, das wäre komplizierter? h. die Anwendungsobjekte sind in vielen verschiedenen Tabellen gespeichert, die verschiedenen spezifischen Anwendungsobjekten zugeordnet sind. Ah, auch unsere Anwendung ist gepuffert, so dass das In-Memory-Dataset nicht mit der Datenbank synchronisiert sein kann. – RickL

3

Ich sehe ein paar Lösungen:

  1. Wenn Sie die ApplicationObjects in einem Array haben, speichern nur der Index - könnte kleiner sein.
  2. Sie könnten ein wenig C++/CLI verwenden, um das Wörterbuch mit UTF-8 zu speichern.
  3. Sie sich nicht die Mühe, all die verschiedenen Saiten zu speichern, verwenden Sie einen Trie
+0

Für Punkt 1) sind sie nicht in einem Array gespeichert, aber meintest du den Index anstelle des String-Schlüssels speichern? Wie suchst du dann nach den Saiten? Oder meintest du statt der List eine List zu haben? Ich denke, es könnte kleiner sein, aber wahrscheinlich keine große Menge. – RickL

3

Ich vermute, dass Sie Ihnen eine Menge von sehr kleinen Listen haben finden können.

Ich empfehle Ihnen ungefähr herauszufinden, wie die Häufigkeit ist - wie viele Ihrer Wörterbucheinträge haben einzelne Elementlisten, wie viele haben zwei Elementlisten usw. Sie könnten möglicherweise mehrere separate Wörterbücher speichern - eines für "Ich habe nur habe ein Element "(direkte Zuordnung) dann" Ich habe zwei Elemente "(Zuordnung zu einer Paar-Struktur mit den beiden Referenzen in) usw., bis es albern wird - möglicherweise bei etwa 3 Einträge - an diesem Punkt gehen Sie wieder normal Listen. Umhüllen Sie die ganze Menge hinter einer einfachen Schnittstelle (Eintrag hinzufügen/Einträge abrufen). Auf diese Weise haben Sie viel weniger verschwendeten Speicherplatz (meist leere Puffer, zählt usw.).

Wenn dies alles nicht sinnvoll ist, lass es mich wissen und ich werde versuchen, etwas Code zu finden.

+0

Das ist eine interessante Beobachtung ... ja, ich würde denken, dass die meisten Listen sehr klein sein würden. Mit Ihrem Vorschlag würde ich annehmen, dass die Erstellung des invertierten Indexes länger dauern würde, da Sie Elemente zwischen den Wörterbüchern mit 1 oder 2 Artikeln usw. verschieben müssten, aber möglicherweise Platz sparen könnten. – RickL

+0

Ich vermute, der Unterschied in der Leistung wäre ziemlich klein, um ehrlich zu sein - aber ja, es würde einige Overhead sein. Auf jeden Fall lohnt es sich, die Verteilung zu überprüfen, bevor Sie es kodieren :) –

+0

Ein Gedanke, um es potenziell billiger zu machen, mit zu beginnen: Haben Sie nur ein einzelnes Dictionary >. Es bedeutet, ein Objekt pro Wert zu haben, anstatt Strukturen zu verwenden, um das zu vermeiden, aber Sie müssten nur den Eintrag im Wörterbuch ersetzen, anstatt eine remove/add –

0

Wird der Index nur hinzugefügt oder entfernen Sie auch Schlüssel daraus?

+0

Schlüssel sollten aus dem Index entfernt werden, wenn das referenzierte ApplicationObject gelöscht wird. – RickL

1

Sie könnten den Ansatz von Lucene nehmen. Zuerst erstellen Sie einen In-Memory-Stream mit wahlfreiem Zugriff (System.IO.MemoryStream), dieser Stream spiegelt einen auf der Festplatte, aber nur einen Teil davon (wenn Sie den falschen Teil haben, laden Sie einen anderen von der Festplatte) . Dies verursacht Kopfschmerzen, Sie benötigen ein Datei-Mapping-Format für Ihr Wörterbuch. Wikipedia hat eine Beschreibung des paging technique.

In dem Datei-mappable-Szenario. Wenn Sie Reflector öffnen und die Dictionary-Klasse wiedergeben, sehen Sie, dass es Buckets enthält. Sie können wahrscheinlich jeden dieser Buckets als eine Seite und eine physische Datei verwenden (auf diese Weise sind Einsätze schneller). Sie können dann auch Werte einfach löschen, indem Sie einfach einen Wert "Element x gelöscht" in die Datei einfügen und die Datei immer wieder säubern.

Übrigens halten Buckets Werte mit identischen Hashes. Es ist sehr wichtig, dass Ihre Werte, die Sie speichern, die GetHashCode() -Methode überschreiben (und der Compiler wird Sie vor Equals() warnen, also überschreiben Sie diese ebenfalls). Wenn Sie dies tun, erhalten Sie eine erhebliche Geschwindigkeitssteigerung bei Suchvorgängen.

1

Wie funktioniert die Verwendung der Memory Mapped File Win32-API, um Ihre Speicherstruktur transparent zu sichern?

http://www.eggheadcafe.com/articles/20050116.asp hat die PInvokes notwendig, um es zu aktivieren.

+1

Beginnend mit .NET Framework, Version 4, können Sie verwalteten Code für den Zugriff auf Speicherabbilddateien auf die gleiche Weise verwenden, wie native Windows-Funktionen auf Speicherabbilddateien zugreifen, wie in Memory-Mapped Files in Win32 in der MSDN Library beschrieben. http://msdn.microsoft.com/en-us/library/dd997372.aspx – Tony

Verwandte Themen