Jump to content
Unity Insider Forum

Unity 2017 und .NET 4.6


runner78

Recommended Posts

Seit einigen Wochen gibt es ja schon die Beta von Unity 2017.1 mit experimenteller Unterstützung der .Net Profil 4.6.
Hat jemand damit schon mehr Erfahrung gemacht und etwas experimentiert?

Ich habe erst gestern die Beta2 das erste mal ausprobiert und freue mich, dass endlich Task und asynk/await in Unity Einzug hält.
Hier wäre interessant zu wissen, was mit den neuen Möglichkeiten sich alles besser und einfache Realisieren lässt, oder was man besser sein lässt.

Interessant wären zum Beispiel Strukturen wie:

void Start()
{
    Mesh mesh = null;
    Task.Run(async () =>
    {
        await CreateMeshAsync((m) =>
        {
            mesh = m;
        });
        gameObject.AddComponent<MeshFilter>().mesh = mesh;
    });
}

Funktioniert leider nicht, da man nicht mehr im Main Thread ist.

Laut Forum https://forum.unity3d.com/threads/future-plans-for-the-mono-runtime-upgrade.464327/ gibt es zwar einen synchronization context, aber entweder ist er noch nicht implementiert, ich finde ihn nicht, oder ich habe etwas falsch verstanden.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Habe noch ein wenig experimentiert mit await in den Start und Update Methoden.

async void Start()
{
    Mesh mesh = await CreateMeshAsync();
}

Sofern in der CreateMeshAsync kein neuer Thread mit Task.Run() erzeugt wird, läuft die Methode asynchron auf dem Mainthread und funktioniert.

Ein einfacher Test mit einem Delay in der Update:

async void Update () {
    await Task.Delay(1000);
}

 

Beide Beispiel haben eines gemeinsam, was zukünftig gut beachtet werden muss.
Durch das await werden die Methoden vom aktuellem Frame entkoppelt, dadurch wird Code nach dem await in einem ganz anderen Frame ausgeführt, als der Code davor.
Es auch kann passieren, dass die Awake -> Start -> Update Reihenfolge nicht mehr stimmt, oder der Time.deltaTime Wert passt nicht mehr in den Context, da zwischenzeitlich mehrere Frames übersprungen wurden.

So dreht man die Reihenfolge um:

async void Awake()
{
    await Task.Delay(5000);
    Debug.Log("Await");
}
async void Start()
{
    await Task.Delay(3000);
    Debug.Log("Start");
}
void Update()
{
    Debug.Log("Update");
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 7 months later...
Am 28.4.2017 um 13:10 schrieb runner78:

 


void Start()
{
    Mesh mesh = null;
    Task.Run(async () =>
    {
        await CreateMeshAsync((m) =>
        {
            mesh = m;
        });
        gameObject.AddComponent<MeshFilter>().mesh = mesh;
    });
}

Funktioniert leider nicht, da man nicht mehr im Main Thread ist.

Laut Forum https://forum.unity3d.com/threads/future-plans-for-the-mono-runtime-upgrade.464327/ gibt es zwar einen synchronization context, aber entweder ist er noch nicht implementiert, ich finde ihn nicht, oder ich habe etwas falsch verstanden.

Ein ziemlicher Schwachsinn, den ich da geschrieben hatte. Wenn man die Meshzuweisung im Task.Run() macht, ist man natürluch in einem anderen Thread, und da hilft auch der beste synchronization context nichts. Der stellt nur Sicher, dass man nach einem await wieder im MainThread ist.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Man konnte auch ohne die Klasse Task ja bereits Multithreading in Unity betreiben, daher bin ich mir auch nicht sicher, was mit der Klasse Task nun besser wird, ich vermute mal es vereinfacht nur einiges.

Hier eine nette Klasse fürs Multithreading, ich könnte mir vorstellen, daß es sich mit der Klasse Task vielleicht einiges vereinfachen bzw. verkürzt implementieren lässt?
http://wiki.unity3d.com/index.php/JobQueue

Link zu diesem Kommentar
Auf anderen Seiten teilen

Zum einen Vereinfacht Task das arbeiten mit Threads, aber macht auch mehr.
Tasks verwenden intern einen ThreadPool, damit laufen die Threads schon, wenn man eine Aufgabe z.B mit Task.Run() ausführt, wird sie über dem TaskScheduler den erstbesten laufenden Thread im ThreadPool übergeben und es muss nicht erst ein Thread manuell angelegt und gestartet werden.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 16 Minuten schrieb runner78:

Zum einen Vereinfacht Task das arbeiten mit Threads, aber macht auch mehr.
Tasks verwenden intern einen ThreadPool, damit laufen die Threads schon, wenn man eine Aufgabe z.B mit Task.Run() ausführt, wird sie über dem TaskScheduler den erstbesten laufenden Thread im ThreadPool übergeben und es muss nicht erst ein Thread manuell angelegt und gestartet werden.

Das gleiche macht oben die Klasse "JobQueue" ja auch. Aber klar so müsste man nicht "JobQueue"  verwenden oder eine eigene Klasse schreiben, die Threads verwaltet (pooled) und man eine Klasse für Threadhandling als Bestandteil von Unity zur Verfügung.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Task ist aber Bestandteil des .Net Framework und somit verwenden mittlerweile viele .net Bibliotheken Task.

vor allem aber werden Task bei async/await verwendet, damit lässt sich sehr einfach kompakten asynchronen code schreiben.

Die Task Klasse ist übrigens etwas komplexer :)
https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/Threading/Tasks/Task.cs

Link zu diesem Kommentar
Auf anderen Seiten teilen

Naja, sobald man mit Threads arbeitet ist nie was einfach. Ich denke die Klasse Task kann schon dabei helfen, aber vor allem in Verbindung mit Unity gibt es viele Fallstricke, da eben die Unitykernelroutinen oft zum einen nicht Threadsave sind und zum anderen den Mainthread blockieren können und natürlich hat eine Klasse wie Task mehr Codezeilen und mehr Schnittstellen, aber alle brauchen tut man die vermutlich nicht. Aber auch mit "Task" oder "JobQueue" ist es generell nicht so einfach "Dinge" auszulagern und diese wieder mit dem Unity Mainthread zu verbinden. Man muss eben wissen was man tut. Was ich aber sagen wollte, man braucht eben nicht unbedingt "Task", da man eben auch andere Klassen verwenden kann.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Diesen Task-Fetisch verstehe ich beim besten Willen nicht. Ja, er hat nette Keywords als syntactic sugar. Ja, man kann damit Dinge aus Threads zurückgeben anstatt sie in irgendeine Variable zu schreiben und dann im anderen Thread wieder auszulesen. Aber mal ernsthaft: Wer nicht komplett auf den Kopf gefallen ist, kommt auch wunderbar ohne aus.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dass Unity in viele Bereich nicht Threadsafe ist, ändert sich hoffentlich mit dem neuem Entity Component System, da es ja auch für eine gute Zusammenarbeit mit dem c# Job System ausgelegt ist. Wirklich einfach wird Multithreading aber nie.

Task sollen eben in erster Linie das Arbeiten mit Multithreading erleichtern. Wer darauf verzichten will, kann das gerne tun, aber ob es Sinn macht die Möglichkeiten des Frameworks zu ignorieren und anstatt kompakten leichter lesbaren und meist weniger Fehleranfälligen Code zu schreiben, und sich lieber mit einer fast 12 Jahren alten Technik rumplagt, ist dahingestellt.

async/await ist übrigens nicht gleichzusetzen mit Multithreading, in .net gibt es einige Bibliotheken die awaitable sind, aber trotzdem auf dem Mainthread laufen, ohne ihn zu blockieren. In der Regel sind es die ganzen Netzwerk und I/O Schnittstellen. Du kannst auch deine eigene Klasse Erstellen auf die man awaiten kann, das ist nicht auf Task beschränkt.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich hab bisher noch keinen Task-Code gesehen, der kompakter oder leichter lesbar gewesen wäre als mein Thread/Threadpool-Code. Das sind jeweils Oneliner.

Was die Fehleranfälligkeit angeht, weiß ich noch nicht so recht, wo Tasks die großartig reduzieren sollen. Wenn du wirklich geringe Fehleranfälligkeit in Multithreading haben willst, bau dir deinen kritischen Code in Rust und importiere dir die Library, die du da rauskompilierst.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 15 Stunden schrieb Zer0Cool:

Man konnte auch ohne die Klasse Task ja bereits Multithreading in Unity betreiben, daher bin ich mir auch nicht sicher, was mit der Klasse Task nun besser wird, ich vermute mal es vereinfacht nur einiges.

Hier eine nette Klasse fürs Multithreading, ich könnte mir vorstellen, daß es sich mit der Klasse Task vielleicht einiges vereinfachen bzw. verkürzt implementieren lässt?
http://wiki.unity3d.com/index.php/JobQueue

Nicht nur einfacher, auch Effizienter wenn man nur Task verwendet.

Beispiel JobQueue, auf meinem PC dauert die Ausführung durchschnittlich 2200 Millisekunden:

 public class CalcLimit2 : JobItem
    {
        public string CustomName;
        public int count = 5;
        public float Result;
        protected override void DoWork()
        {
            float v = 0;
            for (int i = 0; i < this.count; i++)
            {
                v += Mathf.Pow(0.5f, i);
                if ((i % 100) == 0 && this.IsAborted)
                    return;
            }
            this.Result = v;
        }
        public override void OnFinished()
        {
            //UnityEngine.Debug.Log("Job: " + CustomName + " has finished with the result: " + Result);
        }
    }

    public class JobQuereEx : MonoBehaviour
    {
        private JobQueue<CalcLimit2> queue;
        Stopwatch watch;
        bool onWork = false;

        private void Start()
        {
            int maxThread;
            int o;
            ThreadPool.GetMaxThreads(out maxThread, out o);

            JobQueueExample(maxThread);
        }

        void Update()
        {
            this.queue.Update();
            if (this.onWork && this.queue.CountActiveJobs() == 0)
            {
                this.watch.Stop();
                UnityEngine.Debug.Log($"JobQueue Elapsed Milliseconds : {this.watch.ElapsedMilliseconds}");
                this.onWork = false;
            }
        }

        void OnDisable()
        {
            this.queue.ShutdownQueue();
        }
        private void JobQueueExample(int maxThread)
        {
            UnityEngine.Debug.Log($"Start JobQueue with {maxThread} Threads");
            this.watch = Stopwatch.StartNew();
            this.queue = new JobQueue<CalcLimit2>(maxThread);
            for (int i = 0; i < maxThread; i++)
            {
                this.queue.AddJob(new CalcLimit2 { count = 50000, CustomName = "over 9001" });
            }
            this.onWork = true;
        }
    }

Gleiche Logik mit Task, auf meinem PC durchschnittlich 850 Millisekunden:

public class TaskTest : MonoBehaviour
    {
        private void Start()
        {
            int maxThread;
            int o;
            ThreadPool.GetMaxThreads(out maxThread, out o);

            TaskExample(maxThread);
        }

        private void TaskExample(int maxThread)
        {
            var tokenSource = new CancellationTokenSource();
            var token = tokenSource.Token;

            UnityEngine.Debug.Log($"Start {maxThread} tasks");
            var watch = Stopwatch.StartNew();
            Task[] tasks = new Task[maxThread];
            for (int i = 0; i < tasks.Length; i++)
            {
                tasks[i] = Task.Run(() => CalcLimit2(50000, token), token);
            }
            Task.WaitAll(tasks);
            watch.Stop();
            UnityEngine.Debug.Log($"Task Elapsed Milliseconds : {watch.ElapsedMilliseconds}");
        }

        private float CalcLimit2(int count, CancellationToken token)
        {
            float v = 0;
            for (int i = 0; i < count; i++)
            {
                v += Mathf.Pow(0.5f, i);
                if ((i % 100) == 0 && token.IsCancellationRequested)
                    return default(float);
            }
            return v;
        }
    }

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich find's n bisschen krumm mit Performance zu argumentieren, solange man sich überhaupt in C# befindet. Also entweder, ich benutze nen kleinen One-Liner und lebe damit, dass das ein bisschen läger dauert, oder ich bau was in einer Sprache, die überhaupt erstmal performant sein kann, bau mir eine Library damit und linke die.

Klar, Tasks können gerne hier und da ein kleines bisschen besser sein als Threads direkt zu benutzen. Was ich nur nicht ralle ist, wieso wegen dieser kleinen Verbesserung einige Leute so hart auf .net 4.6 abgehen. Das Zeug hat ConcurrentDictionarys, die sind auch recht praktisch, und da hyped keiner als gäb's kein Morgen mehr.

Hier hast du übrigens ein Code-Beispiel, wie das bei mir aussieht:

private IEnumerator DoStuff()
{
  yield return new CoroutineThread(() => Thread.Sleep(1000));
  Debug.Log("Done and back in Unity.");
}

Die CoroutineThread-Klasse hatte ich hier schonmal gepostet. Gibt's auch in 'ner Threadpool-Ausführung, und mit mehreren Threads auf einmal.

Bin halt nicht der Meinung, dass async/await da noch groß Potential haben, das noch kompakter zu machen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Du unterschlägst hier den Aufruf der Methode, also 2 Zeilen in zwei Methoden.

Dieses Beispiel hattest du in einem anderen Thema gepostet:

private void Start()
{
  StartCoroutine(DoSomething());
}

private IEnumerator DoSomething()
{
  yield return new CoroutineThread(() => Thread.Sleep(1000));
  print("The thread has finished sleeping.");
}

Mit Task:

private void Start()
{
  Task.Run(() => Thread.Sleep(1000));
}

oder:

private async void Start()
{
  await Task.Run(() => Thread.Sleep(1000));
  print("The thread has finished sleeping.");
}

oder wenn man unbedingt 2 Methoden haben will:

private void Start()
{
  DoSomething();
}

private async Task DoSomething()
{
  await Task.Run(() => Thread.Sleep(1000));
  print("The thread has finished sleeping.");
}

Alles mit .net Boardmitteln, ich brauche keine zusätzlichen eigene Klassen mehr.

Deine TheadPool Variante habe ich jetzt nicht gefunden.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 39 Minuten schrieb runner78:

Du unterschlägst hier den Aufruf der Methode, also 2 Zeilen in zwei Methoden.

Ja, weil ich ein nach Abschluss der Aufgabe in einer Coroutine weiterarbeiten will.

Wenn ich wie in deinem Beispiel nach Abschluss des Tasks nichts mehr vorhabe, kann ich mir das StartCoroutine und das yield return selbstverständlich schenken.

vor 42 Minuten schrieb runner78:

Deine TheadPool Variante habe ich jetzt nicht gefunden.

Die Threadpool-Variante wird genauso aufgerufen wie die andere. Ist nur halt ne andere Klasse. Beide sind nur ein paar Zeilen lang.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich bin ehrlich gesagt kein Freund der Coroutine, die  einem Asynchronität vorgaukeln, wo keine ist.
Die einzige Sinnhafte Anwendung von Coroutinen, die mir spontan einfällt ist so wie du sie Verwendest hast, um auf das beenden eine echten Asynchronen Aufgabe zu warten. Dafür gibt es aber ab .net 4.5 async/await.

vor 17 Minuten schrieb Sascha:

Wenn ich wie in deinem Beispiel nach Abschluss des Tasks nichts mehr vorhabe, kann ich mir das StartCoroutine und das yield return selbstverständlich schenken.

Dafür hättest du auch Thread direkt Verwenden können.

void Start()
{
    new Thread(new ThreadStart(() => Thread.Sleep(1000))).Start();
}

Wie gehst du eigentlich vor, wenn du ein Ergebnis erwartest?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Zu glauben, Coroutinen währen auf anderen Threads, ist ein Anfängerfehler. Aber sorry, zu glauben, die erfüllen keinen Zweck, ist auch einer.

Mit Coroutinen kannst du som vielen Code so viel kompakter machen als mit allen anderen zur Verfügung stehenden mitteln. Es ist ein Ersatz für hässlichen Code in Update mit hässlichen Objektfeldern, nicht mehr und nicht weniger.

vor 23 Minuten schrieb runner78:

Dafür hättest du auch Thread direkt Verwenden können.

Ist mir schon klar. Hab ich auch nirgendwo anders gemacht.

vor 23 Minuten schrieb runner78:

Wie gehst du eigentlich vor, wenn du ein Ergebnis erwartest?

Das ist ja eine der geilen Sachen an Coroutines. Die lokalen Variablen darin sind zwar lokal und lungern damit nicht irgendwo dauerhaft im Heap rum, überleben aber beliebig viele Frames.

Deshalb kann man ganz einfach lokale Variablen im Thread beschreiben:

var results = new int[1000];
yield return new CoroutineThread(() =>
{
  for(var i = 0; i < results.Length; i++)
  {
    results[i] = Random.Range(0,1000);
    Thread.Sleep(10);
  }
});
DoSomethingWith(results);

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 4 Stunden schrieb Helishcoffe:

Wie kann man denn 4.6 in Visual Studio überhaupt nutzen? Habe die API in den Build Settings auf 4.6 eingestellt aber intelliSense sagt mir immer noch dass z.B. System.Threading.Tasks nicht existiert. 

Nach dem Umstellen in den PlayerSettings sollte nach dem Neustart von Unity die Projekt Datei automatisch auf 4.6 umgestellt sein. Eventuelle noch mal auf 3.5 und dann auf 4.6 Stellen.

 

vor 20 Stunden schrieb Sascha:

Das ist ja eine der geilen Sachen an Coroutines. Die lokalen Variablen darin sind zwar lokal und lungern damit nicht irgendwo dauerhaft im Heap rum, überleben aber beliebig viele Frames.

Deshalb kann man ganz einfach lokale Variablen im Thread beschreiben:


var results = new int[1000];
yield return new CoroutineThread(() =>
{
  for(var i = 0; i < results.Length; i++)
  {
    results[i] = Random.Range(0,1000);
    Thread.Sleep(10);
  }
});
DoSomethingWith(results);

 

In deinem Beispiel benutzt du eine Closure, also schwirrt die Variable tatsächlich irgendwo im Heap rum ;) (Indirekt)
Der Compiler erzeugt bei einer Closure im Hintergrund eine Klasse, wo er die Variable abspeichert. Lokale Variablen werden bekanntlich beim Verlassen des Gültigkeitsbereichs gelöscht, Damit sie aber in einer Closure zur Verfügung stehen müssen die ja irgendwo gespeichert bleiben. Hier schön erklärt inklusive was sich mit C# 7 in dieser Hinsicht besser wird. https://www.youtube.com/watch?v=-3274JsdtOQ (ab ca. 28:00) Das ist allerdings ein generelles Problem.

Hier das Beispiel mit Task ohne Closure:

var results = await Task.Run(() => {
    var innerResults = new int[1000];
    for (var i = 0; i < innerResults.Length; i++)
    {
        innerResults[i] = Random.Range(0, 1000);
        Thread.Sleep(10);
    }
    return innerResults;
});
DoSomethingWith(results);

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Danke, ich weiß wie Closures funktionieren.

Ich hab auch nicht gesagt, dass die Variablen nicht im Heap sind, sondern dass sie nicht dauerhaft im Heap sind. Ist schon ein Unterschied.

Ich wiederhole mich auch gerne nochmal: Ich habe nie gesagt, dass Tasks keine Vorteile gegenüber direkten Threads hätten. Meine Aussage von Anfang an war, dass ich nicht verstehe, warum alle so tun, als würde damit alles anders werden. Der Hype um Tasks in Unity ist teilweise so groß, dass der Eindruck entsteht, Threading wäre vorher gar nicht komfortabel möglich.

Link zu diesem Kommentar
Auf anderen Seiten teilen

In deinem Fall sind es aber sogar 3 Heap Objekte, einmal, der Iterator (IEnumerator und yield return laufen auch auf dem Heap), die Instanz der Automatisch generierte Klasse und noch das Array selbst.

Die Variable bleibt solange im Heap, solange es mindestens eine Referenz darauf gibt, und da in unserem Beispiel die Variable an DoSomethingWith() weitergegeben wird, kann sich in dem Kontext über die Lebenszeit der Variable kein Aussage Treffen.

Ich habe mal dein Beispiel etwa abgeändert:

private int[] results = new int[1000];
IEnumerator StartLocal()
{
    yield return new CoroutineThread(() =>
    {
        for (var i = 0; i < this.results.Length; i++)
        {
            this.results[i] = Random.Range(0, 1000);
            Thread.Sleep(10);
        }
    });
}
void DoSomethingWith()
{
    // mach was mit this.results
    this.results = null;
}

Aus der Lambda wird im Hintergehend eine normale Methode, aber immer noch 2 mal Heap.

Mit der "kleinen" Version deines CoroutineThread aus https://forum.unity-community.de/topic/11895-coroutinethread/ kämen noch 2 weitere Heap Objekte dazu, Thread und ThreadStart die du da jedes mal erstellst. Über Task und deine ThreadPool Version kann ich dazu nichts sagen.

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Nachtrag zum "Hype":

Task insbesondere mit async/await waren damals, als es neu war in der c# Welt schon ein Hype und heute in neuen Projekten im Grunde Standard. Unity ist da nur etwas spät dran, sowohl was .NET betrifft als auch dem Hype :lol: Ich Hype bei Unity Lieber das neue Job, Entity Componenten System wie auch die Nativen Listen und arrays.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Am 3.12.2017 um 11:54 schrieb runner78:

async/await ist übrigens nicht gleichzusetzen mit Multithreading, in .net gibt es einige Bibliotheken die awaitable sind, aber trotzdem auf dem Mainthread laufen, ohne ihn zu blockieren.

Das stimmt, aber async wurde vorallem implementiert, um Arbeit im Hintergrund durchzuführen.

Am 3.12.2017 um 23:42 schrieb runner78:

Ich bin ehrlich gesagt kein Freund der Coroutine, die  einem Asynchronität vorgaukeln, wo keine ist.

Genau das, wofür du Unity da anprangerst, hast du oben ja selbst ebenfalls als asynchron bezeichnet.

Mehr machen Unity's Coroutinen überhaupt nicht. Das ist im Grunde bloß ein automatisierter Iterator. Das sieht in etwa so aus:

private IEnumerator myCoroutine;

private void Update
{
  if (CoroutineIsReady)
    myCoroutine.MoveNext();
}

Dabei wird der Main Thread nicht blockiert. Denn (wenn man's richtig macht) arbeitet man ja immer bloß ein Stück ab, und geht dann zum nächsten Stück (MoveNext) sobald man bereit ist.

Das klingt zumindest auch halbwegs vernünftig, angesichts der Tatsache, dass Unity aktuell ein Entity-Component Modell implementiert (was im Allgemeinen nicht auf parallele Strukturen ausgerichtet ist !!)
P.S. Auch moderne Spiele sind zum überwiegenden Teil immer noch kaum gemultithreaded. (Außnahme: Konsolen Spiele)

 

Zu deinem ECS Hype:
Es gibt aus gutem Grund kaum eine Engine / ein Spiel das derzeit auf ein Entity Component System setzt (ein Beispiel ist allerdings die Overwatch Engine.), viele andere Großproduktionen nutzen ein EC Model (Unity, Unreal, ...) oder entwickeln ihr Spiel ohne feste Architektur (Data oriented design)
Der Grund ist ganz einfach, dass ein ECS zum einen deutlich die Komplexität deines Codes steigert und zum anderen ist es nicht so Cache freundlich wie DoD. (allerdings freundlicher als ein EC Model.)
Du hast's ja im Video gesehen, plötzlich kommen auf alle Komponenten 2 Klassen / Strukturen (Logik / Daten seperat; Also: mehr Speicherbedarf wenn wir doch schon zählen was wir alles im RAM haben ;) )

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 4 Stunden schrieb Life Is Good:

Das stimmt, aber async wurde vorallem implementiert, um Arbeit im Hintergrund durchzuführen

Hauptziel von async/await war die UI Programmierung, um den UI-Thread nicht zu blockieren.

vor 4 Stunden schrieb Life Is Good:

Dabei wird der Main Thread nicht blockiert. Denn (wenn man's richtig macht) arbeitet man ja immer bloß ein Stück ab, und geht dann zum nächsten Stück (MoveNext) sobald man bereit ist.

Coroutinen an sich blockieren immer den Main Thread, nur eben aufgeteilt auf mehrere Frames.

 

vor 4 Stunden schrieb Life Is Good:

Es gibt aus gutem Grund kaum eine Engine / ein Spiel das derzeit auf ein Entity Component System setzt (ein Beispiel ist allerdings die Overwatch Engine.), viele andere Großproduktionen nutzen ein EC Model (Unity, Unreal, ...) oder entwickeln ihr Spiel ohne feste Architektur (Data oriented design)
Der Grund ist ganz einfach, dass ein ECS zum einen deutlich die Komplexität deines Codes steigert und zum anderen ist es nicht so Cache freundlich wie DoD. (allerdings freundlicher als ein EC Model.)
Du hast's ja im Video gesehen, plötzlich kommen auf alle Komponenten 2 Klassen / Strukturen (Logik / Daten seperat; Also: mehr Speicherbedarf wenn wir doch schon zählen was wir alles im RAM haben ;) )

Das neue Entity System benutzt ein "Data oriented design" und wurde für eine gute Zusammenarbeit mit dem c# Jobsystem entwickelt, das Multithreading auf das äußerste ausreizen dürfte.

Wie ich in einem Anderen Thread schon geschrieben hatte, das neue System erfordert an einigen Stellen umdenken.

Wo mehr Ram verbraucht wird:
ScriptBehaviors bekommen immer eine Kopie der Daten die an den Entitäten hängen, das mag auf den ersten Moment ineffizient wirken, laut Unity ist es aber tatsächlich effizienter bei der Ausführung. Die Daten werden im Ram in Reih und Glied nebeneinander aufgereiht, dann greift beim iterieren eine Prozessoroptimierung und man braucht weniger Prozessorzyklen pro Iteration.

Wo weniger Ram verbaucht wir:
Instanz Felder oder Properties, die irgendwelche Daten halten die unabhängig von der Entität/GameObject sind.

Sich ein wirkliches Bild von dem System machen kann man aber erst ab der 2018.1, es wurde ja auch gesagt, das man das System noch etwas Benutzerfreundlich machen will, besonders im Bezug auf das Jobsystem. Die erste Beta sollte schon im Laufe des Dezembers erscheinen, Das Entity System ist dann noch Experimentell, da könnte sich immer noch einiges an der Handhabung ändern.

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...