0

Ich habe eine Swift-App auf macOS. Batch-verarbeitet Bilder. Ich weiß nicht im Voraus, wie groß diese Bilder sein werden und auf welcher Hardware meine App läuft - diese sind beide benutzerabhängig.Wie kann ich meine Mac OS App Tasks so weit wie möglich parallelisieren, ohne den Arbeitsspeicher zu verlieren?

Ich benutze GCD, um die Verarbeitung der Bilder zu parallelisieren, da es den Durchsatz wirklich beschleunigen kann.

In einigen Fällen kann jedoch zu viel Parallelisierung schaden: Wenn der Benutzer hochauflösende Bilder verarbeitet, erzeugt die Parallelisierung zu viel Speicherdruck und die Systemleistung wird sehr schlecht.

Also ich würde gerne einen Weg finden, "meine parallele Task-Prozessor" mit einer Geschwindigkeit, die Parallelisierung maximiert, während die Arbeitsbelastung im RAM (so dass es keine Trigger zum Auslagern & Swapping: Ich möchte vermeiden Festplatte IO).

Irgendwelche Ideen, wie man das macht?

+0

Wie parallelisieren Sie? Ich meine, teilen Sie jedes Bild über die verfügbaren Kerne oder verarbeiten Sie mehrere Bilder parallel? –

+0

Ich verarbeite mehrere Bilder parallel. Wenn ich die Arbeit für ein Bild parallelisieren könnte, wäre dieses Problem nicht so wichtig. Vielleicht muss ich mich mehr anstrengen (es hat auch den Vorteil geringerer Latenz, wenn wir nur ein Bild verarbeiten), aber die Frage steht immer noch. – BearOverflow

+0

Update: Was ich befürchtet habe, ist wahr - der Teil, der parallelisiert werden kann, profitiert nicht so sehr von der Parallelisierung. Der Overhead beim Erstellen von parallelen Tasks nimmt die Vorteile der CPU-Effizienzgewinne weg. Der beste Parallelisierungsfaktor für diesen Teil ist 2 (darüber hinaus werden keine Gewinne mehr erzielt), was nett ist, aber es macht erst einen Unterschied mit massiven Bildern (8192x8192), was nicht der nominelle Anwendungsfall ist. Für den nominalen Anwendungsfall glaube ich, dass die geringfügige Verbesserung die Komplexität des Codes nicht gewährleistet. Die Frage steht also wirklich noch. – BearOverflow

Antwort

0

Ich endete mit der Implementierung einer TokenBucket-Art von Singleton, die mit der Zulassung Kontrolle auf der Grundlage der Speicheranforderungen behandelt. Es wird so initialisiert, dass 80% des RAM von meiner App genutzt werden können.

Wenn jemand eine speicherintensive Operation durchführen möchte, muss er() Speicher anfordern. Wenn nicht genug Speicher vorhanden ist, blockiert der Anruf, bis es da ist. Danach muss der Thread den Speicher freigeben().

Code:

class MemoryGate { 

    private let maxBytes : UInt64 
    private var availableBytes : Int64 

    private let cv = NSCondition() 

    init(maxBytes: UInt64) { 
     self.maxBytes = maxBytes 
     self.availableBytes = Int64(maxBytes) 
     Log.debug?.message("maxBytes=\(maxBytes)") 
    } 

    public func request(amount: UInt64) { 
     Log.debug?.message("Resquesting \(amount) bytes") 
     cv.lock() 

     // If the amount is bigger than the max allowed, no amount of waiting is going 
     // to help, so we go through and let the other smaller jobs be held back until 
     // memory is freed 
     if (amount <= maxBytes) { 
      while (availableBytes < Int64(amount)) { 
       cv.wait() 
      } 
     } 

     availableBytes -= Int64(amount) 

     Log.debug?.message("Got \(amount) bytes. availableBytes=\(availableBytes)") 
     cv.unlock() 
    } 

    public func release(amount: UInt64) { 
     cv.lock() 
     availableBytes += Int64(amount) 
     Log.debug?.message("Released \(amount) bytes. availableBytes=\(availableBytes)") 
     cv.broadcast() 
     cv.unlock() 
    } 
} 
0

Es gibt eine GCD dispatch source für Speicherdruckereignisse. Nicht sicher, wie Ihr Code strukturiert ist, aber wäre es möglich, parallele Aufgaben zu erstellen, bis Sie ein Ereignis DISPATCH_MEMORYPRESSURE_WARN erhalten, dann aufhören, Aufgaben zu machen oder sogar einige zu erledigen?

+0

Das ist ein bisschen schwierig, weil ich in der Stapelverarbeitung eine Liste von Fotos bekomme, die der Benutzer verarbeiten möchte (sagen wir 500), und davon muss ich sie in eine DispatchQueue werfen, die von GCD verwaltet wird. Angeblich weiß GCD am besten, wie es die verfügbaren CPU-Ressourcen verwenden wird, also sollte ich sie einfach alle für GCD werfen, um seine Arbeit zu erledigen. Aber sobald sie drin sind, leben sie ihr Leben: Ich glaube nicht, dass ich sie zurücknehmen kann. Dann fangen alle an, Speicher zuzuweisen. Bis ich DISPATCH_MEMORYPRESSURE_WARN bekomme, ist es zu spät - die Aufgaben werden bereits zur Verarbeitung an GCD gesendet. – BearOverflow

Verwandte Themen