Finally hat Microsoft nun auch öffentlich die NEuerungen um C#9 bekannt gegeben.
Die wichtigste Neuerung sind: Records.
Records sind eine Mischung aus Klassen und Strukturen, die nur das nötigste beinhalten.
Im Entwicklersprachgebraucht wird auch von POCOs geredet - also reine Datenstrukturen.
Der erste Vorschlag war mal
public record NominalRecord
{
public int Property { get; }
public readonly int Field;
}
Nun sieht es aber danach aus, dass wir folgendes erhalten: Data Classes.
[Edit, gfoidl: 23.08.2020] Es schaut so aus als ob doch record
bleiben wird.
public data class NominalRecord
{
public int Property { get; }
public readonly int Field;
}
C# bekommt vielen weiteren Syntax-Zucker wie zum Beispiel
Person person = new Person(123);
kann nun auch
Person person = new(123);
sein.
Mit Native-Sized Number Types kommt ein neuer Datentyp in die .NET Welt.
Der Datentyp soll vor allem für die effizientere Interop-Kommunikation verwendet werden.
Die gesamte Featureübersicht seht ihr auf https://github.com/dotnet/roslyn/blob/master/docs/Language%20Feature%20Status.md
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hallo,
Mit Native-Sized Number Types kommt ein neuer Datentyp in die .NET Welt.
Der Datentyp soll vor allem für die effizientere Interop-Kommunikation verwendet werden.
Nicht nur, sondern v.a. für effizienteren low-level-code, da direkt mit der Wortgröße der CPU gearbeitet werden kann und so der JIT besseren Maschinencode generieren kann. Eine Menge an Hacks und Workarounds die bisher dazu nötig waren werden dadurch eine saubere typsichere Lösung ersetzt.
Motiviert ist dies u.a. durch die starke Verwendung innerhalb von .NET selbst und hier insbesondere bei Methoden die Span<byte>
basiert werken und oftmals vektorisiert sind.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Nicht nur, sondern v.a. für effizienteren low-level-code, da direkt mit der Wortgröße der CPU gearbeitet werden kann und so der JIT besseren Maschinencode generieren kann.
Also einer der Gründe, der in der Vergangenheit mal genannt wurde, war hier auch Machine Learning.
Hier ist oft keine mega Genauigkeit notwendig, aber dafür eine sehr hohe Geschwindigkeit.
Warum auch immer fehlt dieser doch nachvollziehbare Grund im Blogpost.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hallo Abt,
keine mega Genauigkeit
nint
(Native sized integer) hat mit "Genauigkeit" nichts zu tun.
BTW: das wäre im Kontext von ML auch float / double und da ein float nur halb so groß ist wie eine double, passen in ein SIMD-Register doppelt so viele Werte und somit ist es (ganz grob) doppelt so schnell.
nint
ist wirklich low-level auf Assembly-Ebene. Ohne allzu tief in diesem Thread auf die Materie eingehen zu wollen nur kurz ein Beispiel für x64.
Mit int
indizierte Zugriffe Arrays schauen (vereinfacht, ohne bound-checks) in Assembly-Code so aus:
movsxd rax, r8d
mov eax, ptr [rdx+rax]
Störend dabei ist das movsxd
, welches das Register r8d
, das den Index vom Typ int
hält, auf die CPU-Wortbreite "sign extended", also erweitert und das Vorzeichen berücksichtigt. Das ist korrekter Code, aber nicht unbedingt nötig und kann bei sehr vielen Iterationen schon etwas ausmachen.
Möglich wäre jetzt statt dem int
einfach long
nehmen, da dies 64bit groß ist und somit der CPU-Wortgröße entspricht.
So Problem gelöst. Oder doch nicht? .NET soll "überall" laufen können, also auch auf x86 und da wäre es wieder int
.
Genau hier setzt nint
an. Es ist unabhängig von der Plattform der Integertyp mit der CPU-Wortbreite. Sinnbildlich kann die "Implementierung" von nint
so dargestellt werden:
#if 64 bit
using nint = System.Int64; // long
#else
using nint = System.Int32; // int
#endif
Der gleiche indizierte Zugriff (von oben) mit nint
statt int
würde folgenden Code erzeugen:
mov rax, r8
mov eax, ptr [rdx+rax]
Es ist also statt dem movsxd
"nur" ein mov
und moderne CPUs können dieses spezielle mov
sogar eliminieren (dank dem Register-Allocator mit der Register-Renaming Technik). Vllt. generiert der JIT (dank Peephole-Optimization) auch den "idealen" Code:
mov eax, ptr [rdx+r8]
Es geht dabei nicht nur um den "Austausch" von den CPU-Instruktionen, sondern auch um eine Verringerung der Maschinencode-Größe, welche sich v.a. in Schleifen (positiv) bemerkbar machen kann.
Ohne nint
(und Konsorten) musste bisher auf IntPtr
-Tricks od. void*
-Tricks zurückgegriffen werden. Unleserlicher und fehleranfälliger Code war die Folge. Durch nint
kann jetzt der C#-Compiler die nötigen Prüfungen durchführen.
Der netto Effekt / Gewinn schaut relativ gering aus.
Beispielweise profitieren von dieser Änderung / Möglichkeit in .NET so gut wie alle Span-Erweiterungsmethoden (SequenceEquals, IndexOf, ...) und in Folge auch alle String-Vergleichmethoden. Weiter oben im .NET Stack hat dies dann z.B. Vorteile in Kestrel, da es so mehr RPS gibt 😉
Da du oben Interop zitiert hast, so stimmt es dort auch. In C weiß ja niemand so recht wie groß int
ist 😉, daher gibt es dort int32_t
und int64_t
mit fest definierter Größe. Weiters gibt es dort size_t
für den Integertyp mit CPU-Wortgröße.
Bisher gab es bei Interop ein Problem, wenn die native Signatur (aka Deklaration) beispielsweise wie folgt ist:
API_EXPORT void do_something(char* data, size_t index);
Wie sollte nun in C# das DllImport
aussehen? int
od. long
?
V.a. da DllImports statisch typisiert sein müssen, d.h. es kann zur Laufzeit keine andere Signatur verwendet werden.
Das Workaround war keine DllImports zu verwenden und stattdessen per LoadLibrary
und Konsorten zur Laufzeiten letztlich einen Delegaten zu erzeugen, welche je nach Environment.Is64BitProcess
eine andere Überladung verwendete. Alles umständlich.
Od. man verwendete einfach int
od. long
und hoffte dass es gut geht. Auch nicht sehr zielführend.
Hatte man die "native Seite" selbst in der Hand -- wie in .NET z.B. die PAL-Layer (platform abstraction) -- so wurde bewusst auf size_t
verzichtet und (nur) int32_t
verwendet.
Mit nint
gehören diese Probleme der Vergangenheit an.
Wie gesagt ist das alles sehr low-level und ein Großteil der C#-Programmierer wird sich darum wohl nie kümmern brauchen 😉
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
@gfoidl
Danke für die Erklärung, jetzt wird mir erst der Sinn klar.
Beim durchlesen des Github Issue konnte ich das nicht ganz verstehen wo der Einsatzzweck genau liegen sollte.
Ansonsten warte ich mal ab bis C# 9 zur Verfügung steht und schau mir mal die Einsatzmöglichkeiten im Detail an.
Die Neuerung mit dem data class Ansatz klingt einleuchten und dürfte bestimmt auch Einzug in Entity Framework und andere OR Mapper finden um die Entitäten Klassen von regulären Klasse zu trennen.
Zu mindest kann ich mir diesen Ansatz vorstellen, ob er dann auch kommt steht in den Sternen 😮
T-Virus
Developer, Developer, Developer, Developer....
99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.
@gfoidl: ich muss mich ehrlich wundern, dass der Compiler dass nicht sowieso optimieren kann. Gibt es konkrete Gründe, die das verhindern?
@Coder007: der Compiler kennt die Zielplattform nicht, deswegen kann er das nicht alleine.
Anders als bei z.B. C wird nicht Maschinencode erzeugt beim kompilieren von C#
Hallo Coder007,
Gibt es konkrete Gründe, die das verhindern?
Wenn ich mich nicht täusche, so die ECMA-Spec auf der .NET beruht. Wenn im Code int
steht, so muss letztlich auch int
verwendet werden. D.h. weder der C#-Compiler noch der JIT-Compiler dürfen das weg-optimieren.
Nachfolgend meine rein persönliche Einschätzung.
Das könnte zwar beim JITen "aufgeweicht" werden, aber der JIT selbst hat wenig Zeit und Möglichkeiten den Programmfluss genau zu analysieren, denn es könnte sein, dass es tatsächlich ein int
bleiben muss damit die Absicht vom Programmierer nicht falsch umgesetzt wird. Daher halte ich es auch für besser, wenn explizit angegeben wird dass es sich um ein nint
handelt.
Optimierende C/C++ Compiler haben hier aufgrund der Vorab-Kompilierung mehr Möglichkeiten (und Zeit) und können auch mit int
bzw. int32_t
optimalen Maschinencode generieren.
Der JIT in .NET an sich ist eine super Sache, es gibt aber auch ein paar "Baustellen" die offen sind und daher suboptimalen Code erzeugen. Durch das Open-Sourcen von .NET wurden aber schon viele Baustellen behoben / verbessert.
Seitens MS geht es dabei aber immer um Priorisierung der offenen Punkte, somit wurde speziell für diesen Fall noch nichts / wenig für automatische Optimierungen umgesetzt. V.a. mit dem Hintergrund dass es mit nint
eine explizite Variante gibt.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"