Performance optimierung-> Threading

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
p90
User
Beiträge: 198
Registriert: Donnerstag 22. Juli 2010, 17:30

Hi,

nach vielen Hilfsthreads hab ich mein Programm jetzt endlich fertig.
Leider hab ich immo nochein Probleme.
Ich benutze immer nur einen Core -> ich verschenke 50% Leistung
Auf dem Rechner auf dem es später laufen soll hab ich einen Quadcore, könnte also 4 Cores verwenden.


Hier erst mal das Programm:

Code: Alles auswählen

import csv
import collections
import pylab
import numpy as n
from threading import Thread
import threading 
import matplotlib.pyplot as plt
from optparse import OptionParser


#parser = OptionParser()
#parser.add_option("-m", action="store_true", help="Runs simulation, writting, plotting, write plot", dest="main")
#(options, args) = parser.parse_args()


# Defination Simulations Konstanten und Kraftkopplungen
G=6.674 * 10**(-11) #Gravitationskopplung
#G=1
time = float(0.0001) # Zeit pro Hop in s
hops = int(10000000) # Hops

# Definiere Hilfsfunktionen
def norm(x):
    out=n.sqrt(n.dot(x,x))
    return out

def csvreader(filename,delim):
    print "analyzing ",filename
    csv_in = csv.reader(open(filename, "rb"), delimiter=';')
    #csv_in = pylab.csv2rec(filename, checkrows=0, skiprows=1, delimiter=delim, names='name,pos_x,pos_y,speed_x,speed_y,mass,bla')
    bodies=[]
    #Name, Positionx, Positiony, Speedx, Speedy, Masse
    for row in list(csv_in)[1:]:
        name, pos_x, pos_y, speed_x, speed_y, mass= row
        bodies.append([name, n.array([float(pos_x),float(pos_y)]), n.array([float(speed_x),float(speed_y)]), float(mass)])
    print "finished reading"
    return bodies

def csvwriter(log):
    collector = collections.defaultdict(list)
    for entry in log:
        collector[entry[0]].append(entry[1])

    for name, entries in collector.iteritems():
        with open("%s.txt" % name, "w") as fobj:
            writer = csv.writer(fobj, delimiter='\t', lineterminator='\n')
            writer.writerows(entries)
    return "finished writing"

def pylplot(log, bodies):
    data = collections.defaultdict(lambda: collections.defaultdict(list))
    for name, pos, hop in log:
        data[name]['x'].append(pos[0])
        data[name]['y'].append(pos[1])
    t=[]
    for y in bodies:
        t.append([y[0],y[1][0],y[1][1],y[2][0],y[2][1],y[3]])

    fig = plt.figure()
    ax = fig.add_subplot(111)
    for name in data:
        ax.plot(data[name]['x'], data[name]['y'])
    plt.legend(t,
           'lower right', shadow=True, prop=dict(size=8))
    plt.subplots_adjust(left=0, bottom=0, top=1, right=1)
    plt.savefig('out.png') 
    plt.show()
    return "plot finished"

def calcgravforce(x,y):
    #G * m1 * m2 * er /r^2
    out = (x[1]-y[1]) * G * x[3] * y[3] / (norm(x[1]-y[1])**3)
    return out

def calcgravforcecomplete(x, bodies):
    force = n.array([0,0])
    for y in bodies:
        if not y == x:
            force = force - calcgravforce(x, y)
    return force

def calcnewposition(x, force):
    # 1/2 * a * t^2 + v * t +x
    out = 0.5 * (time**2) * force / x[3] + time * x[2] + x[1]
    return out
    
def calcnewspeed(x, force):
    #a * t + v
    out = time * force / x[3]+ x[2]
    return out

def calcnewmass(x):
    #konstante masse, Raketen noch nicht drin
    out = x[3]
    return out

def calcbody(x, force):
    out = [x[0], calcnewposition(x, force), calcnewspeed(x, force), calcnewmass(x)]
    return out

def calc(z):
    force = calcgravforcecomplete(z[0], z[1])
    return calcbody(z[0], force)

#if options.main():
def main():
    bodies = csvreader("koerper.txt", ";")
    startbodies = bodies[:]
    log=[]
    for i in range(0, hops):
        bodiesnew=[]
        for x in bodies:
            force = calcgravforcecomplete(x, bodies)
            bodiesnew.append(calcbody(x, force))
            log.append([x[0], x[1], i])
        bodies=bodiesnew
    print "finished calc"
    print csvwriter(log)
    print pylplot(log, startbodies)
    return 1

main()
Insbesonders muss ich unten die:

Code: Alles auswählen

        for x in bodies:
            force = calcgravforcecomplete(x, bodies)
            bodiesnew.append(calcbody(x, force))
            log.append([x[0], x[1], i])
        bodies=bodiesnew
beschleunigen. Der Rest wird nur einmal ausgeführt, das hier aber einige Millionen mal.
Das gute ist, für jede forschleifen Durchlauf sind alle Werte statisch, man kann es also gut parallelisieren. Nur wie?
Habs jetzt schon mit p = pool() usw. versucht aber da bekomme ich nur ständig irgendwelche Speicheradressen mit Fehlern geplottet. Kann ich das Listenschreiben überhaupt parallelisieren oder nur die eigentliche Berechnung?
Wichtig ist hier vlt auch das ich immo unter Windows das ganze schreibe, es aber später unter Linux laufen soll.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Mit einfachen Threads wirst du wegen des GILs nicht weiterkommen. Schau dir mal das multiprocessing-Modul an und Teile die Aufgabe auf mehrere Prozesse auf. Parallel in die selbe Liste schreiben solltest du sein lassen, je nach Menge der Daten würde ich entweder die Liste nach allen Berechnungen zusammensetzen oder aber eine kleine Hilfsklasse schreiben, welche mehrere Listen hält und die Zugriffe verdeckt.

Noch ein paar Anmerkungen zu deinem Code:
- Einfach nur threading zu importieren ist schon recht sinnlos ;-)
- Ein "time = float(0.0001)" macht ein float nicht noch floatiger; selbiges gilt für Integer.
- Das Binden eines Ergebnis an "out" und in der nächsten Zeile ein "return out" ist überflüssig. Benutze gleich das return.
- Wenn du Dateien öffnest, dann solltest du sie auch wieder schließen.
- Solltest du große csv-Dateien lesen, dann möchtest du nicht "list(csv_in)[1:]" verwenden, sondern itertools.islice oder einen Generator.
- Text als Rückgabewert ist meist ein schlechtes Zeichen. Wenn kein Fehler aufgetreten ist, möchtest du im Normalfall nichts zurückgeben, im Fehlerfall eine Exception.
- Bei range kannst du die 0 als Startparameter sparen.
- Den Aufruf der main-Funktion möchtest du noch duch "if __name__ == '__main__':" schützen.
- Insgesamt sind das viel zu viele Indizes, welche man nicht nachvollziehen kann.
- Da sind noch eine Menge anderer Punkte, aber das sollte fürs erste reichen.

Sebastian
Das Leben ist wie ein Tennisball.
p90
User
Beiträge: 198
Registriert: Donnerstag 22. Juli 2010, 17:30

Hi,

also hier das selbe erst mal in etwas schöner:

Code: Alles auswählen

import csv
import pylab
import numpy as n
import matplotlib.pyplot as plt
from optparse import OptionParser

parser = OptionParser()
#parser.add_option("-m", action="store_true", help="Runs simulation, writting, plotting, write plot", dest="main")
(options, args) = parser.parse_args()


# Defination Simulations Konstanten und Kraftkopplungen
G=6.674 * 10**(-11) #Gravitationskopplung
simulationtime = float(0.01) # Zeit pro Hop in s
simulationhops = int(1000) # Hops

class Mybody: 
    def __init__(self, name, pos, speed, mass): 
        self.name = name #String
        self.pos = pos #Array of floats
        self.speed = speed #Array of floats
        self.mass = mass #Float
        self.newpos = pos
        self.newspeed = speed
        self.log = []

    def name(self):
        return self.name
    
    def pos(self):
        return self.pos
    
    def speed(self):
        return self.speed

    def mass(self):
        return self.mass

    def calchop(self, force):
        self.newspeed = simulationtime * force / self.mass + self.speed
        self.newpos = 0.5 * (simulationtime**2) * force / self.mass + simulationtime * self.speed + self.pos

    def hop(self):
        self.log.append(self.pos)
        self.speed = self.newspeed
        self.pos = self.newpos

    def printlog(self):
        return self.log

    def gravforce(self, y):
        if self.pos.all == y.pos.all:
            return n.array([0,0])
        return (self.pos - y.pos) * G * self.mass * y.mass / (norm(self.pos - y.pos)**3)

    def writelog(self):
        with open("%s.txt" % self.name, "w") as fobj:
            writer = csv.writer(fobj, delimiter='\t', lineterminator='\n')
            writer.writerows(self.log)

class Mybodies:
    def __init__(self):
        self.bodies = []
        self.startbodies = []
        self.hops = 0


    def add(self,body):
        self.bodies.append(body)
        print "Add Body:", len(self.bodies)

    def add_mult(self, mult_body):
        for y in mult_body:
            self.add(y)

    def calcgravforcecomplete(self, body):
        force = n.array([0,0])
        for y in self.bodies:
            force = force - body.gravforce(y)
        return force    
    
    def next(self):
        for x in self.bodies:
            force = self.calcgravforcecomplete(x)
            x.calchop(force)
        for x in self.bodies:
            x.hop()

    def startbodies(self):
        return self.startbodies

    def writelogs(self):
        for body in self.bodies:
            body.writelog()

# Definiere Hilfsfunktionen
def norm(x):
    out=n.sqrt(n.dot(x,x))
    return out

def csvreader(filename,delim):
    print "analyzing ",filename
    csv_in = csv.reader(open(filename, "rb"), delimiter=';')
    bodies=[]
    for row in list(csv_in)[1:]:
        name, pos_x, pos_y, speed_x, speed_y, mass = row
        body=Mybody(name, n.array([float(pos_x),float(pos_y)]), n.array([float(speed_x),float(speed_y)]), float(mass))
        bodies.append(body)
    print "finished reading"
    return bodies

def pylplot(log, bodies):
    data = collections.defaultdict(lambda: collections.defaultdict(list))
    for name, pos, hop in log:
        data[name]['x'].append(pos[0])
        data[name]['y'].append(pos[1])
    t=[]
    for y in bodies:
        t.append([y.name,y.pos[0],y.pos[1],y.speed[0],y.speed[1],y.mass])

    fig = plt.figure()
    ax = fig.add_subplot(111)
    for name in data:
        ax.plot(data[name]['x'], data[name]['y'])
    plt.legend(t,
           'lower right', shadow=True, prop=dict(size=8))
    plt.subplots_adjust(left=0, bottom=0, top=1, right=1)
    plt.savefig('out.png') 
    plt.show()
    return "plot finished"


#Hauptteil

bodies = Mybodies()
bodies.add_mult(csvreader("koerper.txt", ";"))
for i in range(simulationhops):
    bodies.next()
print "finished calc"
bodies.writelogs()

#plotten ist noch nicht wieder eingebaut
#print pylplot(bodies.logs(), bodies.startbodies())
Zu deinen Punkten:
  • - jo, weiß ich, hatte wie gesagt damit experimentiert, daher dieser Überrest
    - jo, weiß ich auch, aber der nächste weiß vlt nicht das er da nen float und int hinpacken soll
    - dachte immer das sollte man wegen der lesbarkeit nicht machen aber hast recht
    - okay, daran hatte ich jetzt noch garnicht gedacht, ist jetzt aber drin
    - die csv Datei ist immo noch sehr klein (2-3 Einträge) da mein Rechner einfach nicht mehr schaft, werde das aber noch verbessern, danke für die Anregung
    - okay, kommt mir komisch vor aber macht irgenwie Sinn
    - done
    - Das verstehe ich nicht. "if __name__ == '__main__':" wird zwar insbesondere für windows immer empfohlen, aber warum und vor was man sich da schützt verstehe ich nicht. Kann es sein das eine Exception einfach aus der momentanen Codeebene rausspringt und es dann sozusagen eine Endlosschleife gibt? Weil dann macht das Sinn
    - Ich hoffe das kann man jetzt auch wesentlich besser nachvollziehen
    - immer her damit!
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Auf jeden Fall ist der Code jetzt um einiges übersichtlicher geworden.

Zu "if __name__ == '__main__':": Das solltest du nutzen um Code nur dann auszuführen, wenn das Modul gestartet wird. Andernfalls gibt es unter Umständen Probleme, falls du dein Modul in einem anderen importieren möchtest. Hast du Code auf Modulebene, so sollte dieser, im Normalfall, in eine main-Funktion gepackt werden. Die rufst du dann mittels

Code: Alles auswählen

if __name__ == '__main__':
    main()
am Ende des Moduls auf. Einen besonderen Schutz liefert dies nicht, es ermöglicht dir lediglich einen vernünftigen Import. Zum Code auf Modulebene gehören bei dir der OptionParser und alles unter dem Kommentar "Hauptteil". Konstanten bleiben natürlich weiterhin auf Modulebene stehen.

Dann noch kurz zu ein paar anderen Kleinigkeiten:
- Beim Entpacken des Rückgabewerts des OptionParsers brauchst du auf der linken Seite keine Klammern. Kommas erzeugen Tupel, nicht die Klammern (mit Ausnahme des leeren Tupels).
- Konstanten solltest du durchgänging in Großbuchstaben schreiben und Wörter durch einen Unterstrich trennen. Wirf einfach mal einen Blick in PEP 8.
- Da du offensichtlich Python < 3 verwendest, sollten deine Klassen von object erben.
- So triviale getter-Methoden wie name, pos, speed oder mass kannst du dir in Python sparen, greife von außen direkt auf die Attribute zu. In anderen Sprachen ist dieser Umweg oft nötig, da diese keine Properties unterstützen.
- printlog gibt bei dir gar nichts aus ;-)
- Was für einen Sinn hat startbodies in Mybodies? Du verwendest es nirgends. Ich vermute mal, dass du den Code etwas gekürzt hast.
- zu calcgravforcecomplete: Schau dir mal die sum-Funktion an.

Sebastian
Das Leben ist wie ein Tennisball.
BlackJack

@p90: Ob da irgendwo `float` oder `int` erwartet wird, sieht man doch auch wenn Du 0.01 und 1000 zuweist. Den Typ sieht man doch an diesen Werten!?

Was soll der `My`-Präfix bei den Klassenamen aussagen?

Bei `Mybodies.add_mult()` ist das 'mult' IMHO unschön. Gilt generell für Abkürungen die nicht weit verbreitet sind. `add_bodies()` und das Argument dann einfach `bodies` nennen drückt das ganze IMHO klarer und ohne Abkürzungen aus.
p90
User
Beiträge: 198
Registriert: Donnerstag 22. Juli 2010, 17:30

Hi,
@BlackJack
hatte die Klassen MyBody genannt um leichter zu sehen was da gerade einen Fehler geworden hat.
add_mult ist wirklich doof, hab es jetzt add ersetzen lassen da add_mult eh mehr abdeckt.

@EyDu
hab bisher den optionPaser überhaupt nicht verwendet.
Es steht nur drin damit ich den Code beim Einbau direkt da habe.
Aber das kommt als nächstes.
startbodies brauche ich nur zum Ploten.
Plote da die Startwerte mit rein.
Da das noch nicht eingebaut war, war die Variable verweißt.
Zu sum:
Habs damit noch nicht hinbekommen.
Es sieht jetzt so aus:

Code: Alles auswählen

import csv
import pylab
import numpy as n
import matplotlib.pyplot as plt
from optparse import OptionParser
import collections

# Defination Simulations Konstanten und Kraftkopplungen
G=6.674 * 10**(-11) #Gravitationskopplung
simulation_time = float(0.01) # Zeit pro Hop in s
simulation_hops = int(1000000) # Hops

parser = OptionParser()
#parser.add_option("-m", action="store_true", help="Runs simulation, writting, plotting, write plot", dest="main")
(options, args) = parser.parse_args()

class Body: 
    def __init__(self, name, pos, speed, mass): 
        self.name = name #String
        self.pos = pos #Array of floats
        self.speed = speed #Array of floats
        self.mass = mass #Float
        self.new_pos = pos
        self.new_speed = speed
        self.log_x = []
        self.log_y = []
        
    def calc_hop(self, force):
        self.new_speed = simulation_time * force / self.mass + self.speed
        self.new_pos = 0.5 * (simulation_time**2) * force / self.mass + simulation_time * self.speed + self.pos

    def hop(self):
        self.log_x.append(self.pos[0])
        self.log_y.append(self.pos[1])
        self.speed = self.new_speed
        self.pos = self.new_pos

    def grav_force(self, y):
        if self.pos.all == y.pos.all:
            return n.array([0,0])
        return (self.pos - y.pos) * G * self.mass * y.mass / (norm(self.pos - y.pos)**3)

    def write_log(self):
        with open("%s.txt" % self.name, "w") as fobj:
            writer = csv.writer(fobj, delimiter='\t', lineterminator='\n')
            writer.writerows([self.log_x, self.log_y])
        fobj.close()

class Bodies:
    def __init__(self):
        self.bodies = []
        self.start_bodies = []
        self.hops = 0
        
    def add(self, bodies):
        for y in bodies:
            self.bodies.append(y)
            self.start_bodies.append(Body(y.name, y.pos, y.speed, y.mass))
            
    def calc_grav_force_complete(self, body):
        force = n.array([0,0])
        for y in self.bodies:
            force = force - body.grav_force(y)
        return force    
    
    def next(self):
        for x in self.bodies:
            force = self.calc_grav_force_complete(x)
            x.calc_hop(force)
        for x in self.bodies:
            x.hop()

    def write_logs(self):
        for body in self.bodies:
            body.write_log()

    def show_plot(self):
        t=[]
        for y in self.start_bodies:
            t.append([y.name,y.pos[0],y.pos[1],y.speed[0],y.speed[1],y.mass])

        fig = plt.figure()
        ax = fig.add_subplot(111)
        
        for body in self.bodies:
            ax.plot(body.log_x, body.log_y)
        plt.legend(t,
               'lower right', shadow=True, prop=dict(size=8))
        plt.subplots_adjust(left=0, bottom=0, top=1, right=1)
        plt.savefig('out.png') 
        plt.show()

    
# Definiere Hilfsfunktionen
def norm(x):
    out=n.sqrt(n.dot(x,x))
    return out

def csvreader(filename,delim):
    print "analyzing ",filename
    csv_in = csv.reader(open(filename, "rb"), delimiter=';')
    bodies=[]
    for row in list(csv_in)[1:]:
        name, pos_x, pos_y, speed_x, speed_y, mass = row
        body=Body(name, n.array([float(pos_x),float(pos_y)]), n.array([float(speed_x),float(speed_y)]), float(mass))
        bodies.append(body)
    print "finished reading"
    return bodies

#Hauptteil
def main():
    bodies = Bodies()
    bodies.add(csvreader("koerper.txt", ";"))
    for i in range(simulation_hops):
        bodies.next()
    print "finished calc"
    bodies.write_logs()
    bodies.show_plot()
                    
if __name__ == '__main__':
    main()
Ich muss sagen, WOW.
das ganze braucht jetzt 50% weniger Speicher als zuvor (bei 100k Hops ca. 90MB, jetzt nur noch 40MB-50MB).
BlackJack

Mit `sum()` (ungetestet):

Code: Alles auswählen

    def calc_grav_force_complete(self, body):
        return sum((-body.grav_force(b) for b in self.bodies), n.array([0, 0]))
p90
User
Beiträge: 198
Registriert: Donnerstag 22. Juli 2010, 17:30

Hi,

nachdem das Programm nach dem einbauen einiger Veränderungen wieder ewig langsam lief
hab ich da hier gefunden:
http://bugs.python.org/issue5855
Werde also doch meine eklige selbst gemachte Lösung verwenden.
Hier nochmal das aktuelle, leider wieder etwas unübersichtlicher.

Code: Alles auswählen

import csv
import pylab
import numpy as n
import matplotlib.pyplot as plt
from optparse import OptionParser

# Defination Simulations Konstanten und Kraftkopplungen
G=6.674 * 10**(-11) #Gravitationskopplung
simulation_time = float(0.01) # Zeit pro Hop in s
simulation_hops_write = int(100000)#Zwischenzeitliches rausschreiben damit der ram nicht voll lauft

parser = OptionParser()
parser.add_option("-c", action="store_true", help="Run simulation", dest="calc", default=True)
parser.add_option("-r", action="store_false", help="No simulation, show only data", dest="calc")

#parser.add_option("-f", "--file", type="string", help="Bodyfile, needed always!", dest="inputfile")

#parser.add_option("-o", "--outfolder", type="string", help="Outputfolder, default /inputfile", dest="outputfolder")

parser.add_option("-p", "--plot", action="store_true", help="Save plot as png", dest="plot", default=False)
parser.add_option("-s", "--show", action="store_true", help="Show plot", dest="show", default=False)

#parser.add_option("-q", "--quiet", action="store_true", help="No output by print", dest="quiet")

parser.add_option("-k", "--hops", type="int", help="Simulationhops", dest="simhops", default=100000)

(options, args) = parser.parse_args()

class Body: 
    def __init__(self, name, pos, speed, mass): 
        self.name = name #String
        self.pos = n.array([float(pos[0]),float(pos[1])]) #Array of floats
        self.speed = n.array([float(speed[0]),float(speed[1])]) #Array of floats
        self.mass = float(mass) #Float
        self.new_pos = pos
        self.new_speed = speed
        self.log = []
        self.writer = 0
        
    def calc_hop(self, force):
        self.new_speed = simulation_time * force / self.mass + self.speed
        self.new_pos = 0.5 * (simulation_time**2) * force / self.mass + simulation_time * self.speed + self.pos

    def hop(self):
        self.log.append(self.pos)
        self.speed = self.new_speed
        self.pos = self.new_pos

    def grav_force(self, y):
        if self.pos.all == y.pos.all:
            return n.array([0,0])
        diffvec = (self.pos - y.pos) 
        return  diffvec * G * self.mass * y.mass / (norm(diffvec)**3)

    def write_log(self, close=1):
        with open("%s.txt" % self.name, "w") as fobj:
            #self.writer = csv.writer(fobj, delimiter='\t', lineterminator='\n')
            self.writer = csv.writer(fobj, delimiter=';', lineterminator='\n')
            self.writer.writerows(self.log)
        if close:
            fobj.close()
        self.log=[]

class Bodies:
    def __init__(self):
        self.bodies = []
        self.start_bodies = []
        self.hops = 0
        
    def add(self, bodies):
        for y in bodies:
            self.bodies.append(y)
            self.start_bodies.append(Body(y.name, y.pos, y.speed, y.mass))

    def calc_grav_force_complete(self, body):
        force = n.array([0,0])
        for y in self.bodies:
            force = force - body.grav_force(y)
        #return sum(-body.grav_force(b) for b in self.bodies)
        return force
          
    def next(self):
        for x in self.bodies:
            force = self.calc_grav_force_complete(x)
            x.calc_hop(force)
        for x in self.bodies:
            x.hop()

    def write_logs(self,i=0):
        for body in self.bodies:
            body.write_log(i)



    
# Definiere Hilfsfunktionen
def norm(x):
    out=n.sqrt(n.dot(x,x))
    return out

def body_reader(filename,delim):
    print "analyzing Bodyfile:",filename
    csv_in = csv.reader(open(filename, "rb"), delimiter=';')
    bodies = []
    for row in list(csv_in)[1:]:
        name, pos_x, pos_y, speed_x, speed_y, mass = row
        body=Body(name, n.array([float(pos_x),float(pos_y)]), n.array([float(speed_x),float(speed_y)]), float(mass))
        bodies.append(body)
    print "finished reading Bodyfile:",filename
    return bodies

def simulation_reader(filename,delim):
    print "analyzing Simulationfile:",filename
    csv_in = csv.reader(open(filename, "rb"), delimiter=';')
    x = []
    y = []
    for row in list(csv_in)[1:]:
        pos_x, pos_y = row
        x.append(pos_x)
        y.append(pos_y)
    print "finished reading Simulationfile:", filename
    return [x,y]

def show_plot(start_bodies):
    t=[]
    fig = plt.figure()
    ax = fig.add_subplot(111)
    
    for y in start_bodies:
        t.append([y.name,y.pos[0],y.pos[1],y.speed[0],y.speed[1],y.mass])
        z = []
        z = simulation_reader(y.name + ".txt", ";")
        ax.plot(z[0], z[1])

    plt.legend(t,'lower right', shadow=True, prop=dict(size=8))
    plt.subplots_adjust(left=0, bottom=0, top=1, right=1)
    if options.plot:
        plt.savefig('out.png') 
    if options.show:
        plt.show()



#Hauptteil
def main():
    simulation_writes = int(options.simhops)
    bodies = Bodies()
    bodies.add(body_reader("koerper.txt", ";"))
    if options.calc:
        z=simulation_writes / simulation_hops_write
        for k in range(z):
            for i in range(simulation_hops_write):
                bodies.next()
            bodies.write_logs(1)
            print "Finished ", k, " from ", z+1, " parts" 
        for k in range(simulation_writes % simulation_hops_write):
            bodies.next()
        bodies.write_logs()
    if options.plot or options.show:
        show_plot(bodies.start_bodies)
                    
if __name__ == '__main__':
    main()
Hab mich mit zwischenzeitlichen raus schreiben der logs um das Ram Problem gemogelt
(kann man das eigentlich so machen, scheint zwar zu gehen, aber wer weiß) aber hänge immer noch im CPU.
BlackJack

@p90: Der "Bug" hat nichts mit Deinem Programm zu tun.
p90
User
Beiträge: 198
Registriert: Donnerstag 22. Juli 2010, 17:30

Hm, keine Ahnung, dafür kenne ich mich nicht genug aus.
Aber das Programm braucht 8% mehr Zeit mit der Sum Funktion als mit meiner Lösung.

Das mit dem schreiben wie ich es da probiert habe geht auf alle Fälle nicht.
Wie kann ich den am Ende einer Datei was schreiben statt sie immer wieder von vorne zu befüllen? Hab bisher noch nichts gefunden.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Solange du nur ans Ende schreiben willst, reicht also `mode` "a" statt "w". http://docs.python.org/library/functions.html#open
p90
User
Beiträge: 198
Registriert: Donnerstag 22. Juli 2010, 17:30

Hi
@cofi
Danke! Jetzt funktioniert es genau wie es soll!

Zum Thema optimieren.
Da studiere ich 6 Semester Physik und kann noch nicht mal den Stoff aus dem 1. Semester.
Hab das ganze umgebaut um einen Massenschwerpunkt zu verwenden.
Benötigte bei 30 Körpern und 100Hops vorher 70s.
Jetzt sind es nur noch 3,5s!
Für 2 Körper und 100000 Hops war ich vorher bei 23s, jetzt bei 21s. Ist zwar nicht viel, aber ich hatte befürchtet das es da eine Verschlechterung geben würde.
Jetzt muss ich nur noch Threading einbauen (Hohohoho...) und dann sollte es gut sein.
Hier erst mal der Code wieder.

Code: Alles auswählen

import csv
import pylab
import numpy as n
import matplotlib.pyplot as plt
from optparse import OptionParser
from datetime import datetime
import time
import threading 

# Defination Simulations Konstanten und Kraftkopplungen
G=6.674 * 10**(-11) #Gravitationskopplung
simulation_time = float(0.001) # Zeit pro Hop in s
simulation_hops_write = int(100000)#Zwischenzeitliches rausschreiben damit der ram nicht voll lauft

parser = OptionParser()
parser.add_option("-c", action="store_true", help="Run simulation", dest="calc", default=True)
parser.add_option("-r", action="store_false", help="No simulation, show only data", dest="calc")

parser.add_option("-f", "--file", type="string", help="Bodyfile, needed always!", dest="bodyfile", default="koerper.txt")

#parser.add_option("-o", "--outfolder", type="string", help="Outputfolder, default /inputfile", dest="outputfolder", default="\")

parser.add_option("-p", "--plot", action="store_true", help="Save plot as png", dest="plot", default=False)
parser.add_option("-s", "--show", action="store_true", help="Show plot", dest="show", default=False)

#parser.add_option("-q", "--quiet", action="store_true", help="No output by print", dest="quiet")

parser.add_option("-k", "--hops", type="int", help="Simulationhops", dest="simhops", default=1000)

(options, args) = parser.parse_args()

class Body(threading.Thread): 
    def __init__(self, name, pos, speed, mass):
        threading.Thread.__init__(self) 
        self.name = name #String
        self.pos = n.array([float(pos[0]),float(pos[1])]) #Array of floats
        self.speed = n.array([float(speed[0]),float(speed[1])]) #Array of floats
        self.mass = float(mass) #Float
        self.log = []
        self.force = n.array([0,0])
        self.first_write = True
        self.center_of_mass = Body("Center of mass",n.array([0,0]),n.array([0,0]),0)
        
    def run(self):
        self.hop()
        
    def hop(self, log=True):
        if log:
            self.log.append(self.pos)
        self.pos = 0.5 * (simulation_time**2) * self.force / self.mass + simulation_time * self.speed + self.pos
        self.speed = simulation_time * self.force / self.mass + self.speed

    def write_log(self):
        mode = "a"
        if self.first_write:
            self.first_write = False
            mode = "w"
        with open("%s.txt" % self.name, mode) as fobj:
            writer = csv.writer(fobj, delimiter=';', lineterminator='\n')
            writer.writerows(self.log)
            fobj.close()
        self.log=[]

    def grav_force(self, center_of_mass):
        center_for_self = (center_of_mass.pos * center_of_mass.mass - self.pos * self.mass) / (center_of_mass.mass - self.mass)
        self.force = G * (center_of_mass.mass - self.mass) * self.mass * (center_for_self - self.pos) / ((norm(center_for_self - self.pos))**3)

class Bodies:
    def __init__(self):
        self.bodies = []
        self.hops = 0
        self.center_of_mass = Body("Center of mass",n.array([0,0]),n.array([0,0]),0)
        
    def add(self, bodies):
        for y in bodies:
            self.bodies.append(y)

    def init(self):
        for y in self.bodies:
            self.center_of_mass.pos += y.pos * y.mass
            self.center_of_mass.mass += y.mass
        self.center_of_mass.pos = (self.center_of_mass.pos / self.center_of_mass.mass)
        for y in self.bodies:
            self.center_of_mass.speed += y.speed * y.mass
        self.center_of_mass.speed = self.center_of_mass.speed / self.center_of_mass.mass
        
        
    def grav_force(self, body):
        center_for_body = (self.center_of_mass.pos * self.center_of_mass.mass - body.pos * body.mass) / (self.center_of_mass.mass - body.mass)
        force = G * (self.center_of_mass.mass - body.mass) * body.mass * (center_for_body - body.pos) / ((norm(center_for_body - body.pos))**3) 
        return force

    def next(self):
        threads = []
        for x in self.bodies:
            #x.run(self.center_of_mass)
            #x.grav_force(self.center_of_mass)
            #x.hop()
            x.center_of_mass = self.center_of_mass
            threads.append(x) 
            x.start()

        for t in threads: 
            t.join()
        self.center_of_mass.hop(False)

    def write_logs(self):
        for body in self.bodies:
            body.write_log()
    
# Definiere Hilfsfunktionen
def norm(x):
    out=n.sqrt(n.dot(x,x))
    return out

def body_reader(filename,delim):
    print "analyzing Bodyfile:",filename
    csv_in = csv.reader(open(filename, "rb"), delimiter=';')
    bodies = []
    for row in list(csv_in)[1:]:
        name, pos_x, pos_y, speed_x, speed_y, mass = row
        body=Body(name, n.array([float(pos_x),float(pos_y)]), n.array([float(speed_x),float(speed_y)]), float(mass))
        bodies.append(body)
    print "finished reading Bodyfile:",filename
    return bodies

def simulation_reader(filename,delim):
    print "analyzing Simulationfile:",filename
    csv_in = csv.reader(open(filename, "rb"), delimiter=';')
    x = []
    y = []
    for row in list(csv_in)[1:]:
        pos_x, pos_y = row
        x.append(pos_x)
        y.append(pos_y)
    print "finished reading Simulationfile:", filename
    return [x,y]

def show_plot():
    t=[]
    fig = plt.figure()
    ax = fig.add_subplot(111)
    
    for y in body_reader(options.bodyfile, ";"):
        t.append([y.name,y.pos[0],y.pos[1],y.speed[0],y.speed[1],y.mass])
        z = []
        z = simulation_reader(y.name + ".txt", ";")
        ax.plot(z[0], z[1])

    plt.legend(t,'lower right', shadow=True, prop=dict(size=8))
    plt.subplots_adjust(left=0, bottom=0, top=1, right=1)
    if options.plot:
        plt.savefig('out.png') 
    if options.show:
        plt.show()

#Hauptteil
def main():
    simulation_writes = int(options.simhops)
    bodies = Bodies()
    bodies.add(body_reader(options.bodyfile, ";"))
    bodies.init()
    if options.calc:
        z=simulation_writes / simulation_hops_write
        for k in range(z):
            for i in range(simulation_hops_write):
                bodies.next()
            bodies.write_logs()
            print "Finished ", k, " from ", z+1, " parts" 
        for k in range(simulation_writes % simulation_hops_write):
            bodies.next()
        bodies.write_logs()
    if options.plot or options.show:
        show_plot()
                    
if __name__ == '__main__':
    time.time()
    t1 = time.time()
    main()
    t2 = time.time()
    print "calc in ", (t2-t1)


Hab bishe rnoch nie mit threading gearbeitet, aber wenn ich das Bsp hier:
http://openbook.galileocomputing.de/pyt ... 18_004.htm
sehe sollte es doch gehen?

PS: Ich weiß, das da unten ist nicht Ideal um die Zeit zu messen aber es ist der einzige Prozess der an meinen 2. CPU Core gebunden ist. Sollte also keine so starken Schwankungen geben
BlackJack

@p90: Ist vielleicht etwas oberflächlich, aber der Quelltext enthält einige Zeilen die länger als 80 Zeichen sind und es könnten noch ein paar mehr Leerzeichen gesetzt werden. PEP8 empfiehlt welche vor und nach Operatoren sowie nach Kommas.

Die Importe `pylab` und `datetime` werden nicht verwendet.

Den `OptionParser`-Code würde ich da auch noch von der Modulebene in einer Funktion verschwinden lassen.

Das mit dem Threading ist so keine gute Idee. Selbst wenn Threading bei CPython etwas bringen würde, macht es IMHO keinen Sinn für jeden Körper für jeweils einen "Hop" einen eigenen Thread zu starten. Threads starten und abräumen ist auch nicht umsonst zu haben was die Laufzeit angeht. Wenn Du parallele Ausführung von Python-Code in CPython haben möchtest, dann musst Du Prozesse statt Threads verwenden. Schau Dir mal das `multiprocessing`-Modul an. Und da dann nicht so viele Prozesse starten, sondern eine feste Anzahl und auf die dann die Berechnungen verteilen beziehungsweise verteilen lassen. Ausserdem kann man Threads nur *einmal* laufenlassen, wenn deine Body's also von `Thread` erben, dann können die jeweils nur einmal `run()` und damit `hop()` ausführen.

`Bodies.init()` würde ich in `Bodies.add()` einbauen. Man sollte sich bemühen, dass ein Objekt immer in einem gültigen, "benutzbaren" Zustand ist. Ansonsten liesse sich `Bodies.add()` mit der `list.extend()`-Methode in einer Zeile ausdrücken.

Bei den beiden `*_reader()`-Funktionen kann man jeweils den Namen `row` einsparen indem man das entpacken gleich in der ``for``-Schleife macht. Ausserdem kann man sich auch jeweils das komplette Einlesen aller Datensätze vor der Verarbeitung sparen, indem man die erste Zeile einfach vorher ausliest:

Code: Alles auswählen

def simulation_reader(filename):
    print 'analyzing Simulationfile:', filename
    with open(filename, 'rb') as csv_file:
        csv_in = csv.reader(csv_file, delimiter=';')
        _header = csv_in.next()
        x = list()
        y = list()
        for pos_x, pos_y in csv_in:
            x.append(pos_x)
            y.append(pos_y)
    print 'finished reading Simulationfile:', filename
    return [x, y]
Die Namen `x` und `y` für Körper zu verwenden ist nicht besonders aussagekräftig. `t` für einen Thread ist da zwar naheliegender, aber ich hätte es trotzdem ausgeschrieben. Das Argument `delim` bei `body_reader()` und `simulation_reader()` wird jeweils nicht verwendet.

Die Verwendung von `t` in `show_plot()` erscheint mir komisch. Der Name wird in jedem Schleifendurchlauf neu gebunden, aber nur der letzte gebundene Wert wird auch tatsächlich verwendet.

`psyco` wäre vielleicht noch einen Versuch Wert das Programm zu beschleunigen.
p90
User
Beiträge: 198
Registriert: Donnerstag 22. Juli 2010, 17:30

Hi,

also der Tipp den Header vorher auszulesen macht das ganze nicht nur schöner zu lesen sondern
lässt die Anzeigezeit auf fast 0 sinken.
Interessant das eine so kleine Änderung so viel bewirkt.
Das geht zwar nur einmal in die Laufzeit ein, trotzdem schön.

Das t in show_plot ist eine Sammlung für die Legende, deshalb sieht das etwas komisch aus.
x und y sind nicht die Körper, das mach ich in der body_reade() sondern die bereits kalkulierten Planetenbahnen die aus x und y Koordinaten bestehen.
Ich speichere das ganze noch während der Simulation und muss es dann zum plotten wieder einlesen.

Zum berechnen auf nem DualCore:
Habs mit den process hin bekommen.
War sehr enttäuscht da er zwar beide Cores benutzt aber ca. 300x länger zur Berechnung brauchte. Vlt hab ich es aber auch falsch gemacht.

Code: Alles auswählen

  
def next(self):
   p = []
   for x in self.bodies:
        x.center_of_mass_posxmass = self.center_of_mass.pos       
        p.append(Process(target=x.hop_and_log()))
        for t in p:
            t.start()
        for t in p:
            t.join()
[EDIT]
Durch etwas umstellen der Berechnungen (insbesondere um / zu vermeiden) hab ich es nochmal von 18s auf 15s runter bekommen
BlackJack

@p90: Also für mich sieht das ``for x in self.bodies:`` da so aus, als wenn `Body`-Exemplare an das `x` gebunden werden!?

Beim `Process` machst Du zwei Sachen falsch: 1. Wird so für jeden Körper in der Schleife jeder Vorhergehende ja noch einmal berechnet. Mit der Folge dass Du quadratische Laufzeit und ein falsches Ergebnis hast, weil für die ersten Körper die Berechnung wesentlich öfter durchgeführt wird. Und 2. startest Du für die Berechnung für jeden Körper einen Prozess, was *noch* teurer ist als für jeden einen Thread zu starten. Ein Prozess sollte mehrere Körper berechnen. Wie gesagt, ich würde eine feste Anzahl von Prozessen starten -- zum Beispiel so viele wie Prozessoren beziehungsweise Kerne vorhanden sind -- und die Körper darauf verteilen. Schau Dir mal `Pool`-Objekte an.
p90
User
Beiträge: 198
Registriert: Donnerstag 22. Juli 2010, 17:30

Ah okay, ja.
Wenn du das x meinst dann hast du recht.

Habs leider immer noch nicht im Griff.

Code: Alles auswählen

        #pool = Pool()
        for x in self.bodies:
            x.center_of_mass_posxmass = self.center_of_mass.pos
            x.hop_and_log()
        #    pool.apply_async(x.hop_and_log())
        #    p.append(Process(target=x.hop_and_log()))
        #for t in p:
        #    t.start()
        #for t in pool:
        #    t.join()
        #pool.close()
        #pool.join()
Wenn ich einfach nur das async mache habe ich am ende 100000 Threads die das System schrotten. Wenn ich versuche da ein close und join einzubauen geht garnichts mehr.
Das Komisch ist, wenn ich nur 100 Hops simuliere funktioniert es, simuliere ich 1000 geht gar nichts mehr.
BlackJack

@p90: Für asynchron gestartete Funktionen gibt's anscheinend jeweils einen Thread zur Kommunikation mit dem Prozess, der solange besteht, bis das Ergebnis abgefragt wurde. Ich würde die unnötige Ergebnisliste in Kauf nehmen und das ganze Aufteilen `Pool.map()` überlassen (ungetestet):

Code: Alles auswählen

    def next(self):
        for body in self.bodies:
            body.center_of_mass_posxmass = self.center_of_mass.pos
        self.pool.map(lambda b: b.hop_and_log(), self.bodies)
Das `Pool`-Exemplar würde ich in `Bodies.__init__()` erzeugen, dann werden die Prozesse nur einmal gestartet und immer wiederverwendet, statt bei jedem `next()` neue Prozesse zu starten.
p90
User
Beiträge: 198
Registriert: Donnerstag 22. Juli 2010, 17:30

Oh man.
Ich geb es auf.
Egal was ich mache, entweder es geht gar nicht oder läuft nur langsam.
Dann doch besser so!
Nochmal danke für die Hilfe!
Antworten