Ich hab vorhin mal eine kleine Funktion geschrieben, welche es ermöglicht, eine For-Schleifen artige Struktur beliebig zu parallelisieren, erstmal hier der Code, kann einfach importiert werden:
BlitzMax: [AUSKLAPPEN] [EINKLAPPEN] SuperStrict
Import BRL.Threads Import BRL.LinkedList
?Threaded Private Type TForPart Field _from:Long Field _to:Long Field _step:Int Method ToString:String() Return "~qFor i = "+_from+" To "+_to+" Step "+_step+"~q" EndMethod EndType
Type TForSheduler Field mut:TMutex Field list:TList Field curLink:TLink Method New() mut = TMutex.Create() list = New TList curLink = Null EndMethod Method Init(pfrom:Long, pto:Long, pstep:Int, psize:Int) mut.Lock() list.Clear() Local pstart:Long = pfrom While pfrom <= pto Local part:TForPart = New TForPart part._from = pfrom + ((pfrom-pstart) Mod pstep) If part._from > pto Then Exit part._to = pfrom + psize - 1 If part._to > pto Then part._to = pto part._step = pstep list.AddLast(part) DebugLog "Added Part: "+part.ToString() pfrom :+ psize Wend curLink = list.FirstLink() mut.Unlock() EndMethod Method Reset() mut.Lock() curLink = list.FirstLink() mut.Unlock() EndMethod Method GetPart:TForPart() Local r:TForPart = Null mut.Lock() If curLink <> Null Then r = TForPart(curLink.Value()) curLink = curLink.NextLink() EndIf mut.Unlock() Return r EndMethod EndType Type TCont Field func(i:Long) Field shed:TForSheduler EndType Public ?
Function PForLeg(_from:Long, _to:Long, _step:Int, func(i:Long), _size:Int = 5, threadCount:Int = 4) If _to < _from Or _step < 1 Or _size < 1 Or threadCount < 1 Then Throw "Invalid Arguments"
Local j:Long = _from While j <= _to func(j) j :+ _step Wend EndFunction
Function PFor(_from:Long, _to:Long, _step:Int, func(i:Long), _size:Int = 5, threadCount:Int = 4) If _to < _from Or _step < 1 Or _size < 1 Or threadCount < 1 Then Throw "Invalid Arguments" ?Not Threaded PForLeg(_from, _to, _step, func, _size, threadCount) ?Threaded If threadCount = 1 Then PForLeg(_from, _to, _step, func, _size, threadCount) Return EndIf
Local shed:TForSheduler = New TForSheduler shed.Init(_from, _to, _step, _size) Function itf:Object(par:Object) Local o:TCont = TCont(par) Assert o, "Internal Sheduler Error" Local shed:TForSheduler = o.shed Local func(i:Long) = o.func Local part:TForPart = shed.GetPart() While part <> Null Local k:Long = part._from While k <= part._to func(k) k :+ part._step Wend part = shed.GetPart() Wend Return Null EndFunction Local threads:TThread[] = New TThread[threadCount] For Local j:Int = 0 Until threadCount Local o:TCont = New TCont o.func = func o.shed = shed threads[j] = TThread.Create(itf, o) Next For Local t:TThread = EachIn threads t.Wait() Next ? EndFunction
Hier ein simples Verwendungs-Beispiel:
BlitzMax: [AUSKLAPPEN] [EINKLAPPEN] SuperStrict
Framework BRL.Blitz
Import "pfor.bmx"
Function DoFor(i:Long) WriteStdout("Value "+i+" in Thread "+CurrentThread().ToString()+"~n") Delay 100 EndFunction
Local t:Int = MilliSecs() PFor(1,50, 1, DoFor, 2) t = MilliSecs()-t
WriteStdout("Took "+(t)+"ms~n~n")
t=MilliSecs() PForLeg(1,50, 1, DoFor, 2) t = MilliSecs()-t WriteStdout("Took "+(t)+"ms~n~n")
Hier sieht man auch schön, dass er weniger zeit für die zeitfressende aufgabe braucht, wenn man es parallel ausführt. Bei echter last sähe das natürlich ein wenig anders aus, aber solang noch eine CPU frei ist, ist auch das kein Thema.
Die funktion, die man der PFor funktion übergibt, MUSS komplett thread-safe sein, sonst fliegt einem das ding um die Ohren.
Ich bitte um tests, da ich mir vorallem beim aufteilungs-algo der for-loop noch unsicher bin, ob er in allen Fällen korrekt arbeitet.
Mfg
Edit: Hab das ganze als Modul verpackt, ist auf meinem GIT zu finden.
|