In Screeps wird das CPU-Limit auf eine Art und Weise erzwungen, die es erlaubt, robuste CPU-Limits zu schreiben?

8

In Screeps ist die CPU-Nutzung für jeden Spieler begrenzt, aber die Dokumentation für diese Funktion macht die Art und Weise, wie dies durchgesetzt wird, nicht ausreichend klar, um robusten CPU-Code schreiben zu können. Ich habe die folgenden vier Möglichkeiten in Betracht gezogen:

1. Der Zyklus des Spielers wird niemals unterbrochen.

In einem Extremfall wird die Deserialisierung des Arbeitsspeichers des Spielers, die Ausführung des Hauptskripts und die erneute Serialisierung des Speichers niemals unterbrochen, und das Überschreiten des CPU-Limits bedeutet einfach, dass der Zyklus des Spielers bei nachfolgenden Ticks übersprungen wird, bis die CPU-Schulden zurückgezahlt sind. In diesem Fall ist ein robuster CPU-Limit-Code nicht unbedingt notwendig, aber es wäre dennoch nützlich, zu erkennen, wenn der Zyklus des Spielers übersprungen wird, und möglicherweise die Dinge effizienter zu machen. Dies kann leicht mit einem Code wie folgt erreicht werden:

%Vor%

Diese Art der Verwaltung der CPU-Auslastung von Spielern ist anfällig für Missbrauch durch Endlosschleifen, und ich bin mir fast sicher, dass dies nicht das Verhalten von Screeps ist.

2. Der Zyklus des Spielers ist atomar.

Die nächste Möglichkeit ist, dass der Zyklus des Spielers atomar ist. Wenn das CPU-Limit überschritten wird, wird der Zyklus des Spielers unterbrochen, aber weder der geplante Spielstatus ändert sich, noch werden die Änderungen am Speicher festgelegt. Es wird wichtiger, die Effizienz zu verbessern, wenn ein unterbrochener Zyklus erkannt wird, da das Ignorieren des Spiels es dem Skript des Spielers unmöglich macht, den Spielzustand oder Speicher zu ändern. Die Erkennung unterbrochener Zyklen ist jedoch immer noch einfach:

%Vor%

2.5. Änderungen am Speicher sind atomar, aber Änderungen an Game-Objekten sind nicht möglich.

BEARBEITEN: Diese Möglichkeit ist mir nach dem Lesen der Dokumentation für das Objekt RawMemory aufgefallen . Wenn das Skript unterbrochen wird, werden bereits geplante Änderungen am Spielstatus festgeschrieben, aber keine Änderungen am Speicher werden ausgeführt. Dies ist angesichts der von RawMemory bereitgestellten Funktionalität sinnvoll. Wenn das Skript vor der Ausführung einer benutzerdefinierten Speicherserialisierung unterbrochen wird, wird die Standard-JSON-Serialisierung ausgeführt, wodurch die benutzerdefinierte Speicherserialisierung komplizierter wird: Die benutzerdefinierte Deserialisierung muss verarbeitet werden können das Standard-JSON zusätzlich zu dem Format, das die benutzerdefinierte Serialisierung erstellt hat.

3. JavaScript-Anweisungen sind atomar.

Eine andere Möglichkeit ist, dass der Zyklus des Spielers nicht atomar ist, sondern JavaScript-Anweisungen. Wenn der Zyklus des Spielers unterbrochen wird, um das CPU-Limit zu überschreiten, werden unvollständige Spielstatusänderungen und Speicheränderungen festgeschrieben, aber mit sorgfältiger Codierung - eine Anweisung, die einen Aufruf des Screeps-API-Aufrufs einem Speicherschlüssel zuweist - den Spielstatus Änderungen und Speicheränderungen sind nicht miteinander inkonsistent. Das Schreiben von vollständigem CPU-Limit-robustem Code für diesen Fall scheint kompliziert - es ist kein Problem, das ich bereits gelöst habe, und ich möchte sicher sein, dass dies das wahre Verhalten von Screeps ist, bevor ich es versuche.

4. Nichts ist atomar.

Im anderen Extremfall sind nicht einmal einzelne Anweisungen atomar: Eine Anweisung, die das Ergebnis eines Screeps-API-Aufrufs einem Schlüssel im Speicher zuordnet, könnte zwischen dem Abschluss des Aufrufs und der Zuweisung des Ergebnisses und dem Unvollständigen unterbrochen werden Spielstatus ändert sich und die unvollständigen Speicheränderungen (die jetzt inkonsistent sind) werden ausgeführt. In diesem Fall sind die Möglichkeiten zum Schreiben von robustem CPU-Limit-Code sehr begrenzt. Obwohl das Vorhandensein des Werts, der von der folgenden Anweisung in den Speicher geschrieben wurde, ohne Zweifel den vollständigen Abschluss des Screeps-API-Aufrufs anzeigen würde, würde seine Abwesenheit nicht zweifelsfrei anzeigen, dass der Aufruf nicht abgeschlossen wurde:

%Vor%

Weiß jemand, welches das Verhalten von Screeps ist? Oder ist es etwas anderes, das ich nicht in Betracht gezogen habe? Das folgende Zitat aus der Dokumentation:
  

Das CPU-Limit 100 bedeutet, dass die Ausführung Ihres Skripts nach 100 ms beendet wird, auch wenn es noch keine Arbeit geleistet hat.

deutet an, dass es Fall 3 oder Fall 4 sein könnte, aber nicht sehr überzeugend.

Auf der anderen Seite, das Ergebnis eines Experiments im Simulationsmodus mit einem einzigen Kriechen, die folgende Hauptschleife, und wählen Sie "Beenden" im Dialogfeld für ein nicht reagierendes Skript:

%Vor%

war, dass das Kriechen nur auf Ticks verschoben wurde, wo die Endlosschleife übersprungen wurde, weil failedTicks ihren Schwellwert erreicht hatte. Dies weist auf Fall 2 hin, ist aber nicht schlüssig, da das CPU-Limit im Simulationsmodus anders ist als online - es scheint unendlich zu sein, wenn es nicht mit der Schaltfläche "Beenden" beendet wird.

    
TheBeardyMan 14.08.2016, 15:05
quelle

3 Antworten

1

Fall 4 standardmäßig, aber modifizierbar in Fall 2.5

Als Nehegeb und Dwurf vermutet, und Experimente mit einem privaten Server haben bestätigt, ist das Standardverhalten Fall 4. Änderungen an Spielzustand und Speicher, die vor der Unterbrechung stattgefunden haben, sind festgeschrieben.

Die Ausführung der Standard-JSON-Serialisierung durch die Server-Hauptschleife wird jedoch durch das Vorhandensein eines nicht dokumentierten Schlüssels '_parsed' in RawMemory gesteuert; Der Wert des Schlüssels ist ein Verweis auf Memory. Das Löschen des Schlüssels am Anfang der Hauptschleife des Skripts und das Wiederherstellen des Schlüssels am Ende bewirkt, daß der gesamte Satz von Speicheränderungen durch die Hauptschleife des Skripts, d.h. Fall 2.5:

, atomisiert wird %Vor%     
TheBeardyMan 16.03.2017, 02:45
quelle
2

Es ist nicht # 1 oder # 2. Ich wette, es ist # 4, es wäre am sinnvollsten, die CPU-Auslastung außerhalb der Hauptschleife zu überwachen und sie zu beenden, wenn das Limit erreicht ist. # 3 würde komplizierten Code im screps-Server erfordern, um "Statement-Level" -Transaktionen durchzuführen. Wie Sie herausgefunden haben, gibt es im Simulator kein CPU-Limit.

Die meisten Spieler lösen dieses Problem, indem sie einfach kritischen Code früh in ihre Hauptschleife setzen, z. Turmcode kommt zuerst, dann spawn Code, dann kriechen Bewegung / Arbeit. Dies schützt auch vor nicht abgefangenen Ausnahmen in Ihrem Code, da Ihre wichtigsten Funktionen (hoffentlich) bereits ausgeführt wurden. Dies ist eine schlechte Lösung für das CPU-Limit, aber nach meiner Beobachtung scheint es so, als ob Ihr Code jeden zweiten Tick übersprungen wird, sobald Sie die gesamte CPU in Ihrem Bucket verwendet haben und ständig Ihr reguläres Limit erreichen.

Ich habe momentan kein CPU-Problem (ich habe ein Abonnement), aber ich würde das lösen, indem ich CPU-intensiven Code ans Ende lege und ihn, wenn möglich, nur dann ausführe, wenn Sie genug CPU in Ihrem System haben Bucket und Sie sind nicht in der Nähe Ihrer Grenze von 500 CPU pro Tick. Es hilft auch, größere Creeps zu haben, es ist üblich entweder Pfadfindung oder sogar nur Bewegung (0,2 pro Bewegung), um einen ordentlichen Teil der CPU aufzunehmen und größere Creeps bedeutet weniger Creeps.

    
dwurf 12.09.2016 22:45
quelle
2

Einer der Ingame-Tipps des Tages sagt:

%Vor%

Deshalb würde ich sagen, es ist höchstwahrscheinlich # 4 !
Genau wie Dwurf sagt, sollte der folgende Ansatz für das Skript-Layout in den meisten Fällen den Erfolg bringen:

  

Die meisten Spieler lösen dieses Problem, indem sie einfach kritischen Code früh in ihre Hauptschleife setzen, z. Turmcode kommt zuerst, dann spawn Code, dann kriechen Bewegung / Arbeit. [...]

    
nehegeb 15.11.2016 15:44
quelle

Tags und Links