Python Programm wird mit Multihreading nicht schneller? (Auslastung steigt nicht)

NJay

Moderator
Mitarbeiter
Team
Mitglied seit
19 März 2012
Beiträge
5.313
Danke
302
#1
Hallo,

ich habe mich gestern mal wieder ran gesetzt um mein Python etwas aufzubessern. Ich habe angefangen ein kleines Game zu schreiben, bei denen man zwei Armeen aufeinanderhetzt und ein Ergebnis ausgerechnet wird.

Dann dachte ich, ich schreibe eine kleine Simulation, die Gefechte X-Fach ausrechnet, damit ich auswirkungen von Gameplayänderungen direkt analysieren kann. Da ich sehr viele Objekte habe, dauert die Berechnung sehr lange und lastet einen Kern zu 100% aus. Also dachte ich mir, schaue ich mir mal Multithreading an. Nun habe ich das Programm auf Multithreading portiert, doch es läuft nicht schneller, sondern die Last verteilt sich nur auf mehrere Threads...

Bedeutet, statt 100% auf einem kern verteilt sich die Last auf die Anzahl der Threads die ich gestertet habe. Es wird dadurch aber nicht schneller.

Ich vermute ich habe irgendwas Grundsätzliches falsch gemacht.



Hat jemand von euch eine Idee? Bei Fragen zum Code einfach fragen. Bin kein Python Profi, sieht also bestimmt manches komisch aus. :D

Python:
from random import randint
import threading
from queue import Queue

count_lock = threading.Lock()
q = Queue()

class Soldier():
    def __init__(self, strength_in, max_hp_in):
        self.strength = strength_in
        self.max_hp = max_hp_in
        self.current_hp = max_hp_in


class Battle(threading.Thread):
    def __init__(self, team_a_in, team_b_in):
        threading.Thread.__init__(self)
        self.team_a = team_a_in
        self.team_b = team_b_in
        self.daemon = True
        self.start()

    def run(self):
        fight(team_a, team_b)


def fight(team_a_fight, team_b_fight):
    while len(team_a_fight) != 0 and len(team_b_fight) != 0:
        bonus_a = 1 + randint(-15, 15)/100  # calculates random attack bonus/penalty for team a
        bonus_b = 1 + randint(-15, 15)/100  # calculates random attack bonus/penalty for team b

        #bonus_a = 1
        #bonus_b = 1

        for soldier in team_a_fight:
            soldier_gets_hit = randint(0, len(team_b_fight) - 1)  # pick a random soldier which gets hit
            team_b_fight[soldier_gets_hit].current_hp = team_b_fight[soldier_gets_hit].current_hp - soldier.strength * bonus_a  # calculate damage

        for soldier in team_b_fight:
            soldier_gets_hit = randint(0, len(team_a_fight) - 1)  # pick a random soldier which gets hit
            team_a_fight[soldier_gets_hit].current_hp = team_a_fight[soldier_gets_hit].current_hp - soldier.strength * bonus_b  # calculate damage

        for soldier in team_a_fight:
            if soldier.current_hp <= 0:  # check for dead soldiers and remove them
                # print(soldier.current_hp)
                team_a_fight.remove(soldier)

        for soldier in team_b_fight:
            if soldier.current_hp <= 0:  # check for dead soldiers and remove them
                team_b_fight.remove(soldier)

        # print("************************")
        # print("Number of soldiers in team a:" + str(len(team_a)))
        # print("Number of soldiers in team b:" + str(len(team_b)))
        # print("************************")
    # print("************************")
    # print("Number of soldiers in team a:" + str(len(team_a)))
    # print("Number of soldiers in team b:" + str(len(team_b)))
    # print("************************")
    with count_lock:
        print("Active threads: " + str(threading.active_count()) + " queue size " + str(q.qsize()))
        if len(team_a_fight) == 0:
            global win_counter_a
            win_counter_a += 1
        elif len(team_b_fight) == 0:
            global win_counter_b
            win_counter_b += 1
        else:
            global draw_counter
            draw_counter += 1
    q.task_done()


number_of_simulations = 16  # aka. number of threads
win_counter_a = 0
win_counter_b = 0
draw_counter = 0
iterator = 0

while iterator < number_of_simulations:

    team_a = []
    team_b = []

    # number_of_soldiers_a = input("How many soldiers should team A have? ")
    # number_of_soldiers_b = input("How many soldiers should team B have? ")

    number_of_soldiers_a = 1000000
    number_of_soldiers_b = 1000000

    for i in range(number_of_soldiers_a):
        team_a.append(Soldier(10, 50))
        i += 1

    for i in range(number_of_soldiers_a):
        team_b.append(Soldier(10, 50))
        i += 1

    #print("************************************")
    #print("Number of soldiers in team A:" + str(len(team_a)))
    #print("Number of soldiers in team B:" + str(len(team_b)))
    #print("FIGHT!")

    #battle_1 = Battle(team_a, team_b)
    q.put(Battle(team_a, team_b))
    print("Queue size " + str(q.qsize()))
    # fight(team_a, team_b)

    iterator += 1

print(q.qsize())
# print(threading.active_count())
q.join()

print("Number of wins A: " + str(win_counter_a))
print("Number of wins B: " + str(win_counter_b))
print("Number of draws: " + str(draw_counter))
 

Amari

Administrator
Mitarbeiter
Team
Mitglied seit
16 April 2006
Beiträge
6.473
Danke
407
#2
Mein Wissen was, das angeht ist etwas beschränkt, aber soweit ich weiß taugt Multithreading bei Python nur für blocking I/O. Da du quasi null i/o hast und eher von der Rechenleistung abhängig bist, wirst du mit multi threading die Performance deines Programmes nicht steigern können. Hierzu das hier:

Global interpreter lock - Wikipedia

Und vielleicht ist der Artikel hilfreich. Ka, einfach mal lesen ;)
An introduction to parallel programming using Python's multiprocessing module

Sprich, probier dich an dem multiproccessing python modul :)
 

NJay

Moderator
Mitarbeiter
Team
Mitglied seit
19 März 2012
Beiträge
5.313
Danke
302
#3
Mhmm... Das wurde in den ganzen Tutorials nicht erwähnt. Vielleicht sollte ich die Simulation doch lieber in C++ schreiben :D

Danke für den Tipp
 
Oben