python unfahig 13.5 kb grosse text-datei zu bearbeiten?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Costi
User
Beiträge: 545
Registriert: Donnerstag 17. August 2006, 14:21

das "debuggung" von meinen schull texten (es ist sonntag abend und es wird langasam fuer die HAs zeit...) gestaltet sich als sehr schwierig :lol:

hab dan ein python script geschrieben in der xterm eingegeben, der ungefaehr so ausehen musste:

Code: Alles auswählen

import string
fertig = ""
for i in """hier
kam
meien
text""".split(". "):
   fertig += string.upper(i[0]) + i[1:]

und ploetzlich fuelten sich meien 512 mb RAM, meine beiden intel core duo kerne waren zu 100% beschaftigt und sogar meien 1.4 gb swap wurden vollgeschrieben!
mit muehe und not koennte ich noch mit xkill (meinlieblings utility) den xterm killen mein system erholte sich

ist das normal, oder bin ich falsch voregegangen oder was war los?
cp != mv
BlackJack

Solange Du hier nicht das echte Programm postest, kann Dir da keiner weiterhelfen.

Grundsätzlich ist Python selbstverständlich fähig 13.5 kB grosse Text-Dateien zu bearbeiten. Bist Du fähig fehlerfreie Programme zu schreiben, die 13.5 kB grosse Text-Dateien zu bearbeiten? :roll:

Edit: Die meisten Funktionen im `string`-Modul sind veraltet und man sollte die entsprechenden Methoden auf `str`-Objekten benutzen. Da solltest Du Dir mal `capitalize()` anschauen.

Code: Alles auswählen

In [34]: 'hallo welt'.capitalize()
Out[34]: 'Hallo welt'
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

Eine ~10MB datei in den Arbeitspeicher zulesen veruchsacht rund 70MB Arbeitsspeicherplatz, wenn man jede Zeile als eine string macht (das ist schon ein exrem Bsp.) weil man das eigentlich nicht macht, geht also ohne Probleme da sind deine paar KBs lachhaft warscheinlich wird eine Endlosschleife bei dir erzeigt.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Costi hat geschrieben:[...]
und ploetzlich fuelten sich meien 512 mb RAM, meine beiden intel core duo kerne waren zu 100% beschaftigt und sogar meien 1.4 gb swap wurden vollgeschrieben!
[...]
Und das von einer 13.5 kB großen Datei? 0o xD ->
Das denke ich eher wenig ;)

Hast du mit na ``for`` über den eingelesenen Text bzw. der Instanz von ``file`` iteriert? Dan dürfte wohl keine Endlosschleife erzeugt worden sein.:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

f = file(__file__, "r")

for line in f:
    print line,/

f.close()
Hast du vielleicht eine ``while`` schleife gemacht die nicht beendet wurde in der aber wilde Kontaktion (x += line....) gemacht wird, bei dem natürlich dann der Speicher verbraucht wird.
Hier mal ein Beispiel (Bitte nicht nachmachen ^^):

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

str_ = "Hello Memory ^^"
mem_killer = ''
while True:
    mem_killer += str_
    print mem_killer
Hier wird die ganze Prozessorzeit genutzt und der Speicher wächst stätig an durch die Kontaktion. Ich denke du wirst was ähnliches fabriziert haben.

Mal nun ernsthaft: Wie Sr4l schon schrieb, kannst du sogar eine 10MB Datei (und sogar höher) in den Speicher halte, ohne das 512MB aufgebraucht werden. 13.5 kB sind ein Witz dagegen.
Beispiel:
100MB Datei erzeugen:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys, os
filename = os.path.join(sys.path[0], "test.txt")

# 1024KB * 10 = 100MB
BYTE_PER_LINE = 1048576 # 1024KB
MAX_LINES = 100

line = "".join(['1' for x in xrange(BYTE_PER_LINE)])

f = file(filename, 'wb')
for x in xrange(MAX_LINES):
    f.write(line)
f.close()

print "fertig"
Lesen:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys, os
from time import sleep
filename = os.path.join(sys.path[0], "test.txt")

f = file(filename, 'rb')
lines = f.readlines()
f.close()
print "Datei geladen"

#sleep(100)
im Taskmandeger wird mir 106432KB für den Python Prozess angezeigt. Das sind ~103.9375 MB. Davon werden 100MB für die eingelesene Datei im Speicher gebraucht (+ ein par kilos oder bytes für die interne Struktur in Python die eine ``list`` repräsentiert), und die restlichen ~3.4609375MB (Das wird ungefähr verbraucht wenn ich die Datei nicht einlese) für den Rest von Python.

ERGO: Du wirst da irgendwo was eigenartige fabriziert haben in deinem Sourcecode.

Kannst du uns dein Sourcecode posten, dann können wir dir sagen wo das Problem liegt.

lg
sape
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Sr4l hat geschrieben:Eine ~10MB datei in den Arbeitspeicher zulesen veruchsacht rund 70MB Arbeitsspeicherplatz, wenn man jede Zeile als eine string macht [...]
Nein, es werden nicht 70MB verschwendet für eine Datei die 10MB groß ist, wo jede Zeile in einer Liste ist als string. Für die Datenstruktur der ``list`` werden wie gesagt wenige kilos oder bytes verbraucht. Für de "content" der im Speicher gehalten wird, wird halt eben nicht viel Speicher verbraucht (und schon garnicht 60MB mehr, für 10MB Content!), weil die interne Struktur effizient ist ;)

In der ``struct`` ist für eine Liste einmal ein Pointer zum "content" (Oder besser gesagt: der pointer wird dynamische, zur Größe des "Content", allokiert wo der "content" dann "drine" ist) und noch ein par wenige Sachen (ids und was weiß ich noch alles) die nicht viel verbrauchen. Dazu kann glaube ich BlackJack, Rebecca oder Birkenfeld mehr sagen, wie der Aufbau der ``struct`` einer ``list`` in "C" von GvR programmiert ist und wie in etwa der mehr verbrauch ist. Ich denke, es wird sich da wirklich nur um par kilos handeln :)

Hier der beweis:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys, os
from time import sleep
filename = os.path.join(sys.path[0], "test.txt")

BYTE_PER_LINE = 1024 
MAX_LINES = 1024 * 10

line = "".join(['1' for x in xrange(BYTE_PER_LINE)])

# 10MB Datei erzeugen. 
f = file(filename, 'w') # Textdatei
for x in xrange(MAX_LINES):
    f.write(line + '\n')
f.close()
print "fertig"

#Datei laden
f = file(filename, 'r')
lines = f.readlines()
f.close()
print "Datei geladen" 
print "Anzahl Zeilen: %d" % len(lines)

#sleep(100)
Im Taskmanager wird mir ~14280KB (~13.9453125MB) für den Prozess angezeigt (mal mehr mal weniger). 10MB + ein par Kilos gehen für das halten des Datei-Content drauf und der Rest für die zusätzlichen Sachen in Python + Python selbst.

lg
sape
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Zitat aus den Style Guidelines von Supybot (das bei mir durch die Formulierung hängen geblieben ist):
Whenever joining more than two strings, use string interpolation, not addition:

Code: Alles auswählen

s = x + y + z # Bad.
s = '%s%s%s' % (x, y, z) # Good.
s = ''.join([x, y, z]) # Best, but not as general.
This has to do with efficiency; the intermediate string x+y is made (and thus copied) before x+y+z is made, so it's less efficient. People who use string concatenation in a for loop will be swiftly kicked in the head.
Also hier: Nicht mit einem leeren String beginnen und immer etwas "anfügen" (Strings sind immutable/atomic, müssen also immer kopiert und neu erzeugt werden!), sondern eine Liste füllen und diese am Ende anhand von Zeilenumbrüchen, Kommata oder sonstwas per join() zusammenfügen.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Y0Gi hat geschrieben:[...]
Also hier: Nicht mit einem leeren String beginnen und immer etwas "anfügen" (Strings sind immutable/atomic, müssen also immer kopiert und neu erzeugt werden!), sondern eine Liste füllen und diese am Ende anhand von Zeilenumbrüchen, Kommata oder sonstwas per join() zusammenfügen.
Weis ich doch. Das mit dem...

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-


str_ = "Hello Memory ^^"
mem_killer = ''
while True:
    mem_killer += str_
    print mem_killer
...war auch nur ein Beispiel wie so ein Mem-Killer zustande kommen kann. Auch das man Kontaktion nicht in Schleifen benutzen soll weiß ich und das man stattdessen alles in einer Liste pusht und dann das Ergebnis mit ``"".join`` "verbindet". Oder meintest du mich garnicht?

Aber folgendes ist übertrieben,:

Code: Alles auswählen

s = x + y + z # Bad.
s = ''.join([x, y, z]) # Best, but not as general.
x + y + z wird verbunden und an den namen ``s`` gebunden.

[x, y, z] fügt den Inhalt der drei strings in eine temporäre Liste die dann mit ``join`` zu einem string verbunden wird. Das ist langsamer außerhalb einer schleife. Außerdem ist es speicherintensiver weil erstmal Werte die schon vorhanden sind, extra nochmal in eine temporäre Liste kopiert werden müssen (Redundanter Content).

Hier der Beweis:

Join + temp mem.

Code: Alles auswählen

import dis

def x():
    x = 'test1'
    y = 'test2'
    z = 'test3'
    return "".join([x, y, z])

dis.dis(x)
Output:

Code: Alles auswählen

  7           0 LOAD_CONST               1 ('test1')
              3 STORE_FAST               0 (x)

  8           6 LOAD_CONST               2 ('test2')
              9 STORE_FAST               1 (y)

  9          12 LOAD_CONST               3 ('test3')
             15 STORE_FAST               2 (z)

 10          18 LOAD_CONST               4 ('')
             21 LOAD_ATTR                0 (join)
             24 LOAD_FAST                0 (x)
             27 LOAD_FAST                1 (y)
             30 LOAD_FAST                2 (z)
             33 BUILD_LIST               3
             36 CALL_FUNCTION            1
             39 RETURN_VALUE

Code: Alles auswählen

import dis

def y():
    x = 'test1'
    y = 'test2'
    z = 'test3'
    return x + y + z

dis.dis(y)

Code: Alles auswählen

  7           0 LOAD_CONST               1 ('test1')
              3 STORE_FAST               0 (x)

  8           6 LOAD_CONST               2 ('test2')
              9 STORE_FAST               1 (y)

  9          12 LOAD_CONST               3 ('test3')
             15 STORE_FAST               2 (z)

 10          18 LOAD_FAST                0 (x)
             21 LOAD_FAST                1 (y)
             24 BINARY_ADD          
             25 LOAD_FAST                2 (z)
             28 BINARY_ADD          
             29 RETURN_VALUE

Folgender Teil ist daran interessant:
Erstmal wird die Konstante '' erzeugt (18 ). Dan wird ``join, x, y, z`` geladen (21, 24, 27, 30). Dan _wird_ eine Liste erzeugt (33) in dem x, y, z landen. Danach wird ``join`` aufgerufen (36).

Code: Alles auswählen

             18 LOAD_CONST               4 ('')
             21 LOAD_ATTR                0 (join)
             24 LOAD_FAST                0 (x)
             27 LOAD_FAST                1 (y)
             30 LOAD_FAST                2 (z)
             33 BUILD_LIST               3
             36 CALL_FUNCTION            1
            
Hier wird lediglich ``x, y`` geladen und dann "addiert" und dessen Ergebnis mit ``z`` "addiert" was wesentlich schneller geht, als das oben :)

Code: Alles auswählen

             18 LOAD_FAST                0 (x)
             21 LOAD_FAST                1 (y)
             24 BINARY_ADD         
             25 LOAD_FAST                2 (z)
             28 BINARY_ADD         
            
Ich glaube man kanns erkenne.

In einer schleife ist es aber klar, das man alles in einer Liste pushen soll die am ennde der schleife mit ``join`` kontaktiert wird. Aber außerhalb siehst du ja selber was rauskommt. EDIT: Besonders wenn die Werte (in dem Fall x, y und z) ehe schon vorliegen ist es Schwachsinn extra redundanten Content zu erzeugen durch sowas. In dem Punkt hat sich der schreiber des Artikels klar geirrt und nicht zu ende gedacht.

lg

EDIT: Text Hinzugefügt.
EDIT2: Erklärung hinzugefügt.
Zuletzt geändert von sape am Montag 8. Januar 2007, 16:38, insgesamt 1-mal geändert.
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Nein, ich meinte nicht dich, sondern habe ich mich auf das allererste Codeschnipsel von Costi bezogen.

P.S.: Es heißt konkatenieren (Konkatenation, concat(), ...) und nicht kontaktieren ;)
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Y0Gi hat geschrieben: P.S.: Es heißt konkatenieren (Konkatenation, concat(), ...) und nicht kontaktieren ;)
Ups, sorry, stimmt :D
Antworten