Jump to content
Unity Insider Forum

Unity 2017 und .NET 4.6


runner78

Recommended Posts

vor 3 Stunden schrieb runner78:

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

Hmpf. Ich versteh nicht ganz warum du das jetzt schreibst. Kann man das als

Zitat

Ja.

zusammenfassen und interpretieren ?
Wenn du einen Thread entlasten willst hast du bloß 2 Möglichkeiten (so funktionieren CPUs nunmal) entweder du verteilst die Arbeit über mehr Zeit (So wie Coroutinen) oder du lagerst sie auf seperate Threads aus. Da steckt keine dunkle Magie hinter asynchroner Programmierung. (Dass die Heinzelmännchen kommen, wenn du die Keywords async und await nutzt ist bloß ein Mythos :) )

 

vor 3 Stunden schrieb runner78:

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

Was du hier als "blockieren" bezeichnest ist halt die Art, wie unsere Prozessoren funktionieren. Await auf dem selben Thread macht da nichts anderes, siehe oben.

 

vor 3 Stunden schrieb runner78:

Das neue Entity System

Hier bin ich mal pingelig.
Unity Tech arbeitet gerade an einem Entity-Component System.
Entity System != Entity Component System; außerdem:
Entity Component Model (oft einfach Entity Component, EC) != Entity System und Entity Component != Entity Component System (ECS)

vor 3 Stunden schrieb runner78:

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.

Jein. Dass Daten & Logik (Behaviour) voneinander getrennt werden ist schon DoD mäßig, das ist richtig.
Aber ein ECS schließt im Grunde DoD aus.
Siehe https://en.wikipedia.org/wiki/Data-oriented_design
Denn
1. ist DoD das genaue Gegenteil von sowas wie ECS, EC, MVC etc. Bei DoD folgst du keinen strengen Paradigmen, und keinem festen Design Plan.
2. ist vieles am ECS (variiert natürlich je nach Implementation) ziemlich anti DoD. Beispiel: Eine super Klasse wie Unity's Object Klasse.
Alle Objekte (ob GameObject oder Komponenten) erben von Object. In DoD hast du sowas nicht, weil es Datenmüll und Abhängigkeit produziert. (DoD ist vorallem auf high performance ausgerichtet, wie man dem Namen wohl entnehmen kann.) Manchmal allokiert man absichtlich überflüssigen Speicher, aber das ist ein anderes Thema.

Zu deinen anderen Texten: Ich verstehe Englisch recht gut, danke ;) Darauf wollte ich nicht hinaus.
Sondern, dass du 2, statt einem Objekt hast, was wieder ziemlich anti DoD ist.
Vorallem aber macht es den Code komplexer. Wie gesagt, ECS ist (zumindest aktuell) auch nicht wirklich verbreitet.

Ein guter link noch http://gamedevs.org/uploads/pitfalls-of-object-oriented-programming.pdf

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 12 Stunden schrieb Life Is Good:

Hmpf. Ich versteh nicht ganz warum du das jetzt schreibst. Kann man das als

 

vor 12 Stunden schrieb Life Is Good:

zusammenfassen und interpretieren ?
Wenn du einen Thread entlasten willst hast du bloß 2 Möglichkeiten (so funktionieren CPUs nunmal) entweder du verteilst die Arbeit über mehr Zeit (So wie Coroutinen) oder du lagerst sie auf seperate Threads aus. Da steckt keine dunkle Magie hinter asynchroner Programmierung. (Dass die Heinzelmännchen kommen, wenn du die Keywords async und await nutzt ist bloß ein Mythos :) )

Asynchron bedeutend nicht automatisch im Hintergrund.
Coroutinen sind abhängig von der Framerate, auch wenn der Prozessor noch Ressourcen frei hat, z.B. bei fixen Frameraten, wird die Ausführung unnötig in die länge gezogen. Ich werde mal heute Abend mal etwas experimentieren, mal sehen wie sich Coroutinen und async/await so in Sachen Performance so verhalten, wenn man im Mainthread bleibt.

vor 13 Stunden schrieb Life Is Good:

Hier bin ich mal pingelig.

Ich war hier einfach zu faul es auszuschreiben:D

vor 13 Stunden schrieb Life Is Good:

Jein. Dass Daten & Logik (Behaviour) voneinander getrennt werden ist schon DoD mäßig, das ist richtig.
Aber ein ECS schließt im Grunde DoD aus....

Ein so ganz festes Pattern scheint Unity hier aber nicht zu haben. du musst nicht 2 Klassen machen, du kannst ja auch eine ScriptBehaviour machen, die bestehende Daten benutzt, ohne eine eigene DataComponent an das Entity zu hängen.
Die Daten-Componenten sind alles structs, die werden nicht mehr von Unitys "Object" ableiten.

Eigentlich ist es noch zu früh für diese Diskussion, da man noch nicht selbst reinschnuppern kann.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 14 Minuten schrieb runner78:

Coroutinen sind abhängig von der Framerate

Ahhh, jetzt versteh ich worauf du hinaus willst. Naja, dafür müsste man sich async / await mal genauer anschauen. Wie genau die das intern machen weiß ich gerade nicht. Aber das Prinzip bleibt ja das selbe :)

vor 15 Minuten schrieb runner78:

Eigentlich ist es noch zu früh für diese Diskussion, da man noch nicht selbst reinschnuppern kann.

Da stimme ich zu, also lass ich's mal so stehen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Zitat

Coroutinen sind abhängig von der Framerate,

Sind sie nicht, es ist genau anders herum. Coroutinen beeinflussen die Framerate, werden aber immer zu einem definierten Zeitpunkt durch den Prozessor (in Unity auf dem Mainthread) ausgeführt, dauert eine Coroutine zu lang, verschlechtert sich deine Framerate in Unity.

Unity main thread auf CPU Kernel 1:

Loop each Frame: {
  Unitykernelprozess
  Coroutine 1 (skip/wait or execute next coroutine instructions?)
  Coroutine 2 (skip/wait or execute next coroutine instructions?)
  Coroutine 3 (skip/wait or execute next coroutine instructions?)
}

Werden Instruktionen einer Coroutine ausgeführt, verlängert sich die Ausführungszeit pro Frame (Time.deltatime).

Kann man dagegen einen Thread auf den 2. CPU Kernel auslagern, so wird sich deine FPS verbessern (Vorrausetzung ist natürlich man hat eine CPU mit mehreren Kernels und man verwendet keine Unity-Kernelfunktionen innerhalb des Threads)
Womit ich mich allerdings noch nicht im Detail beschäftigt habe, wie ermittle ich, auf welchem Kernel der Unity-Mainthread aktuell läuft und wie starte ich einen Thread auf einem spezifischen Kernel.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich glaube nicht, dass er das meinte, sondern, dass MoveNext() in Update statt findet. In diesem Sinne ist deine Coroutine schon ein wenig davon abhängig.
Ansonsten scheint await nicht sonderlich davon abzuweichen. Der Compiler generiert eine Statemachine, die von der Struktur ähnlich wie IEnumerator & yield aussieht.
Der Unterschied ist, das MoveNext() dort über Callbacks (sobald die Arbeit verichtet wurde) aufgerufen wird. Wie das aussieht, wenn auf dem selben Thread gewartet wird hab ich jetzt auf die Schnelle aber nicht herausgefunden.
https://weblogs.asp.net/dixin/understanding-c-sharp-async-await-1-compilation

Wenn du mit "Kernel" CPU Kerne meinst, das übernimmt für gewöhnlich das Betriebssystem. Vermutlich liegt der Unity Main Thread einfach auf dem 1. Kern.
Mit https://msdn.microsoft.com/en-us/library/system.diagnostics.process.processoraffinity.aspx könntest du allerdings Threads auf spezifischen Kernen starten.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Mit Kernel meinte ich den jeweiligen Prozessorkern.

Wenn man Threads oder Tasks ohne Prozessorkernzuordnung startet, dann läuft man Gefahr, daß diese Threads oder Tasks ebenfalls auf dem 1 Core laufen und damit bremst man ja den Core aus, auf dem Unity ohnehin schon läuft.

Wenn ich google finde ich auch sowas in der Richtung wss du meintest:
https://stackoverflow.com/questions/6584397/how-to-start-a-thread-on-a-specific-core

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 3 Stunden schrieb Zer0Cool:

Sind sie nicht, es ist genau anders herum. Coroutinen beeinflussen die Framerate, werden aber immer zu einem definierten Zeitpunkt durch den Prozessor (in Unity auf dem Mainthread) ausgeführt, dauert eine Coroutine zu lang, verschlechtert sich deine Framerate in Unity.

Doch, Coroutinen sind von der Framerate abhängig, alles zwischen 2 yield returns wird in einem Frame ausgeführt. Bei einer fixen Framerate wäre die Ausführungsdauer unabhängig der Prozessorgeschwindigkeit immer gleich. Auch wenn die Anweßung zwischen zwei yields nur 2 Millisecunden dauert, wird bei 60 FPS erst 15 Millisekunden später wieder vorgesetzt. In solchen Fällen sollte man die Logik in einen seperaten Thred arbeiten lassen, und da ist async/await mit Task die nach  meinen bisherigen Erfahrung die Variante mit dem geringsten Overhead.

Auf welchen Prozessorkern was läuft, das entscheidet in der Regel das Betriebsystem.

vor 46 Minuten schrieb Life Is Good:

Der Compiler generiert eine Statemachine, die von der Struktur ähnlich wie IEnumerator & yield aussieht.
Der Unterschied ist, das MoveNext() dort über Callbacks (sobald die Arbeit verichtet wurde) aufgerufen wird. Wie das aussieht, wenn auf dem selben Thread gewartet wird hab ich jetzt auf die Schnelle aber nicht herausgefunden.

Ein unterschied ist noch, die Statemachine wird als Valuetype realiesiert, der Enumerator als Referenztype.

async/await ist nicht wirklich dafür ausgelegt, Logik asynchron innerhalb der Anwendung auf dem Mainthread auszuführen, so wie es die Coroutine macht.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Zitat

Doch, Coroutinen sind von der Framerate abhängig, alles zwischen 2 yield returns wird in einem Frame ausgeführt

Da ist man aber selbst schuld wenn man sowas macht. Klar kann ich eine Coroutine jedes Frame ausführen lassen, muss ich aber nicht und sollte man auch nicht, insofern es nicht zwingend notwendig ist.

// suspend execution for 5 seconds
yield return new WaitForSeconds(5);

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hab mal nachgeforscht, man kann zwar den Core für einen Prozess setzen, aber für einen Thread gestaltet es sich schwierig. Die Threads werden wohl entweder durchs Betriebssystem über die Kerne verteilt oder hängen am Kern ihres Prozesses, wobei letzteres schlecht wäre.

Hier ist Code zum Setzen eines Cores für einen einzelnen Thread, aber zumindest unter Unity scheint er nicht zu funktionieren:
http://blog.rebuildall.net/2010/03/08/Running_NET_threads_on_selected_processor_cores

Der Thread lief bei mir nicht an, immer wenn ich die "CurrentThread.ProcessorAffinity" gesetzt habe.

Mhh wirft eine Exception:
"Operation is not valid due to the current state of the object"

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ok, das Problem ist, Unity gibt die Threads von sich selbst nicht zurück und daher wurde der Thread nicht gefunden und daher konnte ich die Affinität nicht setzen .. Unity 2017.1.2
https://forum.unity.com/threads/process-getcurrentprocess-threads-count-returns-zero-all-the-time.292069/

Hier ein ganz einfaches Beispiel, um den Fehler zu verdeutlichen. Auch wenn man selber einen Thread erzeugt, wird dieser nicht gelistet.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Threading;
using System.Diagnostics;
using System.Runtime.InteropServices;

public class MultiCoreTest : MonoBehaviour
{
    void Start()
    {
        // Make sure there is an instance of Unity running.
        Process[] unityProcesses = Process.GetProcessesByName("Unity");

        // Sollte 1 zurückgeben
        UnityEngine.Debug.Log("Number of Unity processes: " + unityProcesses.Length); 
        ProcessThreadCollection threads;

        unityProcesses[0].Refresh(); // Sicherheitshalber noch einmal refreshen
        
        threads = unityProcesses[0].Threads;
        // Sollte > 0 zurückgeben
        UnityEngine.Debug.Log("Number of Unity threads: " + threads.Count);

        // Der Rest funktioniert leider nicht mehr, da keine Threads zurückgeben werden
        // Set the properties on the first ProcessThread in the collection
        // threads[0].IdealProcessor = 0;
        // threads[0].ProcessorAffinity = (IntPtr)1;

    }

Siehe auch:
https://msdn.microsoft.com/de-de/library/system.diagnostics.processthread.processoraffinity(v=vs.110).aspx

Link zu diesem Kommentar
Auf anderen Seiten teilen

Im Ressourcenmonitor von Windows kannst du nachsehen, wie viele Threads Unity verwendet, der Editor benutzt bei mir zwischen 124 und 127 Threads im Editromodus, im Gamemodus  ca. 128 (Unity 2017.3.0f1)

Ich denke, es ist wohl besser die Hände davon zu lassen, am Ende macht man alles nur schlimmer. 

Link zu diesem Kommentar
Auf anderen Seiten teilen

 

vor 8 Stunden schrieb Zer0Cool:

Hier ein ganz einfaches Beispiel, um den Fehler zu verdeutlichen. Auch wenn man selber einen Thread erzeugt, wird dieser nicht gelistet.

Nachtrag: Wenn man das Codebeispiel in einer einfachen .net Konsoleanwendung ausprobiert, listet er die Thread korrekt. Entweder ein Bug von Unity, oder Unity verhindert hier bewusst das manipulieren der Threads.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 1 Stunde schrieb runner78:

 

Nachtrag: Wenn man das Codebeispiel in einer einfachen .net Konsoleanwendung ausprobiert, listet er die Thread korrekt. Entweder ein Bug von Unity, oder Unity verhindert hier bewusst das manipulieren der Threads.

Ja, vermutlich ein Bug:
https://stackoverflow.com/questions/28014662/current-process-without-threads?noredirect=1#comment44416985_28014662
 

Zitat

Im Ressourcenmonitor von Windows kannst du nachsehen, wie viele Threads Unity verwendet, der Editor benutzt bei mir zwischen 124 und 127 Threads im Editromodus, im Gamemodus  ca. 128 (Unity 2017.3.0f1)

Die interessante Frage ist aber, wie viele von diesen Threads laufen auf dem gleichen Core. Ich habe hier 12 Cores und Windows ist nicht in der Lage anzuzeigen, welche Threads auf welchem Core laufen.
Ich vermute nämlich, daß sich eine große Anzahl der Unity Threads auf dem gleichen Core tummeln.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Sie sind leider nicht optimal verteilt, die Last verteilt sich auf die ersten 7 Kerne von insgesamt 12 Kernen. Scheinbar bevorzugt Unity auch die geraden Prozessorkerne.
Am meisten sind der Kerne 2 und 4 "belagert".
Dies kann natürlich von Betriebsystem zu Betriebssytem und von Unityversion zu Unityversion verschieden sein. Aber ich vermute trotzdem, daß die hinteren Cores oft frei sind...

Die Kerne 3,5,7,9,11 sind beispielsweise fast komplett ohne Arbeit. Dies kann aber auch am Hyperthreading liegen, wobei das OS gerade Kerne bevorzugt.
Wäre aber auch ein Ansatzpunkt, der dafür spricht, nebenläufige Tasks in Unity in die ungeraden hinteren Kerne zu packen, damit vom Hyperthreading profitiert wird.

K6se9lY.png

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 7 months later...
Am 7.12.2017 um 23:26 schrieb Zer0Cool:

Mit Kernel meinte ich den jeweiligen Prozessorkern.

Wenn man Threads oder Tasks ohne Prozessorkernzuordnung startet, dann läuft man Gefahr, daß diese Threads oder Tasks ebenfalls auf dem 1 Core laufen und damit bremst man ja den Core aus, auf dem Unity ohnehin schon läuft.

Wenn ich google finde ich auch sowas in der Richtung wss du meintest:
https://stackoverflow.com/questions/6584397/how-to-start-a-thread-on-a-specific-core

Aus heutiger Sicht sollte man nach meiner Meinung wenn möglich das neue Unity JobSystem benutzen.

Das hat wenig Overhead, mit Burst sehr schnell, und Jobs laufen nie auf dem Selben Kern wie der Mainthread.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...