SlideShare a Scribd company logo
1 of 67
Download to read offline
Understanding
C# Struct
All Things
Cysharp, Inc.
Kawai Yoshifumi
About Speaker
2
— 河合 宜文 / Kawai Yoshifumi / @neuecc
— Cysharp, Inc. – CEO/CTO
— Microsoft MVP for Developer Technologies(C#)
— 50以上のOSS公開(UniRx, MagicOnion, MessagePack for C#, etc..)
— 株式会社Cysharp
— 2019年9月, Cygamesの子会社として設立
— C#関連の研究開発/OSS/コンサルティングを行う
— C#大統一理論(サーバー/クライアントともにC#で実装する)を推進
https://github.com/Cysharp
OSS for Unity – GitHub/Cysharp
3
UniTask ★288
Provides an efficient async/await integration to
Unity.
RuntimeUnitTestToolkit ★87
CLI/GUI Frontend of Unity Test Runner to test on
any platforms.
RandomFixtureKit ★16
Fill random/edge-case value to target type for
unit testing.
MagicOnion ★1240
Unified Realtime/API Engine for .NET Core and
Unity.
MasterMemory ★407
Embedded Typed Readonly In-Memory
Document Database for .NET Core and Unity.
https://github.com/neuecc
OSS for Unity – GitHub/neuecc
4
LINQ-to-GameObject-for-Unity ★448
Traverse GameObject Hierarchy by LINQ.
PhotonWire ★92
Typed Asynchronous RPC Layer for Photon.
SerializableDictionary ★87
SerializableCollections for Unity.
ReMotion ★27
Hyper Fast Reactive Tween Engine for Unity.
UniRx ★3722
Reactive Extensions for Unity.
MessagePack-CSharp ★2089
Extremely Fast MessagePack Serializer.
ZeroFormatter ★1778
Infinitely Fast Deserializer.
Utf8Json ★1352
Definitely Fastest JSON Serializer.
6
The Evolution of C# Struct
MessagePack for C#(v2-preview)
7
public ref struct MessagePackWriter
T Deserialize<T>(in ReadOnlySequence<byte> byteSequence)
Span<byte> bytes = stackalloc byte[36];
internal ref partial struct SequenceReader<T>
where T : unmanaged, IEquatable<T>
public ref byte GetPointer(int sizeHint)
ref Entry v = ref entry[0];
MessagePack for C#(v2-preview)
8
public ref struct MessagePackWriter
T Deserialize<T>(in ReadOnlySequence<byte> byteSequence)
Span<byte> bytes = stackalloc byte[36];
internal ref partial struct SequenceReader<T>
where T : unmanaged, IEquatable<T>
ref struct
Span<T> = stackalloc
in parameter
where : unmanaged
public ref byte GetPointer(int sizeHint)
ref Entry v = ref entry[0];
ref return
ref local
DOTS(昨日の基調講演より)
DOTS(昨日の基調講演より)
What’s new struct features
11
C# 7.2 C# 7.3 C# 8.0
in modifier on parameter
ref readonly modifier on
method returns, local
readonly struct
declaration
ref struct declaration
ref extension method
stackalloc to Span
reassign ref local
additional generics
constraints(unmanage
d, Enum, Delegate)
stackalloc initializer
access fixed field
without pinning
readonly method
Disposable ref structs
Unmanaged
constructed types
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/
C# 7.0
ref return statement
ref local variables
12
Which Unity Version should we support?
Unity Version C# Version .NET Version
Unity 2017.4 6.0 .NET 3.5/.NET 4.6
Unity 2018.2 6.0 .NET 4.x/Standard 2.0
Unity 2018.3 7.3 .NET 4.x/Standard 2.0
Unity 2018.4 7.3 .NET 4.x/Standard 2.0
Unity 2019.1 7.3 .NET 4.x/Standard 2.0
13
Which Unity Version should we support?
Unity Version C# Version .NET Version
Unity 2017.4 6.0 .NET 3.5/.NET 4.6
Unity 2018.2 6.0 .NET 4.x/Standard 2.0
Unity 2018.3 7.3 .NET 4.x/Standard 2.0
Unity 2018.4 7.3 .NET 4.x/Standard 2.0
Unity 2019.1 7.3 .NET 4.x/Standard 2.0
2018.3以上一択、それ以下の
バージョンはNot Supported
14
Which Unity Version should we support?
Unity Version C# Version .NET Version
Unity 2017.4 6.0 .NET 3.5/.NET 4.6
Unity 2018.2 6.0 .NET 4.x/Standard 2.0
Unity 2018.3 7.3 .NET 4.x/Standard 2.0
Unity 2018.4 7.3 .NET 4.x/Standard 2.0
Unity 2019.1 7.3 .NET 4.x/Standard 2.0
2018.3から以下の言語に関するdefineが使える
CSHARP_7_3_OR_NEWER
以下のどちらか選んだほうが定義される
NET_4_6
NET_STANDARD_2_0
Struct is important for Performance!
15
— C# 7以降の急速なstruct強化はパフォーマンスのため
— それは .NET Core でも、Unityでも
— アプローチは異なれど、両者とも構造体をパフォーマンスのため活用している
— 特にUnityの推すDOTS(Data Oriented Technology Stack)はstructの塊
— 今、全てを学び、備えよう
public unsafe ref struct BlobBuilderArray<T>
where T : struct
public unsafe static ref T AsRef<T>(void* ptr) where T : struct
public readonly struct BuildComponentDataToEntityLookupTask<TComponentData> : IDisposable
where TComponentData : unmanaged, IComponentData, IEquatable<TComponentData>
Unity ECSの中の
C# 7.3表現
16
The Basic of C# Memory
The Memory of C#
17
AppDomain(Managed)
Thread
Stack
HeapThread
Stack
Unmanaged
The Memory of C#
18
AppDomain(Managed)
Thread
Stack
HeapThread
Stack
Unmanaged
ローカル変数はスタック領域に格納される
ヒープ領域に確保されたデータは
GCの管理化に入る
UnityではC#管理外のメモリを扱う
こともよくある(特にDOTS)
Let’s see memory layout
19
— SharpLabでメモリの中身を見よう!
— https://sharplab.io/
— C# to IL, C# to C#, C# to ASMなど豊富な機能がある
— Run と Inspectを組み合わせるとメモリの中身が見れる
なお、組み込み型の場合、Unity(mono)
とSharpLab(.NET Core)で中身が異なる
ことがある場合に注意
Struct Memory
Int(4バイト)のX, Y, Z(12バイト)が
素直にメモリ上(スタック)に並ぶ
Class Memory
スタック上の変数はヒープ上
のアドレスを指す
ヒープ上に確保されるメモリは管理
用のヘッダ/型情報 + 実データ
Pass by Reference/Pass by Value
22
— C#はデフォルトは全て「値渡し」
— つまりコピーされます
— ローカル変数への代入もコピー
(T x) (ref T x)
class 参照の値渡し 参照の参照渡し
struct 値の値渡し 値の参照渡し
スタック領域は大体この辺っぽい
00 00 00 00 からそれっぽ
いデータが入った気配
引き続き、 y = x で同じデ
ータが追加で入ったっぽい
別のところにxとyがコピー
されて入った気配
戻りのzを受け取って全部
埋まった
int x
int y
int z
コンパイル時(C# -> IL)の段
階で変数の置き場確保してお
きます、的な
Structの基本的な原則
29
— 全てはコピーに気をつける、ということ
— クラスとの違い、ほとんどの問題はコピーにより引き起こされる
— 大きなサイズの構造体を(基本的には)作らない
– IntPtr.Size(4 or 8バイト)以上は参照渡しに比べて大きいということになる
– それだと制限キツすぎなので、一般には16バイト以下ぐらいを目安に
— 変更可能な構造体を(基本的には)作らない
– コピーされることによって、変更したつもりが変更されない
– 一度は悩むVector3変更されない問題
Structの基本的な原則
30
— 全てはコピーに気をつける、ということ
— クラスとの違い、ほとんどの問題はコピーにより引き起こされる
— 大きなサイズの構造体を(基本的には)作らない
– IntPtr.Size(4 or 8バイト)以上は参照渡しに比べて大きいということになる
– それだと制限キツすぎなので、一般には16バイト以下ぐらいを目安に
— 変更可能な構造体を(基本的には)作らない
– コピーされることによって、変更したつもりが変更されない
– 一度は悩むVector3変更されない問題
// position変えたつもりが変わらない!
this.transform.position.Set(10f, 20f, 30f);
// つまりこういうことだから
this.transform.INTERNAL_get_position(out Vector3 value);
value.Set(10f, 20f, 30f);
Structの基本的な原則
31
— 全てはコピーに気をつける、ということ
— クラスとの違い、ほとんどの問題はコピーにより引き起こされる
— 大きなサイズの構造体を(基本的には)作らない
– IntPtr.Size(4 or 8バイト)以上は参照渡しに比べて大きいということになる
– それだと制限キツすぎなので、一般には16バイト以下ぐらいを目安に
— 変更可能な構造体を(基本的には)作らない
– コピーされることによって、変更したつもりが変更されない
– 一度は悩むVector3変更されない問題
// position変えたつもりが変わらない!
this.transform.position.Set(10f, 20f, 30f);
// つまりこういうことだから
this.transform.INTERNAL_get_position(out Vector3 value);
value.Set(10f, 20f, 30f);
positionがプロパティなのが悪い
(フィールドならコピーの問題は起きない)
が、これはtransformのデータの実体は
アンマネージドメモリ側(UnityEngine)にある
ためという、Unityならではの悩みでもある
(C#/C++越境がどうしても抱える話)
Boxed Struct
ボックス化
スタック上の変数は
ヒープのアドレス ヒープ上にクラスと
同様ヘッダが付いた
うえで領域確保
アンボックスの毎に
ここからスタックへ
コピーする
Box化との戦い
33
— ボックス化はnewと一緒
— むしろアンボックスの頻度的により悪い
— ボックス化が避けられないなら最初からクラスで作ることも選択肢
— インターフェイスへのキャストに気をつける
– ジェネリクスを使って回避していく
– enumの場合はEnum(参照型)も同様
public static class BoxedInt
{
public static readonly object Zero = 0;
public static readonly object One = 1;
public static readonly object MinusOne = -1;
}
ある程度決まった値が頻繁にボッ
クス化されるようなら、先に作っ
ておいて使い回すという技
Equalsの自動実装とBox化
34
— structはEqualsが実装されていない場合、自動的に以下のものが呼ばれる
— めっちゃ遅い
— Equalsは辞書のKeyにすると呼ばれる!
そのため辞書のKeyにする構造体は
IEquatable<T>とGetHashCodeの
カスタム実装を必ず行うこと
internal static bool DefaultEquals(object o1, object o2)
{
RuntimeType o1_type = (RuntimeType)o1.GetType();
RuntimeType o2_type = (RuntimeType)o2.GetType();
object[] fields;
InternalEquals(o1, o2, out fields);
for (int i = 0; i < fields.Length; i += 2)
{
object meVal = fields[i];
object youVal = fields[i + 1];
if (!meVal.Equals(youVal)) return false;
}
return true;
}
object, object比較のボクシング
そもそもリフレクションで全フィ
ールド比較(遅い)うえに、フィ
ールドの戻り値もボクシング
原理主義的には可能なもの全てのStructにカスタム実装を入
れたほうがいい、ということになるけれど、あまりにも面倒
なので、さすがにそこはピンポイント(辞書のKeyになるもの
だけ)でいいと思います
Struct Layout and Padding
単純計算ではbyte(1) + long(8) + int(4) = 13ですが、
アラインメント調整のため、最長の8にそれぞれが合
わせられて8 * 3 = 24バイトの確保になっている
Struct Layout and Padding StructLayoutやFieldOffsetによってレイアウ
トはカスタマイズ可能。
structのデフォルトはSequential(宣言順)
Autoに変えると、ZとXが詰められることで最
小の16バイトに縮む
参照型はstructと異なりデフォ
ルトがAuto
Heap Layout: Array
オブジェクトの共通ヘッダの後
ろに長さが付いてる
データ領域には要素がそのまま順番に並ぶ。構造体な
ら、値がそのまま順番に並んでいることになる(参照
型の場合はポインタが並んでいるため、実態のデータ
を更に辿る必要がある)
38
ref and readonly
Zero Allocation foreach in List<T>
39
— foreachは .GetEnumerator -> while(MoveNext()) に変換される
— (ただし配列の場合はコンパイル時にILでforに変換される)
— つまり IEnumerator が生成されてヒープに確保されている?
— されるようでされない
var list = new List<int>() { 1, 2, 3 };
foreach (var item in list)
{
/* do anything */
}
Mutable Struct is Evil but Useful
40
— 一時的な入れ物として使うものに向いてる
public struct Enumerator : IEnumerator<T>
{
List<T> list;
int index;
int version;
T current;
}
public struct BinaryReader
{
byte[] bytes;
int offset;
}
List<T>は直接GetEnumeratorを呼べる
状況ではstruct List<T>.Enumerator を
返すためゼロアロケーション
バイナリを読みすすめる際にReadXxx
を呼ぶたびにoffsetを追加していくとい
うステートを管理
局所的にしか使わないので
classじゃなくてもいい
ref struct
41
— スタックにしか置けないという制約がref struct
– 元々はSpan<T>(System.Memory, .NET Standard 2.0外部ライブラリ)のため
– Span<T>は連続したメモリ領域のビューで、配列のように扱える(NativeArrayみたいな)
– 今までポインタでしか扱えなかったstackallocを自然に扱えて便利
– しかしそれによってスタックにのみ確保したメモリ領域をヒープに移されると危険
– フィールドに置けない(ref structのfieldの場合のみ可)、ボクシングできない、インターフ
ェイスを実装できない、ジェネリクスの型引数にできない、などの制約がある
— ビュー的なものや一時的にしか使わない状態を持つものには適用しやすい
– 制約が多いので無理に使おうとするとハマりますが……
Span<int> temp = stackalloc int[12];
internal ref struct TempList<T>
{
int index;
T[] array;
public ReadOnlySpan<T> Span => new ReadOnlySpan<T>(array, 0, index);
public TempList(int initialCapacity)
{
this.array = ArrayPool<T>.Shared.Rent(initialCapacity);
this.index = 0;
}
public void Add(T value)
{
if (array.Length <= index)
{
var newArray = ArrayPool<T>.Shared.Rent(index * 2);
Array.Copy(array, newArray, index);
ArrayPool<T>.Shared.Return(array, true);
array = newArray;
}
array[index++] = value;
}
public void Dispose()
{
ArrayPool<T>.Shared.Return(array, true); // clear for de-reference all.
}
}
ArrayPool(System.Buffers, Unityで
は似たようなものを自作すれば……)
から確保済み配列を取得し使う
Disposeで返却
一時的にしか使わない配
列を都度確保せずプール
から取得するための構造
(TempList<T>)
プールを扱っているので、
寿命は明確に短くあって
ほしいのでref struct
public void DoNanika(IEnumerable<int> idList)
{
var resources = idList.Select(x => Load(x));
// LINQの遅延実行により二回のLoadが走ってしまう
// それを避けるために .ToList() するとそれはそれでListの無駄を感じる
foreach (var item in resources) { /* nanika suru 1 */ }
foreach (var item in resources) { /* nanika suru 2 */ }
}
public void DoNanika(IEnumerable<int> idList)
{
using var resources = idList.Select(x => Load(x)).ToTempList();
foreach (var item in resources) { /* nanika suru 1 */ }
foreach (var item in resources) { /* nanika suru 2 */ }
}
usingだけで末尾で
Disposeが便利
(C# 8.0から!
Unityではまだ!)
ここの中だけで使う一時配列はPoolから
取ってるのでアロケートなしで済んだ
Avoid the copy, Everywhere
44
— 全て ref で引き回せばいい、とはいうものの現実的ではない
— あまりにも最悪な書き心地になる!
— あるいは全てunsafeでポインタで取り回すという手も……
— Unity.Entitiesのソースコードはかなりそれに近い
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct Archetype
{
public ArchetypeChunkData Chunks;
public UnsafeChunkPtrList ChunksWithEmptySlots;
public ChunkListMap FreeChunksBySharedComponents;
public int EntityCount;
public int ChunkCapacity;
public int BytesPerInstance;
public ComponentTypeInArchetype* Types;
public int TypesCount;
public int NonZeroSizedTypesCount;
public int* Offsets;
public int* SizeOfs;
public int* BufferCapacities;
public int* TypeMemoryOrder;
public int* ManagedArrayOffset;
public int NumManagedArrays;
// ... まだまだいっぱい
void AddArchetypeIfMatching(
Archetype* archetype,
EntityQueryData* query)
(ECSより引用)とにかく巨大なStruct
全部ポインタで引き回すからOK(?)
(ECSはネイティブメモリを使ったり色々
と固有の事情があるので一般論は適用で
きない)
static void Normal(Vector3 v3)
{
}
static void In(in Vector3 v3)
{
}
static void Ref(ref Vector3 v3)
{
}
Avoid the copy, Everywhere
45
そこで登場するのが新しいキーワード
“in”
(このコード例自体には何の意味もないです)
static void Normal(Vector3 v3)
{
}
static void In(in Vector3 v3)
{
}
static void Ref(ref Vector3 v3)
{
}
Avoid the copy, Everywhere
46
Normal(v3);
In(v3);
Ref(ref v3);
呼び側のコード
static void Normal(Vector3 v3)
{
}
static void In(in Vector3 v3)
{
}
static void Ref(ref Vector3 v3)
{
}
Avoid the copy, Everywhere
47
ldloc.0
call Noraml
ldloca.0
call In
ldloca.0
call Ref
Normal(v3);
In(v3);
Ref(ref v3);
呼び側のIL
static void Normal(Vector3 v3)
{
}
static void In(in Vector3 v3)
{
}
static void Ref(ref Vector3 v3)
{
}
Avoid the copy, Everywhere
48
ldloc.0
call Noraml
ldloca.0
call In
ldloca.0
call Ref
呼び方は普通と一緒なのに
refと同じく参照渡しされる!
Normal(v3);
In(v3);
Ref(ref v3);
じゃあ全部 in でいいね!
(にはならない)
static void Normal(Vector3 v3)
{
_ = v3.magnitude;
}
static void In(in Vector3 v3)
{
_ = v3.magnitude;
}
static void Ref(ref Vector3 v3)
{
_ = v3.magnitude;
}
Avoid the copy, Everywhere
49
ldarga.0
call get_magnitude
ldarg.0
ldobj
stloc.0
ldloca.0
call get_magnitude
ldarg.0
call get_magnitude
呼ばれ側のIL
static void Normal(Vector3 v3)
{
_ = v3.magnitude;
}
static void In(in Vector3 v3)
{
_ = v3.magnitude;
}
static void Ref(ref Vector3 v3)
{
_ = v3.magnitude;
}
Avoid the copy, Everywhere
50
ldarga.0
call get_magnitude
ldarg.0
ldobj
stloc.0
ldloca.0
call get_magnitude
ldarg.0
call get_magnitude
呼ばれ側のIL
コピーされてる(防御的コピー)
var v3_2 = v3;
_ = v3_2.magnitude;
static void Normal(Vector3 v3)
{
_ = v3.magnitude;
_ = v3.magnitude;
}
static void In(in Vector3 v3)
{
_ = v3.magnitude;
_ = v3.magnitude;
}
static void Ref(ref Vector3 v3)
{
_ = v3.magnitude;
_ = v3.magnitude;
}
Avoid the copy, Everywhere
51
ldarga.0
call get_magnitude
ldarg.0
ldobj
stloc.0
ldloca.0
call get_magnitude
ldarg.0
call get_magnitude
呼ばれ側のIL
二回呼べばコピーもそのまま二つ
ldarga.0
call get_magnitude
ldarg.0
ldobj
stloc.0
ldloca.0
call get_magnitude
ldarg.0
call get_magnitude
Best practice to use `in`
52
— in はコンパイルすると([In][IsReadOnly]ref T t) になる
— 読み取り専用のため、フィールドへの代入はできない
— v3.x = 10.0f; // compile error
— メソッド呼び出しは可能だが、中身が変わらない保証がないため防御的コピーが走る
— そのため、うかつに多用すると防御的コピーにより、むしろ性能低下もありうる
— もしそれがMutable Structだともはやわけわからないことに
— (Vector3.magnitudeはプロパティなのでメソッド扱い)
— 防御的コピーが走らない条件は
– プロパティ/メソッドを呼ばないこと
– あるいは readonly struct であること
– readonly structは書き換わらないことが保証されるため防御的コピーが走らない
Best practice to use `in`
53
— in はコンパイルすると([In][IsReadOnly]ref T t) になる
— 読み取り専用のため、フィールドへの代入はできない
— v3.x = 10.0f; // compile error
— メソッド呼び出しは可能だが、中身が変わらない保証がないため防御的コピーが走る
— そのため、うかつに多用すると防御的コピーにより、むしろ性能低下もありうる
— もしそれがMutable Structだともはやわけわからないことに
— 防御的コピーが走らない条件は
– プロパティ/メソッドを呼ばないこと
– あるいは readonly struct であること
– readonly structは書き換わらないことが保証されるため防御的コピーが走らない
public readonly struct MyVector3
{
public readonly float x;
public readonly float y;
public readonly float z;
public MyVector3(float x, float y, float z)
{
this.x = x; this.y = y; this.z = z;
}
readonly structの条件は全てのフィ
ールドがreadonlyであること
ref readonly structもOK
原則inの引数はreadonly struct
中でフィールドしか絶対触らないと力
強く言えるならナシではない
よくわからないなら基本使わない
readonly field(struct)の罠
54
— readonlyなfieldのstructにmutableな操作を行っても変更されない
— よって、ミュータブルな操作を行うものはreadonly fieldにすべきではない
— 原則的には「可能なものは」と言いたいところだけど
Unityの場合、可能なものが多いので……
public class NantokaBehaviour : MonoBehaviour
{
public readonly Vector3 NanikanoVector3;
public void Incr()
{
NanikanoVector3.Set(
NanikanoVector3.x + 1f,
NanikanoVector3.y + 1f,
NanikanoVector3.z + 1f);
}
}
何回Incr呼んでも0, 0, 0のまま
55
Manipulate Memory
改めてStructとは
56
— メモリを単純にマッピングした構造
– そのことだけ意識すれば、あとはやりたい放題できる
[StructLayout(LayoutKind.Sequential, Size = 16)]
public struct Empty16
{
}
public struct LongLong
{
public long X;
public long Y;
}
どちらも連続して16バイトの
領域を確保しているだけ、とい
う意味(に読める)
インデックス0とインデックス
8にアクセスしやすくしている
だけ(という風に読める)
57
// 先頭6バイトがTimestamp, 後ろ10バイトがランダムというUlidという仕様(ソート可能なGuidの代替)
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct Ulid
{
// TimestampとRandomnessのはじまりの部分だけ持っておく(最悪なくても別にいい)
[FieldOffset(0)] byte timestamp0;
[FieldOffset(6)] byte randomness0;
public static Ulid NewUlid()
{
var memory = default(Ulid); // 16バイト確保
var timestampMilliseconds = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
ref var fisrtByte = ref Unsafe.As<long, byte>(ref timestampMilliseconds);
Unsafe.Add(ref memory.timestamp0, 0) = Unsafe.Add(ref fisrtByte, 5);
Unsafe.Add(ref memory.timestamp0, 1) = Unsafe.Add(ref fisrtByte, 4);
Unsafe.Add(ref memory.timestamp0, 2) = Unsafe.Add(ref fisrtByte, 3);
Unsafe.Add(ref memory.timestamp0, 3) = Unsafe.Add(ref fisrtByte, 2);
Unsafe.Add(ref memory.timestamp0, 4) = Unsafe.Add(ref fisrtByte, 1);
Unsafe.Add(ref memory.timestamp0, 5) = Unsafe.Add(ref fisrtByte, 0);
Unsafe.WriteUnaligned(ref memory.randomness0, xorshift.NextULong());
Unsafe.WriteUnaligned(ref Unsafe.Add(ref memory.randomness0, 2), xorshift.NextULong());
return memory;
}
Ulid:
[Timestamp(6),
Randomness(10)]
の実装
(System.Runtime
.CompilerService
s.Unsafeを利用、
Unityでも動きま
すが別途参照は必
要、ポインタでや
ってもいいです)
58
// 先頭6バイトがTimestamp, 後ろ10バイトがランダムというUlidという仕様(ソート可能なGuidの代替)
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct Ulid
{
// TimestampとRandomnessのはじまりの部分だけ持っておく(最悪なくても別にいい)
[FieldOffset(0)] byte timestamp0;
[FieldOffset(6)] byte randomness0;
public static Ulid NewUlid()
{
var memory = default(Ulid); // 16バイト確保
var timestampMilliseconds = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
ref var fisrtByte = ref Unsafe.As<long, byte>(ref timestampMilliseconds);
Unsafe.Add(ref memory.timestamp0, 0) = Unsafe.Add(ref fisrtByte, 5);
Unsafe.Add(ref memory.timestamp0, 1) = Unsafe.Add(ref fisrtByte, 4);
Unsafe.Add(ref memory.timestamp0, 2) = Unsafe.Add(ref fisrtByte, 3);
Unsafe.Add(ref memory.timestamp0, 3) = Unsafe.Add(ref fisrtByte, 2);
Unsafe.Add(ref memory.timestamp0, 4) = Unsafe.Add(ref fisrtByte, 1);
Unsafe.Add(ref memory.timestamp0, 5) = Unsafe.Add(ref fisrtByte, 0);
Unsafe.WriteUnaligned(ref memory.randomness0, xorshift.NextULong());
Unsafe.WriteUnaligned(ref Unsafe.Add(ref memory.randomness0, 2), xorshift.NextULong());
return memory;
}
// メモリ領域をコピーすればおk。
// 文字列表現としてBase32エンコード(ToString)も同様に自分のメモリ粋から算出
public bool TryWriteBytes(Span<byte> destination)
{
if (destination.Length < 16)
{
return false;
}
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), this)
return true;
}
出力先としてbyte[]とstringがあればそれでいい
それらはほとんどメモリコピーで実現する
Union
59
[StructLayout(LayoutKind.Explicit, Pack = 1)]
internal struct GuidBits
{
[FieldOffset(0)]
public readonly Guid Value;
[FieldOffset(0)]
public readonly byte Byte0;
[FieldOffset(1)]
public readonly byte Byte1;
[FieldOffset(2)]
public readonly byte Byte2;
[FieldOffset(3)]
public readonly byte Byte3;
/* 中略(Byte4~Byte11) */
[FieldOffset(12)]
public readonly byte Byte12;
[FieldOffset(13)]
public readonly byte Byte13;
[FieldOffset(14)]
public readonly byte Byte14;
[FieldOffset(15)]
public readonly byte Byte15;
Guidとbyte0~16の重ね合わせ
(同一FieldOffset)
通常Stringかbyte[]からしか生成でき
ないGuidを、byte0~16を埋めるだけ
で自由に生成する(本来弄れないGuid
としてのメモリ領域を重ね合わせて
安全に(not unsafe)弄る)
MessagePack for C#でUtf8 Bytesのス
ライスから文字列のアロケーションを避
けて直接Guidに変換するのに利用
改めてStructが要素の配列とは
60
— メモリにStructが単純に並んでいる
X Y Z X Y Z X Y Z X Y ZVector3[]
メモリをまるごとコピーするだけで
最速のシリアライズだよね説
(エンディアンは揃える)
実際MagicOnionで有効にすることが可
能(サーバーもC#なので直接メモリをぶ
ん投げて受け取るのが簡単)
ただしStructの中には参照型(String含
む)は含めないこと。ポイン タをコピー
しても意味がない
61
public class UnsafeDirectBlitArrayFormatter<T> : IMessagePackFormatter<T[]> where T : struct
{
public unsafe int Serialize(ref byte[] bytes, int offset, T[] value)
{
var startOffset = offset;
var byteLen = value.Length * UnsafeUtility.SizeOf<T>();
/* 中略(MsgPackでのExtヘッダー書き込み) */
ulong handle2;
var srcPointer = UnsafeUtility
.PinGCArrayAndGetDataAddress(value, out handle2);
try
{
fixed (void* dstPointer = &bytes[offset])
{
UnsafeUtility.MemCpy(dstPointer, srcPointer, byteLen);
}
}
finally
{
UnsafeUtility.ReleaseGCObject(handle2);
}
// ...
}
T[]をbytesにシリアライズ
memcpyするだけ
62
Span and NativeArray
Span vs NativeArray
63
— Span<T>
— System.Memory
— .NET Standard 2.1では標準(現在は外部ライブラリが必要)
— C# 7.2と統合されている
— あらゆる連続したメモリ領域のビュー
— 配列、stackalloc、ネイティブメモリ(ポインタ)、文字列(String)
— NativeArray
— Unity固有, 特にDOTSのキーパーツ
— UnsafeUtility.Malloc で獲得するUnmanaged Memoryのビュー
フレームワーク対応がないと意味がない
64
— Span<T>
— 今までのAPIがT[]しか受け入れなかったりすると、結局T[]への変換が必要になる
— 無駄アロケート
— .NET Core 2.1で対応充実させ中
— 例えばConvert.ToBase64Stringがbyte[]のほかReadOnlySpan<byte>を受けとる
— つまりUnityではAPI側の対応がほとんどないのでSpanだけ入れても意味は限定的
— NativeArray
— DOTS周辺で使えるけれど、やはり一部のAPIは対応が必要
— 例えばMesh.SetVertexBufferDataとしてList<T>やT[]以外にNativeArray<T>を受け
取るようになったのは 2019.3から
— というように、スムーズに全体的に統合されていくのはもうちょっとかな?
65
Conclusion
の前に。
66
— 構造体の色をクラスとは別の色に変更しておこう!
– 性能特性が異なるもののため、見分けがつくと、とても楽になる
Structを恐れない
67
— Structの使いこなし自体は、もはや必須
– Classを優先する牧歌的時代は終わった
– 確かに罠は(いっぱい)あるが、難しい話ではない
– 覚えるパターンが(ちょっと)多いだけで
— そしてこの流れは戻らない
– 言語強化から、フレームワークの抜本的変更まで、時代は既に来てる
– ……とはいえ、じゃあいきなりめっちゃ使うかと言うと、それはまた別の話
– 使うべき時に使い、読めるようにするという、当たり前を大事にしよう

More Related Content

What's hot

Unity 2018-2019を見据えたDeNAのUnity開発のこれから [DeNA TechCon 2019]
Unity 2018-2019を見据えたDeNAのUnity開発のこれから [DeNA TechCon 2019]Unity 2018-2019を見据えたDeNAのUnity開発のこれから [DeNA TechCon 2019]
Unity 2018-2019を見据えたDeNAのUnity開発のこれから [DeNA TechCon 2019]DeNA
 
Observableで非同期処理
Observableで非同期処理Observableで非同期処理
Observableで非同期処理torisoup
 
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しようUnity Technologies Japan K.K.
 
コルーチンでC++でも楽々ゲーム作成!
コルーチンでC++でも楽々ゲーム作成!コルーチンでC++でも楽々ゲーム作成!
コルーチンでC++でも楽々ゲーム作成!amusementcreators
 
C# 8.0 非同期ストリーム
C# 8.0 非同期ストリームC# 8.0 非同期ストリーム
C# 8.0 非同期ストリーム信之 岩永
 
【Unite 2018 Tokyo】そろそろ楽がしたい!新アセットバンドルワークフロー&リソースマネージャー詳細解説
【Unite 2018 Tokyo】そろそろ楽がしたい!新アセットバンドルワークフロー&リソースマネージャー詳細解説【Unite 2018 Tokyo】そろそろ楽がしたい!新アセットバンドルワークフロー&リソースマネージャー詳細解説
【Unite 2018 Tokyo】そろそろ楽がしたい!新アセットバンドルワークフロー&リソースマネージャー詳細解説Unity Technologies Japan K.K.
 
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方
Observable Everywhere  - Rxの原則とUniRxにみるデータソースの見つけ方Observable Everywhere  - Rxの原則とUniRxにみるデータソースの見つけ方
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方Yoshifumi Kawai
 
Unityでパフォーマンスの良いUIを作る為のTips
Unityでパフォーマンスの良いUIを作る為のTipsUnityでパフォーマンスの良いUIを作る為のTips
Unityでパフォーマンスの良いUIを作る為のTipsUnity Technologies Japan K.K.
 
UniTask入門
UniTask入門UniTask入門
UniTask入門torisoup
 
async/await のしくみ
async/await のしくみasync/await のしくみ
async/await のしくみ信之 岩永
 
NextGen Server/Client Architecture - gRPC + Unity + C#
NextGen Server/Client Architecture - gRPC + Unity + C#NextGen Server/Client Architecture - gRPC + Unity + C#
NextGen Server/Client Architecture - gRPC + Unity + C#Yoshifumi Kawai
 
【Unite 2018 Tokyo】Unityにおける疎結合設計 ~UIへの適用事例から学ぶ、テクニックとメリット~
【Unite 2018 Tokyo】Unityにおける疎結合設計 ~UIへの適用事例から学ぶ、テクニックとメリット~【Unite 2018 Tokyo】Unityにおける疎結合設計 ~UIへの適用事例から学ぶ、テクニックとメリット~
【Unite 2018 Tokyo】Unityにおける疎結合設計 ~UIへの適用事例から学ぶ、テクニックとメリット~UnityTechnologiesJapan002
 
Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現
Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現
Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現Yoshifumi Kawai
 
ネットワーク ゲームにおけるTCPとUDPの使い分け
ネットワーク ゲームにおけるTCPとUDPの使い分けネットワーク ゲームにおけるTCPとUDPの使い分け
ネットワーク ゲームにおけるTCPとUDPの使い分けモノビット エンジン
 
Unityではじめるオープンワールド制作 エンジニア編
Unityではじめるオープンワールド制作 エンジニア編Unityではじめるオープンワールド制作 エンジニア編
Unityではじめるオープンワールド制作 エンジニア編Unity Technologies Japan K.K.
 
.NET Core 3.0時代のメモリ管理
.NET Core 3.0時代のメモリ管理.NET Core 3.0時代のメモリ管理
.NET Core 3.0時代のメモリ管理KageShiron
 
A quick tour of the Cysharp OSS
A quick tour of the Cysharp OSSA quick tour of the Cysharp OSS
A quick tour of the Cysharp OSSYoshifumi Kawai
 
MagicOnion~C#でゲームサーバを開発しよう~
MagicOnion~C#でゲームサーバを開発しよう~MagicOnion~C#でゲームサーバを開発しよう~
MagicOnion~C#でゲームサーバを開発しよう~torisoup
 
インタフェース完全に理解した
インタフェース完全に理解したインタフェース完全に理解した
インタフェース完全に理解したtorisoup
 

What's hot (20)

Unity 2018-2019を見据えたDeNAのUnity開発のこれから [DeNA TechCon 2019]
Unity 2018-2019を見据えたDeNAのUnity開発のこれから [DeNA TechCon 2019]Unity 2018-2019を見据えたDeNAのUnity開発のこれから [DeNA TechCon 2019]
Unity 2018-2019を見据えたDeNAのUnity開発のこれから [DeNA TechCon 2019]
 
Observableで非同期処理
Observableで非同期処理Observableで非同期処理
Observableで非同期処理
 
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
 
コルーチンでC++でも楽々ゲーム作成!
コルーチンでC++でも楽々ゲーム作成!コルーチンでC++でも楽々ゲーム作成!
コルーチンでC++でも楽々ゲーム作成!
 
C# 8.0 非同期ストリーム
C# 8.0 非同期ストリームC# 8.0 非同期ストリーム
C# 8.0 非同期ストリーム
 
UE4でマルチプレイヤーゲームを作ろう
UE4でマルチプレイヤーゲームを作ろうUE4でマルチプレイヤーゲームを作ろう
UE4でマルチプレイヤーゲームを作ろう
 
【Unite 2018 Tokyo】そろそろ楽がしたい!新アセットバンドルワークフロー&リソースマネージャー詳細解説
【Unite 2018 Tokyo】そろそろ楽がしたい!新アセットバンドルワークフロー&リソースマネージャー詳細解説【Unite 2018 Tokyo】そろそろ楽がしたい!新アセットバンドルワークフロー&リソースマネージャー詳細解説
【Unite 2018 Tokyo】そろそろ楽がしたい!新アセットバンドルワークフロー&リソースマネージャー詳細解説
 
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方
Observable Everywhere  - Rxの原則とUniRxにみるデータソースの見つけ方Observable Everywhere  - Rxの原則とUniRxにみるデータソースの見つけ方
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方
 
Unityでパフォーマンスの良いUIを作る為のTips
Unityでパフォーマンスの良いUIを作る為のTipsUnityでパフォーマンスの良いUIを作る為のTips
Unityでパフォーマンスの良いUIを作る為のTips
 
UniTask入門
UniTask入門UniTask入門
UniTask入門
 
async/await のしくみ
async/await のしくみasync/await のしくみ
async/await のしくみ
 
NextGen Server/Client Architecture - gRPC + Unity + C#
NextGen Server/Client Architecture - gRPC + Unity + C#NextGen Server/Client Architecture - gRPC + Unity + C#
NextGen Server/Client Architecture - gRPC + Unity + C#
 
【Unite 2018 Tokyo】Unityにおける疎結合設計 ~UIへの適用事例から学ぶ、テクニックとメリット~
【Unite 2018 Tokyo】Unityにおける疎結合設計 ~UIへの適用事例から学ぶ、テクニックとメリット~【Unite 2018 Tokyo】Unityにおける疎結合設計 ~UIへの適用事例から学ぶ、テクニックとメリット~
【Unite 2018 Tokyo】Unityにおける疎結合設計 ~UIへの適用事例から学ぶ、テクニックとメリット~
 
Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現
Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現
Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現
 
ネットワーク ゲームにおけるTCPとUDPの使い分け
ネットワーク ゲームにおけるTCPとUDPの使い分けネットワーク ゲームにおけるTCPとUDPの使い分け
ネットワーク ゲームにおけるTCPとUDPの使い分け
 
Unityではじめるオープンワールド制作 エンジニア編
Unityではじめるオープンワールド制作 エンジニア編Unityではじめるオープンワールド制作 エンジニア編
Unityではじめるオープンワールド制作 エンジニア編
 
.NET Core 3.0時代のメモリ管理
.NET Core 3.0時代のメモリ管理.NET Core 3.0時代のメモリ管理
.NET Core 3.0時代のメモリ管理
 
A quick tour of the Cysharp OSS
A quick tour of the Cysharp OSSA quick tour of the Cysharp OSS
A quick tour of the Cysharp OSS
 
MagicOnion~C#でゲームサーバを開発しよう~
MagicOnion~C#でゲームサーバを開発しよう~MagicOnion~C#でゲームサーバを開発しよう~
MagicOnion~C#でゲームサーバを開発しよう~
 
インタフェース完全に理解した
インタフェース完全に理解したインタフェース完全に理解した
インタフェース完全に理解した
 

Similar to 【Unite Tokyo 2019】Understanding C# Struct All Things

パターンでわかる! .NET Coreの非同期処理
パターンでわかる! .NET Coreの非同期処理パターンでわかる! .NET Coreの非同期処理
パターンでわかる! .NET Coreの非同期処理Kouji Matsui
 
NetBSDのクロスビルドのしくみとインストール済みLive Imageの作成
NetBSDのクロスビルドのしくみとインストール済みLive Imageの作成NetBSDのクロスビルドのしくみとインストール済みLive Imageの作成
NetBSDのクロスビルドのしくみとインストール済みLive Imageの作成Izumi Tsutsui
 
【Unite Tokyo 2019】運用中超大規模タイトルにおけるUnityアップデート課題の解決手法と事例
【Unite Tokyo 2019】運用中超大規模タイトルにおけるUnityアップデート課題の解決手法と事例【Unite Tokyo 2019】運用中超大規模タイトルにおけるUnityアップデート課題の解決手法と事例
【Unite Tokyo 2019】運用中超大規模タイトルにおけるUnityアップデート課題の解決手法と事例UnityTechnologiesJapan002
 
Ansible troubleshooting 101_2021
Ansible troubleshooting 101_2021Ansible troubleshooting 101_2021
Ansible troubleshooting 101_2021Hideki Saito
 
Visual Studioで始めるTypeScript開発入門
Visual Studioで始めるTypeScript開発入門Visual Studioで始めるTypeScript開発入門
Visual Studioで始めるTypeScript開発入門Narami Kiyokura
 
ICSをビルドしてみた
ICSをビルドしてみたICSをビルドしてみた
ICSをビルドしてみたkinneko
 
今から始める、Windows 10&新.NETへの移行戦略
今から始める、Windows 10&新.NETへの移行戦略今から始める、Windows 10&新.NETへの移行戦略
今から始める、Windows 10&新.NETへの移行戦略信之 岩永
 
2014 1018 OSC-Fall Tokyo NETMF
2014 1018 OSC-Fall Tokyo NETMF2014 1018 OSC-Fall Tokyo NETMF
2014 1018 OSC-Fall Tokyo NETMFAtomu Hidaka
 
【学習メモ#1st】12ステップで作る組込みOS自作入門
【学習メモ#1st】12ステップで作る組込みOS自作入門【学習メモ#1st】12ステップで作る組込みOS自作入門
【学習メモ#1st】12ステップで作る組込みOS自作入門sandai
 
Fukuoka.NET Conf 2018: 挑み続ける!Dockerコンテナによる ASP.NET Core アプリケーション開発事例
Fukuoka.NET Conf 2018: 挑み続ける!Dockerコンテナによる ASP.NET Core アプリケーション開発事例Fukuoka.NET Conf 2018: 挑み続ける!Dockerコンテナによる ASP.NET Core アプリケーション開発事例
Fukuoka.NET Conf 2018: 挑み続ける!Dockerコンテナによる ASP.NET Core アプリケーション開発事例Joni
 
Unityで使える C# 6.0~と .NET 4.6
Unityで使える C# 6.0~と .NET 4.6Unityで使える C# 6.0~と .NET 4.6
Unityで使える C# 6.0~と .NET 4.6信之 岩永
 
ASP.NET vNextの全貌
ASP.NET vNextの全貌ASP.NET vNextの全貌
ASP.NET vNextの全貌A AOKI
 
Ruby向け帳票ソリューション「ThinReports」の開発で知るOSSの威力
Ruby向け帳票ソリューション「ThinReports」の開発で知るOSSの威力Ruby向け帳票ソリューション「ThinReports」の開発で知るOSSの威力
Ruby向け帳票ソリューション「ThinReports」の開発で知るOSSの威力ThinReports
 
ML Studio / CNTK ハンズオン資料の紹介と開発環境の構築手順
ML Studio / CNTK ハンズオン資料の紹介と開発環境の構築手順ML Studio / CNTK ハンズオン資料の紹介と開発環境の構築手順
ML Studio / CNTK ハンズオン資料の紹介と開発環境の構築手順Yoshitaka Seo
 
jQueryの先に行こう!最先端のWeb開発トレンドを学ぶ
jQueryの先に行こう!最先端のWeb開発トレンドを学ぶjQueryの先に行こう!最先端のWeb開発トレンドを学ぶ
jQueryの先に行こう!最先端のWeb開発トレンドを学ぶShumpei Shiraishi
 
Linux & Mac OS でも動く! ~ クロスプラットフォーム対応に見る ASP.NET Core 5 の可能性 ~
Linux & Mac OS でも動く! ~ クロスプラットフォーム対応に見る ASP.NET Core 5 の可能性 ~Linux & Mac OS でも動く! ~ クロスプラットフォーム対応に見る ASP.NET Core 5 の可能性 ~
Linux & Mac OS でも動く! ~ クロスプラットフォーム対応に見る ASP.NET Core 5 の可能性 ~Akira Inoue
 

Similar to 【Unite Tokyo 2019】Understanding C# Struct All Things (20)

パターンでわかる! .NET Coreの非同期処理
パターンでわかる! .NET Coreの非同期処理パターンでわかる! .NET Coreの非同期処理
パターンでわかる! .NET Coreの非同期処理
 
NetBSDのクロスビルドのしくみとインストール済みLive Imageの作成
NetBSDのクロスビルドのしくみとインストール済みLive Imageの作成NetBSDのクロスビルドのしくみとインストール済みLive Imageの作成
NetBSDのクロスビルドのしくみとインストール済みLive Imageの作成
 
【Unite Tokyo 2019】運用中超大規模タイトルにおけるUnityアップデート課題の解決手法と事例
【Unite Tokyo 2019】運用中超大規模タイトルにおけるUnityアップデート課題の解決手法と事例【Unite Tokyo 2019】運用中超大規模タイトルにおけるUnityアップデート課題の解決手法と事例
【Unite Tokyo 2019】運用中超大規模タイトルにおけるUnityアップデート課題の解決手法と事例
 
Ansible troubleshooting 101_2021
Ansible troubleshooting 101_2021Ansible troubleshooting 101_2021
Ansible troubleshooting 101_2021
 
Visual Studioで始めるTypeScript開発入門
Visual Studioで始めるTypeScript開発入門Visual Studioで始めるTypeScript開発入門
Visual Studioで始めるTypeScript開発入門
 
ICSをビルドしてみた
ICSをビルドしてみたICSをビルドしてみた
ICSをビルドしてみた
 
Python Autotest pdc2008w
Python Autotest pdc2008wPython Autotest pdc2008w
Python Autotest pdc2008w
 
今から始める、Windows 10&新.NETへの移行戦略
今から始める、Windows 10&新.NETへの移行戦略今から始める、Windows 10&新.NETへの移行戦略
今から始める、Windows 10&新.NETへの移行戦略
 
2014 1018 OSC-Fall Tokyo NETMF
2014 1018 OSC-Fall Tokyo NETMF2014 1018 OSC-Fall Tokyo NETMF
2014 1018 OSC-Fall Tokyo NETMF
 
【学習メモ#1st】12ステップで作る組込みOS自作入門
【学習メモ#1st】12ステップで作る組込みOS自作入門【学習メモ#1st】12ステップで作る組込みOS自作入門
【学習メモ#1st】12ステップで作る組込みOS自作入門
 
Fukuoka.NET Conf 2018: 挑み続ける!Dockerコンテナによる ASP.NET Core アプリケーション開発事例
Fukuoka.NET Conf 2018: 挑み続ける!Dockerコンテナによる ASP.NET Core アプリケーション開発事例Fukuoka.NET Conf 2018: 挑み続ける!Dockerコンテナによる ASP.NET Core アプリケーション開発事例
Fukuoka.NET Conf 2018: 挑み続ける!Dockerコンテナによる ASP.NET Core アプリケーション開発事例
 
Unityで使える C# 6.0~と .NET 4.6
Unityで使える C# 6.0~と .NET 4.6Unityで使える C# 6.0~と .NET 4.6
Unityで使える C# 6.0~と .NET 4.6
 
私とOSSの25年
私とOSSの25年私とOSSの25年
私とOSSの25年
 
ASP.NET vNextの全貌
ASP.NET vNextの全貌ASP.NET vNextの全貌
ASP.NET vNextの全貌
 
Osoljp studygroup201303
Osoljp studygroup201303Osoljp studygroup201303
Osoljp studygroup201303
 
Ruby向け帳票ソリューション「ThinReports」の開発で知るOSSの威力
Ruby向け帳票ソリューション「ThinReports」の開発で知るOSSの威力Ruby向け帳票ソリューション「ThinReports」の開発で知るOSSの威力
Ruby向け帳票ソリューション「ThinReports」の開発で知るOSSの威力
 
ML Studio / CNTK ハンズオン資料の紹介と開発環境の構築手順
ML Studio / CNTK ハンズオン資料の紹介と開発環境の構築手順ML Studio / CNTK ハンズオン資料の紹介と開発環境の構築手順
ML Studio / CNTK ハンズオン資料の紹介と開発環境の構築手順
 
jQueryの先に行こう!最先端のWeb開発トレンドを学ぶ
jQueryの先に行こう!最先端のWeb開発トレンドを学ぶjQueryの先に行こう!最先端のWeb開発トレンドを学ぶ
jQueryの先に行こう!最先端のWeb開発トレンドを学ぶ
 
Clrh 20140906 lt
Clrh 20140906 ltClrh 20140906 lt
Clrh 20140906 lt
 
Linux & Mac OS でも動く! ~ クロスプラットフォーム対応に見る ASP.NET Core 5 の可能性 ~
Linux & Mac OS でも動く! ~ クロスプラットフォーム対応に見る ASP.NET Core 5 の可能性 ~Linux & Mac OS でも動く! ~ クロスプラットフォーム対応に見る ASP.NET Core 5 の可能性 ~
Linux & Mac OS でも動く! ~ クロスプラットフォーム対応に見る ASP.NET Core 5 の可能性 ~
 

More from UnityTechnologiesJapan002

10分でわかる Unityコンピュータービジョン
10分でわかる Unityコンピュータービジョン10分でわかる Unityコンピュータービジョン
10分でわかる UnityコンピュータービジョンUnityTechnologiesJapan002
 
ROSのロボットモデルでバーチャルロボット受肉する
ROSのロボットモデルでバーチャルロボット受肉するROSのロボットモデルでバーチャルロボット受肉する
ROSのロボットモデルでバーチャルロボット受肉するUnityTechnologiesJapan002
 
Unityでロボットの教師データは作れる!
Unityでロボットの教師データは作れる!Unityでロボットの教師データは作れる!
Unityでロボットの教師データは作れる!UnityTechnologiesJapan002
 
産業用ロボット開発におけるUnityの活用
産業用ロボット開発におけるUnityの活用産業用ロボット開発におけるUnityの活用
産業用ロボット開発におけるUnityの活用UnityTechnologiesJapan002
 
建設シミュレータOCSの開発 / OCS・VTC on Unity におけるROS対応機能について
建設シミュレータOCSの開発 / OCS・VTC on Unity におけるROS対応機能について建設シミュレータOCSの開発 / OCS・VTC on Unity におけるROS対応機能について
建設シミュレータOCSの開発 / OCS・VTC on Unity におけるROS対応機能についてUnityTechnologiesJapan002
 
中国深センから盛り上がる、ソフトウェアフレンドリーなロボティクス
中国深センから盛り上がる、ソフトウェアフレンドリーなロボティクス中国深センから盛り上がる、ソフトウェアフレンドリーなロボティクス
中国深センから盛り上がる、ソフトウェアフレンドリーなロボティクスUnityTechnologiesJapan002
 
Unityでお手軽ロボット開発「toio SDK for Unity」最新事例
Unityでお手軽ロボット開発「toio SDK for Unity」最新事例Unityでお手軽ロボット開発「toio SDK for Unity」最新事例
Unityでお手軽ロボット開発「toio SDK for Unity」最新事例UnityTechnologiesJapan002
 
集まれ!Dreamingエンジニア! 〜箱庭で紡ぎ出されるIoT/クラウドロボティクス開発の新しいカタチ〜
集まれ!Dreamingエンジニア! 〜箱庭で紡ぎ出されるIoT/クラウドロボティクス開発の新しいカタチ〜集まれ!Dreamingエンジニア! 〜箱庭で紡ぎ出されるIoT/クラウドロボティクス開発の新しいカタチ〜
集まれ!Dreamingエンジニア! 〜箱庭で紡ぎ出されるIoT/クラウドロボティクス開発の新しいカタチ〜UnityTechnologiesJapan002
 
BIMからはじまる異世界転生 ~Unity Reflect が叶える新しい建築の世界~
BIMからはじまる異世界転生 ~Unity Reflect が叶える新しい建築の世界~BIMからはじまる異世界転生 ~Unity Reflect が叶える新しい建築の世界~
BIMからはじまる異世界転生 ~Unity Reflect が叶える新しい建築の世界~UnityTechnologiesJapan002
 
【Unity道場 自動車編】Unityで実現する産業向けxRソリューション
【Unity道場 自動車編】Unityで実現する産業向けxRソリューション【Unity道場 自動車編】Unityで実現する産業向けxRソリューション
【Unity道場 自動車編】Unityで実現する産業向けxRソリューションUnityTechnologiesJapan002
 
【Unity道場 自動車編】トヨタのxR活用で進める現場DXへの挑戦 ~UnityとHoloLens 2を用いて~
【Unity道場 自動車編】トヨタのxR活用で進める現場DXへの挑戦 ~UnityとHoloLens 2を用いて~【Unity道場 自動車編】トヨタのxR活用で進める現場DXへの挑戦 ~UnityとHoloLens 2を用いて~
【Unity道場 自動車編】トヨタのxR活用で進める現場DXへの挑戦 ~UnityとHoloLens 2を用いて~UnityTechnologiesJapan002
 
【Unity道場 自動車編】空間再現ディスプレイの概要と活用事例
【Unity道場 自動車編】空間再現ディスプレイの概要と活用事例【Unity道場 自動車編】空間再現ディスプレイの概要と活用事例
【Unity道場 自動車編】空間再現ディスプレイの概要と活用事例UnityTechnologiesJapan002
 
【Unity道場 自動車編】 リアルタイム3D技術が支えるデジタルツイン
【Unity道場 自動車編】 リアルタイム3D技術が支えるデジタルツイン【Unity道場 自動車編】 リアルタイム3D技術が支えるデジタルツイン
【Unity道場 自動車編】 リアルタイム3D技術が支えるデジタルツインUnityTechnologiesJapan002
 
【Unity道場 自動車編】モビリティへの活用に向けて
【Unity道場 自動車編】モビリティへの活用に向けて【Unity道場 自動車編】モビリティへの活用に向けて
【Unity道場 自動車編】モビリティへの活用に向けてUnityTechnologiesJapan002
 

More from UnityTechnologiesJapan002 (20)

5分でわかる Sensor SDK
5分でわかる Sensor SDK5分でわかる Sensor SDK
5分でわかる Sensor SDK
 
10分でわかる Unityコンピュータービジョン
10分でわかる Unityコンピュータービジョン10分でわかる Unityコンピュータービジョン
10分でわかる Unityコンピュータービジョン
 
5分でわかる Unity Forma
5分でわかる Unity Forma5分でわかる Unity Forma
5分でわかる Unity Forma
 
ROSのロボットモデルでバーチャルロボット受肉する
ROSのロボットモデルでバーチャルロボット受肉するROSのロボットモデルでバーチャルロボット受肉する
ROSのロボットモデルでバーチャルロボット受肉する
 
Unityでロボットの教師データは作れる!
Unityでロボットの教師データは作れる!Unityでロボットの教師データは作れる!
Unityでロボットの教師データは作れる!
 
ARとUnity-Robotics-Hubの連携
ARとUnity-Robotics-Hubの連携ARとUnity-Robotics-Hubの連携
ARとUnity-Robotics-Hubの連携
 
産業用ロボット開発におけるUnityの活用
産業用ロボット開発におけるUnityの活用産業用ロボット開発におけるUnityの活用
産業用ロボット開発におけるUnityの活用
 
建設シミュレータOCSの開発 / OCS・VTC on Unity におけるROS対応機能について
建設シミュレータOCSの開発 / OCS・VTC on Unity におけるROS対応機能について建設シミュレータOCSの開発 / OCS・VTC on Unity におけるROS対応機能について
建設シミュレータOCSの開発 / OCS・VTC on Unity におけるROS対応機能について
 
UnityとROSの連携について
UnityとROSの連携についてUnityとROSの連携について
UnityとROSの連携について
 
中国深センから盛り上がる、ソフトウェアフレンドリーなロボティクス
中国深センから盛り上がる、ソフトウェアフレンドリーなロボティクス中国深センから盛り上がる、ソフトウェアフレンドリーなロボティクス
中国深センから盛り上がる、ソフトウェアフレンドリーなロボティクス
 
Unityでお手軽ロボット開発「toio SDK for Unity」最新事例
Unityでお手軽ロボット開発「toio SDK for Unity」最新事例Unityでお手軽ロボット開発「toio SDK for Unity」最新事例
Unityでお手軽ロボット開発「toio SDK for Unity」最新事例
 
集まれ!Dreamingエンジニア! 〜箱庭で紡ぎ出されるIoT/クラウドロボティクス開発の新しいカタチ〜
集まれ!Dreamingエンジニア! 〜箱庭で紡ぎ出されるIoT/クラウドロボティクス開発の新しいカタチ〜集まれ!Dreamingエンジニア! 〜箱庭で紡ぎ出されるIoT/クラウドロボティクス開発の新しいカタチ〜
集まれ!Dreamingエンジニア! 〜箱庭で紡ぎ出されるIoT/クラウドロボティクス開発の新しいカタチ〜
 
5分でわかる Unity点群
5分でわかる Unity点群5分でわかる Unity点群
5分でわかる Unity点群
 
5分でわかる Unity Reflect
5分でわかる Unity Reflect5分でわかる Unity Reflect
5分でわかる Unity Reflect
 
BIMからはじまる異世界転生 ~Unity Reflect が叶える新しい建築の世界~
BIMからはじまる異世界転生 ~Unity Reflect が叶える新しい建築の世界~BIMからはじまる異世界転生 ~Unity Reflect が叶える新しい建築の世界~
BIMからはじまる異世界転生 ~Unity Reflect が叶える新しい建築の世界~
 
【Unity道場 自動車編】Unityで実現する産業向けxRソリューション
【Unity道場 自動車編】Unityで実現する産業向けxRソリューション【Unity道場 自動車編】Unityで実現する産業向けxRソリューション
【Unity道場 自動車編】Unityで実現する産業向けxRソリューション
 
【Unity道場 自動車編】トヨタのxR活用で進める現場DXへの挑戦 ~UnityとHoloLens 2を用いて~
【Unity道場 自動車編】トヨタのxR活用で進める現場DXへの挑戦 ~UnityとHoloLens 2を用いて~【Unity道場 自動車編】トヨタのxR活用で進める現場DXへの挑戦 ~UnityとHoloLens 2を用いて~
【Unity道場 自動車編】トヨタのxR活用で進める現場DXへの挑戦 ~UnityとHoloLens 2を用いて~
 
【Unity道場 自動車編】空間再現ディスプレイの概要と活用事例
【Unity道場 自動車編】空間再現ディスプレイの概要と活用事例【Unity道場 自動車編】空間再現ディスプレイの概要と活用事例
【Unity道場 自動車編】空間再現ディスプレイの概要と活用事例
 
【Unity道場 自動車編】 リアルタイム3D技術が支えるデジタルツイン
【Unity道場 自動車編】 リアルタイム3D技術が支えるデジタルツイン【Unity道場 自動車編】 リアルタイム3D技術が支えるデジタルツイン
【Unity道場 自動車編】 リアルタイム3D技術が支えるデジタルツイン
 
【Unity道場 自動車編】モビリティへの活用に向けて
【Unity道場 自動車編】モビリティへの活用に向けて【Unity道場 自動車編】モビリティへの活用に向けて
【Unity道場 自動車編】モビリティへの活用に向けて
 

Recently uploaded

Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Danieldanielhu54
 
IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxIoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxAtomu Hidaka
 
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。iPride Co., Ltd.
 
UPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdfUPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdffurutsuka
 
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略Ryo Sasaki
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムsugiuralab
 
20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directoryosamut
 
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
新人研修のまとめ       2024/04/12の勉強会で発表されたものです。新人研修のまとめ       2024/04/12の勉強会で発表されたものです。
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。iPride Co., Ltd.
 
PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000Shota Ito
 

Recently uploaded (9)

Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Daniel
 
IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxIoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
 
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
 
UPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdfUPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdf
 
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システム
 
20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory
 
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
新人研修のまとめ       2024/04/12の勉強会で発表されたものです。新人研修のまとめ       2024/04/12の勉強会で発表されたものです。
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
 
PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000
 

【Unite Tokyo 2019】Understanding C# Struct All Things

  • 2. About Speaker 2 — 河合 宜文 / Kawai Yoshifumi / @neuecc — Cysharp, Inc. – CEO/CTO — Microsoft MVP for Developer Technologies(C#) — 50以上のOSS公開(UniRx, MagicOnion, MessagePack for C#, etc..) — 株式会社Cysharp — 2019年9月, Cygamesの子会社として設立 — C#関連の研究開発/OSS/コンサルティングを行う — C#大統一理論(サーバー/クライアントともにC#で実装する)を推進
  • 3. https://github.com/Cysharp OSS for Unity – GitHub/Cysharp 3 UniTask ★288 Provides an efficient async/await integration to Unity. RuntimeUnitTestToolkit ★87 CLI/GUI Frontend of Unity Test Runner to test on any platforms. RandomFixtureKit ★16 Fill random/edge-case value to target type for unit testing. MagicOnion ★1240 Unified Realtime/API Engine for .NET Core and Unity. MasterMemory ★407 Embedded Typed Readonly In-Memory Document Database for .NET Core and Unity.
  • 4. https://github.com/neuecc OSS for Unity – GitHub/neuecc 4 LINQ-to-GameObject-for-Unity ★448 Traverse GameObject Hierarchy by LINQ. PhotonWire ★92 Typed Asynchronous RPC Layer for Photon. SerializableDictionary ★87 SerializableCollections for Unity. ReMotion ★27 Hyper Fast Reactive Tween Engine for Unity. UniRx ★3722 Reactive Extensions for Unity. MessagePack-CSharp ★2089 Extremely Fast MessagePack Serializer. ZeroFormatter ★1778 Infinitely Fast Deserializer. Utf8Json ★1352 Definitely Fastest JSON Serializer.
  • 5.
  • 6. 6 The Evolution of C# Struct
  • 7. MessagePack for C#(v2-preview) 7 public ref struct MessagePackWriter T Deserialize<T>(in ReadOnlySequence<byte> byteSequence) Span<byte> bytes = stackalloc byte[36]; internal ref partial struct SequenceReader<T> where T : unmanaged, IEquatable<T> public ref byte GetPointer(int sizeHint) ref Entry v = ref entry[0];
  • 8. MessagePack for C#(v2-preview) 8 public ref struct MessagePackWriter T Deserialize<T>(in ReadOnlySequence<byte> byteSequence) Span<byte> bytes = stackalloc byte[36]; internal ref partial struct SequenceReader<T> where T : unmanaged, IEquatable<T> ref struct Span<T> = stackalloc in parameter where : unmanaged public ref byte GetPointer(int sizeHint) ref Entry v = ref entry[0]; ref return ref local
  • 11. What’s new struct features 11 C# 7.2 C# 7.3 C# 8.0 in modifier on parameter ref readonly modifier on method returns, local readonly struct declaration ref struct declaration ref extension method stackalloc to Span reassign ref local additional generics constraints(unmanage d, Enum, Delegate) stackalloc initializer access fixed field without pinning readonly method Disposable ref structs Unmanaged constructed types https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/ C# 7.0 ref return statement ref local variables
  • 12. 12 Which Unity Version should we support? Unity Version C# Version .NET Version Unity 2017.4 6.0 .NET 3.5/.NET 4.6 Unity 2018.2 6.0 .NET 4.x/Standard 2.0 Unity 2018.3 7.3 .NET 4.x/Standard 2.0 Unity 2018.4 7.3 .NET 4.x/Standard 2.0 Unity 2019.1 7.3 .NET 4.x/Standard 2.0
  • 13. 13 Which Unity Version should we support? Unity Version C# Version .NET Version Unity 2017.4 6.0 .NET 3.5/.NET 4.6 Unity 2018.2 6.0 .NET 4.x/Standard 2.0 Unity 2018.3 7.3 .NET 4.x/Standard 2.0 Unity 2018.4 7.3 .NET 4.x/Standard 2.0 Unity 2019.1 7.3 .NET 4.x/Standard 2.0 2018.3以上一択、それ以下の バージョンはNot Supported
  • 14. 14 Which Unity Version should we support? Unity Version C# Version .NET Version Unity 2017.4 6.0 .NET 3.5/.NET 4.6 Unity 2018.2 6.0 .NET 4.x/Standard 2.0 Unity 2018.3 7.3 .NET 4.x/Standard 2.0 Unity 2018.4 7.3 .NET 4.x/Standard 2.0 Unity 2019.1 7.3 .NET 4.x/Standard 2.0 2018.3から以下の言語に関するdefineが使える CSHARP_7_3_OR_NEWER 以下のどちらか選んだほうが定義される NET_4_6 NET_STANDARD_2_0
  • 15. Struct is important for Performance! 15 — C# 7以降の急速なstruct強化はパフォーマンスのため — それは .NET Core でも、Unityでも — アプローチは異なれど、両者とも構造体をパフォーマンスのため活用している — 特にUnityの推すDOTS(Data Oriented Technology Stack)はstructの塊 — 今、全てを学び、備えよう public unsafe ref struct BlobBuilderArray<T> where T : struct public unsafe static ref T AsRef<T>(void* ptr) where T : struct public readonly struct BuildComponentDataToEntityLookupTask<TComponentData> : IDisposable where TComponentData : unmanaged, IComponentData, IEquatable<TComponentData> Unity ECSの中の C# 7.3表現
  • 16. 16 The Basic of C# Memory
  • 17. The Memory of C# 17 AppDomain(Managed) Thread Stack HeapThread Stack Unmanaged
  • 18. The Memory of C# 18 AppDomain(Managed) Thread Stack HeapThread Stack Unmanaged ローカル変数はスタック領域に格納される ヒープ領域に確保されたデータは GCの管理化に入る UnityではC#管理外のメモリを扱う こともよくある(特にDOTS)
  • 19. Let’s see memory layout 19 — SharpLabでメモリの中身を見よう! — https://sharplab.io/ — C# to IL, C# to C#, C# to ASMなど豊富な機能がある — Run と Inspectを組み合わせるとメモリの中身が見れる なお、組み込み型の場合、Unity(mono) とSharpLab(.NET Core)で中身が異なる ことがある場合に注意
  • 20. Struct Memory Int(4バイト)のX, Y, Z(12バイト)が 素直にメモリ上(スタック)に並ぶ
  • 22. Pass by Reference/Pass by Value 22 — C#はデフォルトは全て「値渡し」 — つまりコピーされます — ローカル変数への代入もコピー (T x) (ref T x) class 参照の値渡し 参照の参照渡し struct 値の値渡し 値の参照渡し
  • 24. 00 00 00 00 からそれっぽ いデータが入った気配
  • 25. 引き続き、 y = x で同じデ ータが追加で入ったっぽい
  • 28. int x int y int z コンパイル時(C# -> IL)の段 階で変数の置き場確保してお きます、的な
  • 29. Structの基本的な原則 29 — 全てはコピーに気をつける、ということ — クラスとの違い、ほとんどの問題はコピーにより引き起こされる — 大きなサイズの構造体を(基本的には)作らない – IntPtr.Size(4 or 8バイト)以上は参照渡しに比べて大きいということになる – それだと制限キツすぎなので、一般には16バイト以下ぐらいを目安に — 変更可能な構造体を(基本的には)作らない – コピーされることによって、変更したつもりが変更されない – 一度は悩むVector3変更されない問題
  • 30. Structの基本的な原則 30 — 全てはコピーに気をつける、ということ — クラスとの違い、ほとんどの問題はコピーにより引き起こされる — 大きなサイズの構造体を(基本的には)作らない – IntPtr.Size(4 or 8バイト)以上は参照渡しに比べて大きいということになる – それだと制限キツすぎなので、一般には16バイト以下ぐらいを目安に — 変更可能な構造体を(基本的には)作らない – コピーされることによって、変更したつもりが変更されない – 一度は悩むVector3変更されない問題 // position変えたつもりが変わらない! this.transform.position.Set(10f, 20f, 30f); // つまりこういうことだから this.transform.INTERNAL_get_position(out Vector3 value); value.Set(10f, 20f, 30f);
  • 31. Structの基本的な原則 31 — 全てはコピーに気をつける、ということ — クラスとの違い、ほとんどの問題はコピーにより引き起こされる — 大きなサイズの構造体を(基本的には)作らない – IntPtr.Size(4 or 8バイト)以上は参照渡しに比べて大きいということになる – それだと制限キツすぎなので、一般には16バイト以下ぐらいを目安に — 変更可能な構造体を(基本的には)作らない – コピーされることによって、変更したつもりが変更されない – 一度は悩むVector3変更されない問題 // position変えたつもりが変わらない! this.transform.position.Set(10f, 20f, 30f); // つまりこういうことだから this.transform.INTERNAL_get_position(out Vector3 value); value.Set(10f, 20f, 30f); positionがプロパティなのが悪い (フィールドならコピーの問題は起きない) が、これはtransformのデータの実体は アンマネージドメモリ側(UnityEngine)にある ためという、Unityならではの悩みでもある (C#/C++越境がどうしても抱える話)
  • 33. Box化との戦い 33 — ボックス化はnewと一緒 — むしろアンボックスの頻度的により悪い — ボックス化が避けられないなら最初からクラスで作ることも選択肢 — インターフェイスへのキャストに気をつける – ジェネリクスを使って回避していく – enumの場合はEnum(参照型)も同様 public static class BoxedInt { public static readonly object Zero = 0; public static readonly object One = 1; public static readonly object MinusOne = -1; } ある程度決まった値が頻繁にボッ クス化されるようなら、先に作っ ておいて使い回すという技
  • 34. Equalsの自動実装とBox化 34 — structはEqualsが実装されていない場合、自動的に以下のものが呼ばれる — めっちゃ遅い — Equalsは辞書のKeyにすると呼ばれる! そのため辞書のKeyにする構造体は IEquatable<T>とGetHashCodeの カスタム実装を必ず行うこと internal static bool DefaultEquals(object o1, object o2) { RuntimeType o1_type = (RuntimeType)o1.GetType(); RuntimeType o2_type = (RuntimeType)o2.GetType(); object[] fields; InternalEquals(o1, o2, out fields); for (int i = 0; i < fields.Length; i += 2) { object meVal = fields[i]; object youVal = fields[i + 1]; if (!meVal.Equals(youVal)) return false; } return true; } object, object比較のボクシング そもそもリフレクションで全フィ ールド比較(遅い)うえに、フィ ールドの戻り値もボクシング 原理主義的には可能なもの全てのStructにカスタム実装を入 れたほうがいい、ということになるけれど、あまりにも面倒 なので、さすがにそこはピンポイント(辞書のKeyになるもの だけ)でいいと思います
  • 35. Struct Layout and Padding 単純計算ではbyte(1) + long(8) + int(4) = 13ですが、 アラインメント調整のため、最長の8にそれぞれが合 わせられて8 * 3 = 24バイトの確保になっている
  • 36. Struct Layout and Padding StructLayoutやFieldOffsetによってレイアウ トはカスタマイズ可能。 structのデフォルトはSequential(宣言順) Autoに変えると、ZとXが詰められることで最 小の16バイトに縮む 参照型はstructと異なりデフォ ルトがAuto
  • 39. Zero Allocation foreach in List<T> 39 — foreachは .GetEnumerator -> while(MoveNext()) に変換される — (ただし配列の場合はコンパイル時にILでforに変換される) — つまり IEnumerator が生成されてヒープに確保されている? — されるようでされない var list = new List<int>() { 1, 2, 3 }; foreach (var item in list) { /* do anything */ }
  • 40. Mutable Struct is Evil but Useful 40 — 一時的な入れ物として使うものに向いてる public struct Enumerator : IEnumerator<T> { List<T> list; int index; int version; T current; } public struct BinaryReader { byte[] bytes; int offset; } List<T>は直接GetEnumeratorを呼べる 状況ではstruct List<T>.Enumerator を 返すためゼロアロケーション バイナリを読みすすめる際にReadXxx を呼ぶたびにoffsetを追加していくとい うステートを管理 局所的にしか使わないので classじゃなくてもいい
  • 41. ref struct 41 — スタックにしか置けないという制約がref struct – 元々はSpan<T>(System.Memory, .NET Standard 2.0外部ライブラリ)のため – Span<T>は連続したメモリ領域のビューで、配列のように扱える(NativeArrayみたいな) – 今までポインタでしか扱えなかったstackallocを自然に扱えて便利 – しかしそれによってスタックにのみ確保したメモリ領域をヒープに移されると危険 – フィールドに置けない(ref structのfieldの場合のみ可)、ボクシングできない、インターフ ェイスを実装できない、ジェネリクスの型引数にできない、などの制約がある — ビュー的なものや一時的にしか使わない状態を持つものには適用しやすい – 制約が多いので無理に使おうとするとハマりますが…… Span<int> temp = stackalloc int[12];
  • 42. internal ref struct TempList<T> { int index; T[] array; public ReadOnlySpan<T> Span => new ReadOnlySpan<T>(array, 0, index); public TempList(int initialCapacity) { this.array = ArrayPool<T>.Shared.Rent(initialCapacity); this.index = 0; } public void Add(T value) { if (array.Length <= index) { var newArray = ArrayPool<T>.Shared.Rent(index * 2); Array.Copy(array, newArray, index); ArrayPool<T>.Shared.Return(array, true); array = newArray; } array[index++] = value; } public void Dispose() { ArrayPool<T>.Shared.Return(array, true); // clear for de-reference all. } } ArrayPool(System.Buffers, Unityで は似たようなものを自作すれば……) から確保済み配列を取得し使う Disposeで返却 一時的にしか使わない配 列を都度確保せずプール から取得するための構造 (TempList<T>) プールを扱っているので、 寿命は明確に短くあって ほしいのでref struct
  • 43. public void DoNanika(IEnumerable<int> idList) { var resources = idList.Select(x => Load(x)); // LINQの遅延実行により二回のLoadが走ってしまう // それを避けるために .ToList() するとそれはそれでListの無駄を感じる foreach (var item in resources) { /* nanika suru 1 */ } foreach (var item in resources) { /* nanika suru 2 */ } } public void DoNanika(IEnumerable<int> idList) { using var resources = idList.Select(x => Load(x)).ToTempList(); foreach (var item in resources) { /* nanika suru 1 */ } foreach (var item in resources) { /* nanika suru 2 */ } } usingだけで末尾で Disposeが便利 (C# 8.0から! Unityではまだ!) ここの中だけで使う一時配列はPoolから 取ってるのでアロケートなしで済んだ
  • 44. Avoid the copy, Everywhere 44 — 全て ref で引き回せばいい、とはいうものの現実的ではない — あまりにも最悪な書き心地になる! — あるいは全てunsafeでポインタで取り回すという手も…… — Unity.Entitiesのソースコードはかなりそれに近い [StructLayout(LayoutKind.Sequential)] internal unsafe struct Archetype { public ArchetypeChunkData Chunks; public UnsafeChunkPtrList ChunksWithEmptySlots; public ChunkListMap FreeChunksBySharedComponents; public int EntityCount; public int ChunkCapacity; public int BytesPerInstance; public ComponentTypeInArchetype* Types; public int TypesCount; public int NonZeroSizedTypesCount; public int* Offsets; public int* SizeOfs; public int* BufferCapacities; public int* TypeMemoryOrder; public int* ManagedArrayOffset; public int NumManagedArrays; // ... まだまだいっぱい void AddArchetypeIfMatching( Archetype* archetype, EntityQueryData* query) (ECSより引用)とにかく巨大なStruct 全部ポインタで引き回すからOK(?) (ECSはネイティブメモリを使ったり色々 と固有の事情があるので一般論は適用で きない)
  • 45. static void Normal(Vector3 v3) { } static void In(in Vector3 v3) { } static void Ref(ref Vector3 v3) { } Avoid the copy, Everywhere 45 そこで登場するのが新しいキーワード “in” (このコード例自体には何の意味もないです)
  • 46. static void Normal(Vector3 v3) { } static void In(in Vector3 v3) { } static void Ref(ref Vector3 v3) { } Avoid the copy, Everywhere 46 Normal(v3); In(v3); Ref(ref v3); 呼び側のコード
  • 47. static void Normal(Vector3 v3) { } static void In(in Vector3 v3) { } static void Ref(ref Vector3 v3) { } Avoid the copy, Everywhere 47 ldloc.0 call Noraml ldloca.0 call In ldloca.0 call Ref Normal(v3); In(v3); Ref(ref v3); 呼び側のIL
  • 48. static void Normal(Vector3 v3) { } static void In(in Vector3 v3) { } static void Ref(ref Vector3 v3) { } Avoid the copy, Everywhere 48 ldloc.0 call Noraml ldloca.0 call In ldloca.0 call Ref 呼び方は普通と一緒なのに refと同じく参照渡しされる! Normal(v3); In(v3); Ref(ref v3); じゃあ全部 in でいいね! (にはならない)
  • 49. static void Normal(Vector3 v3) { _ = v3.magnitude; } static void In(in Vector3 v3) { _ = v3.magnitude; } static void Ref(ref Vector3 v3) { _ = v3.magnitude; } Avoid the copy, Everywhere 49 ldarga.0 call get_magnitude ldarg.0 ldobj stloc.0 ldloca.0 call get_magnitude ldarg.0 call get_magnitude 呼ばれ側のIL
  • 50. static void Normal(Vector3 v3) { _ = v3.magnitude; } static void In(in Vector3 v3) { _ = v3.magnitude; } static void Ref(ref Vector3 v3) { _ = v3.magnitude; } Avoid the copy, Everywhere 50 ldarga.0 call get_magnitude ldarg.0 ldobj stloc.0 ldloca.0 call get_magnitude ldarg.0 call get_magnitude 呼ばれ側のIL コピーされてる(防御的コピー) var v3_2 = v3; _ = v3_2.magnitude;
  • 51. static void Normal(Vector3 v3) { _ = v3.magnitude; _ = v3.magnitude; } static void In(in Vector3 v3) { _ = v3.magnitude; _ = v3.magnitude; } static void Ref(ref Vector3 v3) { _ = v3.magnitude; _ = v3.magnitude; } Avoid the copy, Everywhere 51 ldarga.0 call get_magnitude ldarg.0 ldobj stloc.0 ldloca.0 call get_magnitude ldarg.0 call get_magnitude 呼ばれ側のIL 二回呼べばコピーもそのまま二つ ldarga.0 call get_magnitude ldarg.0 ldobj stloc.0 ldloca.0 call get_magnitude ldarg.0 call get_magnitude
  • 52. Best practice to use `in` 52 — in はコンパイルすると([In][IsReadOnly]ref T t) になる — 読み取り専用のため、フィールドへの代入はできない — v3.x = 10.0f; // compile error — メソッド呼び出しは可能だが、中身が変わらない保証がないため防御的コピーが走る — そのため、うかつに多用すると防御的コピーにより、むしろ性能低下もありうる — もしそれがMutable Structだともはやわけわからないことに — (Vector3.magnitudeはプロパティなのでメソッド扱い) — 防御的コピーが走らない条件は – プロパティ/メソッドを呼ばないこと – あるいは readonly struct であること – readonly structは書き換わらないことが保証されるため防御的コピーが走らない
  • 53. Best practice to use `in` 53 — in はコンパイルすると([In][IsReadOnly]ref T t) になる — 読み取り専用のため、フィールドへの代入はできない — v3.x = 10.0f; // compile error — メソッド呼び出しは可能だが、中身が変わらない保証がないため防御的コピーが走る — そのため、うかつに多用すると防御的コピーにより、むしろ性能低下もありうる — もしそれがMutable Structだともはやわけわからないことに — 防御的コピーが走らない条件は – プロパティ/メソッドを呼ばないこと – あるいは readonly struct であること – readonly structは書き換わらないことが保証されるため防御的コピーが走らない public readonly struct MyVector3 { public readonly float x; public readonly float y; public readonly float z; public MyVector3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } readonly structの条件は全てのフィ ールドがreadonlyであること ref readonly structもOK 原則inの引数はreadonly struct 中でフィールドしか絶対触らないと力 強く言えるならナシではない よくわからないなら基本使わない
  • 54. readonly field(struct)の罠 54 — readonlyなfieldのstructにmutableな操作を行っても変更されない — よって、ミュータブルな操作を行うものはreadonly fieldにすべきではない — 原則的には「可能なものは」と言いたいところだけど Unityの場合、可能なものが多いので…… public class NantokaBehaviour : MonoBehaviour { public readonly Vector3 NanikanoVector3; public void Incr() { NanikanoVector3.Set( NanikanoVector3.x + 1f, NanikanoVector3.y + 1f, NanikanoVector3.z + 1f); } } 何回Incr呼んでも0, 0, 0のまま
  • 56. 改めてStructとは 56 — メモリを単純にマッピングした構造 – そのことだけ意識すれば、あとはやりたい放題できる [StructLayout(LayoutKind.Sequential, Size = 16)] public struct Empty16 { } public struct LongLong { public long X; public long Y; } どちらも連続して16バイトの 領域を確保しているだけ、とい う意味(に読める) インデックス0とインデックス 8にアクセスしやすくしている だけ(という風に読める)
  • 57. 57 // 先頭6バイトがTimestamp, 後ろ10バイトがランダムというUlidという仕様(ソート可能なGuidの代替) [StructLayout(LayoutKind.Explicit, Size = 16)] public struct Ulid { // TimestampとRandomnessのはじまりの部分だけ持っておく(最悪なくても別にいい) [FieldOffset(0)] byte timestamp0; [FieldOffset(6)] byte randomness0; public static Ulid NewUlid() { var memory = default(Ulid); // 16バイト確保 var timestampMilliseconds = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); ref var fisrtByte = ref Unsafe.As<long, byte>(ref timestampMilliseconds); Unsafe.Add(ref memory.timestamp0, 0) = Unsafe.Add(ref fisrtByte, 5); Unsafe.Add(ref memory.timestamp0, 1) = Unsafe.Add(ref fisrtByte, 4); Unsafe.Add(ref memory.timestamp0, 2) = Unsafe.Add(ref fisrtByte, 3); Unsafe.Add(ref memory.timestamp0, 3) = Unsafe.Add(ref fisrtByte, 2); Unsafe.Add(ref memory.timestamp0, 4) = Unsafe.Add(ref fisrtByte, 1); Unsafe.Add(ref memory.timestamp0, 5) = Unsafe.Add(ref fisrtByte, 0); Unsafe.WriteUnaligned(ref memory.randomness0, xorshift.NextULong()); Unsafe.WriteUnaligned(ref Unsafe.Add(ref memory.randomness0, 2), xorshift.NextULong()); return memory; } Ulid: [Timestamp(6), Randomness(10)] の実装 (System.Runtime .CompilerService s.Unsafeを利用、 Unityでも動きま すが別途参照は必 要、ポインタでや ってもいいです)
  • 58. 58 // 先頭6バイトがTimestamp, 後ろ10バイトがランダムというUlidという仕様(ソート可能なGuidの代替) [StructLayout(LayoutKind.Explicit, Size = 16)] public struct Ulid { // TimestampとRandomnessのはじまりの部分だけ持っておく(最悪なくても別にいい) [FieldOffset(0)] byte timestamp0; [FieldOffset(6)] byte randomness0; public static Ulid NewUlid() { var memory = default(Ulid); // 16バイト確保 var timestampMilliseconds = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); ref var fisrtByte = ref Unsafe.As<long, byte>(ref timestampMilliseconds); Unsafe.Add(ref memory.timestamp0, 0) = Unsafe.Add(ref fisrtByte, 5); Unsafe.Add(ref memory.timestamp0, 1) = Unsafe.Add(ref fisrtByte, 4); Unsafe.Add(ref memory.timestamp0, 2) = Unsafe.Add(ref fisrtByte, 3); Unsafe.Add(ref memory.timestamp0, 3) = Unsafe.Add(ref fisrtByte, 2); Unsafe.Add(ref memory.timestamp0, 4) = Unsafe.Add(ref fisrtByte, 1); Unsafe.Add(ref memory.timestamp0, 5) = Unsafe.Add(ref fisrtByte, 0); Unsafe.WriteUnaligned(ref memory.randomness0, xorshift.NextULong()); Unsafe.WriteUnaligned(ref Unsafe.Add(ref memory.randomness0, 2), xorshift.NextULong()); return memory; } // メモリ領域をコピーすればおk。 // 文字列表現としてBase32エンコード(ToString)も同様に自分のメモリ粋から算出 public bool TryWriteBytes(Span<byte> destination) { if (destination.Length < 16) { return false; } Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), this) return true; } 出力先としてbyte[]とstringがあればそれでいい それらはほとんどメモリコピーで実現する
  • 59. Union 59 [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct GuidBits { [FieldOffset(0)] public readonly Guid Value; [FieldOffset(0)] public readonly byte Byte0; [FieldOffset(1)] public readonly byte Byte1; [FieldOffset(2)] public readonly byte Byte2; [FieldOffset(3)] public readonly byte Byte3; /* 中略(Byte4~Byte11) */ [FieldOffset(12)] public readonly byte Byte12; [FieldOffset(13)] public readonly byte Byte13; [FieldOffset(14)] public readonly byte Byte14; [FieldOffset(15)] public readonly byte Byte15; Guidとbyte0~16の重ね合わせ (同一FieldOffset) 通常Stringかbyte[]からしか生成でき ないGuidを、byte0~16を埋めるだけ で自由に生成する(本来弄れないGuid としてのメモリ領域を重ね合わせて 安全に(not unsafe)弄る) MessagePack for C#でUtf8 Bytesのス ライスから文字列のアロケーションを避 けて直接Guidに変換するのに利用
  • 60. 改めてStructが要素の配列とは 60 — メモリにStructが単純に並んでいる X Y Z X Y Z X Y Z X Y ZVector3[] メモリをまるごとコピーするだけで 最速のシリアライズだよね説 (エンディアンは揃える) 実際MagicOnionで有効にすることが可 能(サーバーもC#なので直接メモリをぶ ん投げて受け取るのが簡単) ただしStructの中には参照型(String含 む)は含めないこと。ポイン タをコピー しても意味がない
  • 61. 61 public class UnsafeDirectBlitArrayFormatter<T> : IMessagePackFormatter<T[]> where T : struct { public unsafe int Serialize(ref byte[] bytes, int offset, T[] value) { var startOffset = offset; var byteLen = value.Length * UnsafeUtility.SizeOf<T>(); /* 中略(MsgPackでのExtヘッダー書き込み) */ ulong handle2; var srcPointer = UnsafeUtility .PinGCArrayAndGetDataAddress(value, out handle2); try { fixed (void* dstPointer = &bytes[offset]) { UnsafeUtility.MemCpy(dstPointer, srcPointer, byteLen); } } finally { UnsafeUtility.ReleaseGCObject(handle2); } // ... } T[]をbytesにシリアライズ memcpyするだけ
  • 63. Span vs NativeArray 63 — Span<T> — System.Memory — .NET Standard 2.1では標準(現在は外部ライブラリが必要) — C# 7.2と統合されている — あらゆる連続したメモリ領域のビュー — 配列、stackalloc、ネイティブメモリ(ポインタ)、文字列(String) — NativeArray — Unity固有, 特にDOTSのキーパーツ — UnsafeUtility.Malloc で獲得するUnmanaged Memoryのビュー
  • 64. フレームワーク対応がないと意味がない 64 — Span<T> — 今までのAPIがT[]しか受け入れなかったりすると、結局T[]への変換が必要になる — 無駄アロケート — .NET Core 2.1で対応充実させ中 — 例えばConvert.ToBase64Stringがbyte[]のほかReadOnlySpan<byte>を受けとる — つまりUnityではAPI側の対応がほとんどないのでSpanだけ入れても意味は限定的 — NativeArray — DOTS周辺で使えるけれど、やはり一部のAPIは対応が必要 — 例えばMesh.SetVertexBufferDataとしてList<T>やT[]以外にNativeArray<T>を受け 取るようになったのは 2019.3から — というように、スムーズに全体的に統合されていくのはもうちょっとかな?
  • 67. Structを恐れない 67 — Structの使いこなし自体は、もはや必須 – Classを優先する牧歌的時代は終わった – 確かに罠は(いっぱい)あるが、難しい話ではない – 覚えるパターンが(ちょっと)多いだけで — そしてこの流れは戻らない – 言語強化から、フレームワークの抜本的変更まで、時代は既に来てる – ……とはいえ、じゃあいきなりめっちゃ使うかと言うと、それはまた別の話 – 使うべき時に使い、読めるようにするという、当たり前を大事にしよう