Schrift
[thread]12205[/thread]

Speicheroptimierung bei Hash/Array

Leser: 1


<< >> 10 Einträge, 1 Seite
tschulz
 2008-07-18 23:32
#112313 #112313
User since
2008-07-18
3 Artikel
BenutzerIn
[default_avatar]
Hallo Perl-Community,

Ich habe eine Datenstruktur, die aus einem Hash besteht, der Arrays beinhaltet, die Hashes beinhalten. Beispiel:

Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$hashref = {
                       name1 => [
                                           { abc => "datax",
                                             def => "datay" },
                                           { abc => "datax",
                                             def => "datay" },
                                        ],
                       name2 => [
                                           { abc => "datax",
                                             def => "datay" },
                                           { abc => "datax",
                                             def => "datay" },
                                        ],
                  };


Insgesamt existieren (hier als Beispiel) 200.000 Strings, die ich hier vereinfacht mit "data..." bezeichnet habe. Sie sind fast nie länger als 10 oder 15 Zeichen und reines ASCII. Hashes haben meistens 5 Namen-Wert-Paare.

Problem:
Diese Datenstruktur, mit z.b. 200.000 Strings, benötigt 150 - 200 MB RAM, und das ist zu viel; sobald mehrere Hunderttausend in den Speicher geladen werden, geht unserem Server der Abeitsspeicher aus. Aufrüstung ist finanziell eher nicht möglich.

Ich vermute, dass Perl entweder bei den vielen, kleinen Hashs von Anfang an jeweils mehr Speicher allokiert, als nötig wäre, um später besser weitere Werte einfügen zu können, oder dass Perl sich in einer Art Memory-Pool zu viel Speicher "gesichert" hat.

Wie kann man die Datenstruktur optimieren oder Perls Verhalten beid er Speicherverwaltung verbessern? Hat jemand schon mal ähnliche Erfahrungen gemacht? Ich bin für jeden Hinweis dankbar.

Gruß
Tobias
jan
 2008-07-19 00:10
#112314 #112314
User since
2003-08-04
2536 Artikel
ModeratorIn
[Homepage] [default_avatar]
bei der optimierung ist, denke ich, erstmal die frage, ob du denn alle wirklich gleichzeitig brauchst und sie nicht sequentiell abarbeiten kannst. wie sieht das aus, was willst du auswerten?
RalphFFM
 2008-07-19 00:22
#112315 #112315
User since
2006-11-16
258 Artikel
BenutzerIn
[Homepage] [default_avatar]
Ist vielleicht ein Würgaround:
sudo dd if=/dev/zero of=/mnt/512Mb.swap bs=1M count=512
sudo mkswap /mnt/512Mb.swap
sudo swapon /mnt/512Mb.swap
...
tschulz
 2008-07-19 01:02
#112317 #112317
User since
2008-07-18
3 Artikel
BenutzerIn
[default_avatar]
jan+2008-07-18 22:10:27--
bei der optimierung ist, denke ich, erstmal die frage, ob du denn alle wirklich gleichzeitig brauchst und sie nicht sequentiell abarbeiten kannst. wie sieht das aus, was willst du auswerten?


Ich brauche nicht alle gleichzeitig, aber ich muss mehrere tausend mal pro Minute auf die Values dieser name1- und name2-Keys zugreifen, fast jedesmal auf verschiedene. Deswegen sollten die Daten als Hash bereitstehen. Ich könnte zwar pro Zugriff die Input-Dateien neu nach diesem Eintrag parsen, doch dieses Parsen würde jedesmal 10 Sekunden in Anspruch nehmen, was die Anwendung unbenutzbar macht. Hatte ich schonmal implementiert.

Eine Swapdatei ist definitiv keine Lösung, auch nicht das tie'en des Hashs auf eine Datei.

Die Daten selbst können eigentlich gar nicht so viel Speicher verbrauchen. Wenn ich alle Strings in den inneren Hashes, auf die es ja ankommt, in ein Array stecke, braucht man keine 200 MB sondern lediglich 20. Nur brauche ich einen schnelleren Lookup, als es mit einer for-Schleife durch den Hash möglich wäre. Deswegen meine Vermutung, dass irgendwo im Perl-Hash der Flaschenhals ist, vielleicht weil er eine bestimmte Menge an SPeicher allokiert, die er aber nicht braucht?
nepos
 2008-07-19 01:17
#112318 #112318
User since
2005-08-17
1420 Artikel
BenutzerIn
[Homepage] [default_avatar]
Du kannst dich ja mal mit CPAN:Devel::Size spielen. Damit kann man sich den Speicherverbrauch von Variablen in Perl genauer ansehen.
Gast Gast
 2008-07-19 08:03
#112322 #112322
Für mich scheint es sinnvoll eine Datenbank zu nutzen, da sie für so etwas ausgelegt ist.

Alternativ könnte man die Datensätze auch in Perlcode packen (siehe z.B. Data::Dumper) Dann geht das laden schneller. (z.B mit eval)

Eventuell wäre es auch möglich die Datendatei zu splitten und über alle Dateien ein Index zu erstellen, mit der man nur die gerade benötigten findet und ließt.

Wenn es sich um ein Multiprozessorsystem handelt lohnt es ich darüber nachzudenken ob sich das Aufsplitten des Programms oder wenigstens der Leseoperationen möglich wäre, um die Geschwindigkeit zu steigern.
tschulz
 2008-07-19 10:13
#112323 #112323
User since
2008-07-18
3 Artikel
BenutzerIn
[default_avatar]
Datenbank wäre eine Möglichkeit, aber sie darf eben nicht dasselbe an RAM verbrauchen.

nepos+2008-07-18 23:17:07--
Du kannst dich ja mal mit CPAN:Devel::Size spielen. Damit kann man sich den Speicherverbrauch von Variablen in Perl genauer ansehen.


Habe ich bereits.
Gast Gast
 2008-07-19 13:06
#112325 #112325
Du hast geschrieben, dass die Hashes meistens 5 Namen-Wert-Paare haben.
Falls man hier die Anzahl der Paare genau auf bsw. 5 festlegen könnte und die Keys leicht durch Zahlen ersetzt werden könnten und die arrays immer genau gleich viele Hashes beinhalten würden, hieße das, man könnte die Daten in 1 Array packen und dann z.B. folgendermaßen darauf zugreifen:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
my @arr = (
'dataaa0',
'dataab0',
'dataac0',
'dataad0',
'dataae0',
'databa0',
'databb0',
'databc0',
'databd0',
'databe0',
'dataaa1',
'dataab1',
'dataac1',
'dataad1',
'dataae1',
'databa1',
'databb1',
'databc1',
'databd1',
'databe1',
);

my $name = 1;
my $idx = 0;
my $num = 2;

print $arr[10*$name+5*$idx+$num];

MfG
LanX-
 2008-07-19 13:56
#112326 #112326
User since
2008-07-15
1000 Artikel
BenutzerIn

user image
Hallo
tschulz+2008-07-18 21:32:47--
Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$hashref = {
                       name1 => [
                                           { abc => "datax",
                                             def => "datay" },
                                           { abc => "datax",
                                             def => "datay" },
                                        ],
                       name2 => [
                                           { abc => "datax",
                                             def => "datay" },
                                           { abc => "datax",
                                             def => "datay" },
                                        ],
                  };


4 Ideen:

1. ein großes Hash wird weniger Platz allokieren und verschwenden, wenn du einen Trenner nimmst der definitiv nicht vorkommt, könntest du schreiben:
$hashref->{name1-#idx-abc}
IMHO gibts noch so ein altes Perl4 Konstrukt für multidiminsionales Strukturen mit ';' als Trenner !?! also $hashref->{name1;#idx;abc}

2. Es gibt IMHO noch so eine zwischenform von Hashes und Arrays, [s]named Arrays oder so[/s] pseudo-hash, wo jedem Index ein Key zugeordnet werden kann, gerade um Platz zu sparen. Das war aber experimental. (EDIT: siehe auch Class::PseudoHash)

3. obiges selbstbasteln: zum einen kannst du dir mit Hashties Strukturen bauen die oberflächlich wie Hashes aussehen, inwendig aber viel kompakter sind. z.B. CPAN:Tie::Hash::Array

4. Zum anderen, wenn sich die key auf der unetren ebene (abc,def, ...) prinzipiell immer wiederholen, dann leg ein Hash an das pro Key ein eindeutigen Lookup-Index zuweist sodass du nur ein Array brauchst.

Hoffe davon hilft was, habe gerade kein perl zum Testen zur Verfügung

Grüße
Rolf

EDIT: Grundsätzlich ist Swapen kein problem solange es möglichst selten vorkommt, d.h. Daten und Ablauf so gestalten dass man möglichst lange imgleichen Speicherbereich bleibt.
betterworld
 2008-07-19 15:02
#112328 #112328
User since
2003-08-21
2613 Artikel
ModeratorIn

user image
In Perl 5.10 sind einige Datenstrukturen afaik speichereffizienter geworden. Verwendest Du diese Version?

Du könntest aus
Code: (dl )
1
2
3
4
5
6
7
8
name1 => [
{ abc => "datax"},
{ abc => "datax"},
],
name2 => [
{ abc => "datax"},
{ abc => "datax"},
],

so etwas machen:
Code: (dl )
1
2
3
4
name11 => { abc => "datax"},
name12 => { abc => "datax"},
name21 => { abc => "datax"},
name22 => { abc => "datax"},

Es koennte sein, dass das weniger Speicher braucht, weil die Arrays nicht mehr dabei sind (dafuer aber mehr Hash-Keys). Ich habe es aber nicht ausprobiert.

Eine Datenbank waere wohl wirklich einen Versuch wert. Mit den richtigen Einstellungen und Indices etc. kann man es uU sehr effizient machen.
<< >> 10 Einträge, 1 Seite



View all threads created 2008-07-18 23:32.