ݺߣ

ݺߣShare a Scribd company logo
Поговорим о различных версиях .NET
Андрей Акиньшин, Энтерра
.NEXT 2014 Moscow
1/32
О чём будем разговаривать?
О разных версиях MS.NET и Mono, а именно:
1 Compilers
2 BCL
3 GC
4 JIT
5 CLR Internal
2/32
Поговорим о компиляторах
3/32 Compilers
Свёртка констант
Console.WriteLine(checked(int.MinValue * -1));
Console.WriteLine(checked(int.MinValue / -1));
Console.WriteLine(unchecked(int.MinValue * -1));
Console.WriteLine(unchecked(int.MinValue / -1));
4/32 Compilers
Свёртка констант
Console.WriteLine(checked(int.MinValue * -1));
Console.WriteLine(checked(int.MinValue / -1));
Console.WriteLine(unchecked(int.MinValue * -1));
Console.WriteLine(unchecked(int.MinValue / -1));
MS.NET Mono
checked
* Error CS0220 Error CS0220
/ Error CS0220 Error CS0220
unchecked
* -2147483648 -2147483648
/ -2147483648 Error CS0220
4/32 Compilers
Кодировки
[Description(Value)]
public class Program
{
public const string Value = "Xud800Y";
public static void Main()
{
var description = GetDescription(typeof(Program));
Dump("Attribute", description.Description);
Dump("UTF8->String", Encoding.UTF8.GetString(
new byte[] { 0x58, 0xED, 0xA0, 0x80, 0x59 }));
Dump("String->UTF-8", Encoding.UTF8.GetBytes(Value));
}
}
5/32 Compilers
Кодировки
[Description(Value)]
public class Program
{
public const string Value = "Xud800Y";
public static void Main()
{
var description = GetDescription(typeof(Program));
Dump("Attribute", description.Description);
Dump("UTF8->String", Encoding.UTF8.GetString(
new byte[] { 0x58, 0xED, 0xA0, 0x80, 0x59 }));
Dump("String->UTF-8", Encoding.UTF8.GetBytes(Value));
}
}
MS.NET Mono
IL 58 ed a0 80 59 58 59 bf bd 00
Attribute 0058 0059 fffd fffd 0000 null
UTF8->String 0058 fffd fffd 0059 0058 fffd fffd fffd 0059
String->UTF-8 58 ef bf bd 59 58 59 bf bd 00
5/32 Compilers
Замыкания
var numbers = new int[] { 1, 2, 3 };
var actions = new List<Action>();
foreach (var number in numbers)
actions.Add(() => Console.WriteLine(number));
foreach (var action in actions)
action();
6/32 Compilers
Замыкания
var numbers = new int[] { 1, 2, 3 };
var actions = new List<Action>();
foreach (var number in numbers)
actions.Add(() => Console.WriteLine(number));
foreach (var action in actions)
action();
Compiler Command line Output
MS.NET (C#3, 3.5.30729.7903) v3.5csc.exe 3 3 3
MS.NET (C#4, 4.0.30319.1) v4.0.30319csc.exe 3 3 3
MS.NET (C#5, 4.0.30319.33440) v4.0.30319csc.exe 1 2 3
MS.NET (C#5, 4.0.30319.33440) v4.0.30319csc.exe /langversion:4 1 2 3
Mono 2.4.4 gmcs 3 3 3
Mono 3.10 mcs 1 2 3
Mono 3.10 mcs -langversion:4 1 2 3
6/32 Compilers
Поговорим о базовых классах
7/32 BCL
ThreadPool.GetMaxThreads
int workerThreads, completionPortThreads;
ThreadPool.GetMaxThreads(
out workerThreads, out completionPortThreads);
8/32 BCL
ThreadPool.GetMaxThreads
int workerThreads, completionPortThreads;
ThreadPool.GetMaxThreads(
out workerThreads, out completionPortThreads);
Зависит от: runtime, hardware, OS.
Возможные значения:
workerThreads
MS.NET 2.0 25
MS.NET 3.5 250
MS.NET 4.0 x86 1023
MS.NET 4.0 x64 32768
Mono 2.4.4, Ubuntu 14.04 Server 35
Mono 3.10, Ubuntu 14.04 Server 100
Mono 3.10, Kubuntu 14.04 400
Mono 3.3, Windows 8.1 800
8/32 BCL
ValueType.GetHashCode
var a1 = new KeyValuePair<int, int>(1, 2);
var a2 = new KeyValuePair<int, int>(1, 3);
Console.WriteLine(
a1.GetHashCode() != a2.GetHashCode());
var b1 = new KeyValuePair<int, string>(1, "x");
var b2 = new KeyValuePair<int, string>(1, "y");
Console.WriteLine(
b1.GetHashCode() != b2.GetHashCode());
9/32 BCL
ValueType.GetHashCode
var a1 = new KeyValuePair<int, int>(1, 2);
var a2 = new KeyValuePair<int, int>(1, 3);
Console.WriteLine(
a1.GetHashCode() != a2.GetHashCode());
var b1 = new KeyValuePair<int, string>(1, "x");
var b2 = new KeyValuePair<int, string>(1, "y");
Console.WriteLine(
b1.GetHashCode() != b2.GetHashCode());
<int, int> <int, string>
MS.NET True False
Mono True True
9/32 BCL
Uri.AbsoluteUri
var uri = new Uri("http://x/%2F/y.?%3D%3F");
Console.WriteLine("AbsoluteUri: " +
uri.AbsoluteUri);
Console.WriteLine("ToString(): " +
uri.ToString());
10/32 BCL
Uri.AbsoluteUri
var uri = new Uri("http://x/%2F/y.?%3D%3F");
Console.WriteLine("AbsoluteUri: " +
uri.AbsoluteUri);
Console.WriteLine("ToString(): " +
uri.ToString());
AbsoluteUri ToString()
. %2F %3D %3F . %2F %3D %3F
MS.NET 4.0 ∅ / %3D %3F ∅ / = ?
MS.NET 4.0 Fix ∅ %2F %3D %3F ∅ / = ?
MS.NET 4.5 . %2F %3D %3F . %2F %3D %3F
Mono 3.2.8 . / %3D %3F . / = %3F
Mono 3.10 . %2F %3D %3F . %2F %3D %3F
10/32 BCL
TypeBuilder.CreateType
private interface IFoo {}
void Main()
{
var typeBuilder = moduleBuilder.DefineType(
"Foo", TypeAttributes.Public,
typeof(object), new[] { typeof(IFoo) });
typeBuilder.CreateType();
}
11/32 BCL
TypeBuilder.CreateType
private interface IFoo {}
void Main()
{
var typeBuilder = moduleBuilder.DefineType(
"Foo", TypeAttributes.Public,
typeof(object), new[] { typeof(IFoo) });
typeBuilder.CreateType();
}
Output
MS.NET TypeLoadException
Mono 3.8 OK
Mono Bug 22059 (August 13, 2014)
Fixed in 68e5cc3 (August 18, 2014)
11/32 BCL
List.ForEach
var list = new List<int> { 1, 2 };
list.ForEach(i =>
{
if (i == 1)
list.Add(3);
Console.WriteLine(i);
});
12/32 BCL
List.ForEach
var list = new List<int> { 1, 2 };
list.ForEach(i =>
{
if (i == 1)
list.Add(3);
Console.WriteLine(i);
});
Output
MS.NET 4.0 1 2 3
MS.NET 4.5 1 InvalidOperationException
Mono 3.10 1 2 3
Mono Bug 24775 (November 24, 2014)
Fixed in 5517c56 (November 25, 2014)
12/32 BCL
Поговорим о сборщике мусора
13/32 GC
Сколько весит объект?
const int ObjectCount = 10000000;
var array = new object[ObjectCount];
var before = GC.GetTotalMemory(true);
for (int i = 0; i < ObjectCount; i++)
array[i] = new object();
var after = GC.GetTotalMemory(true);
var objectSize = (after - before) * 1.0 /
ObjectCount;
GC.KeepAlive(array);
14/32 GC
Сколько весит объект?
const int ObjectCount = 10000000;
var array = new object[ObjectCount];
var before = GC.GetTotalMemory(true);
for (int i = 0; i < ObjectCount; i++)
array[i] = new object();
var after = GC.GetTotalMemory(true);
var objectSize = (after - before) * 1.0 /
ObjectCount;
GC.KeepAlive(array);
MS.NET x86 MS.NET x64
object 12 24
14/32 GC
Размер объекта в Mono-x64
15/32 GC
Размер объекта в Mono-x64
GC ObjectCount Size
Boehm 1 0
15/32 GC
Размер объекта в Mono-x64
GC ObjectCount Size
Boehm 1 0
Boehm 10 0
15/32 GC
Размер объекта в Mono-x64
GC ObjectCount Size
Boehm 1 0
Boehm 10 0
Boehm 100 0
15/32 GC
Размер объекта в Mono-x64
GC ObjectCount Size
Boehm 1 0
Boehm 10 0
Boehm 100 0
Boehm 500 8.192
15/32 GC
Размер объекта в Mono-x64
GC ObjectCount Size
Boehm 1 0
Boehm 10 0
Boehm 100 0
Boehm 500 8.192
Boehm 600 13.653
15/32 GC
Размер объекта в Mono-x64
GC ObjectCount Size
Boehm 1 0
Boehm 10 0
Boehm 100 0
Boehm 500 8.192
Boehm 600 13.653
Boehm 1000 12.288
15/32 GC
Размер объекта в Mono-x64
GC ObjectCount Size
Boehm 1 0
Boehm 10 0
Boehm 100 0
Boehm 500 8.192
Boehm 600 13.653
Boehm 1000 12.288
Boehm 10000000 16
15/32 GC
Размер объекта в Mono-x64
GC ObjectCount Size
Boehm 1 0
Boehm 10 0
Boehm 100 0
Boehm 500 8.192
Boehm 600 13.653
Boehm 1000 12.288
Boehm 10000000 16
Sgen 1 16
15/32 GC
Размер long-массива в Mono-x64-Sgen
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
5 88
6 88
7 88
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
5 88
6 88
7 88
8 120
9 120
n Size
11 120
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
5 88
6 88
7 88
8 120
9 120
n Size
11 120
12 176
18 176
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
5 88
6 88
7 88
8 120
9 120
n Size
11 120
12 176
18 176
19 248
27 248
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
5 88
6 88
7 88
8 120
9 120
n Size
11 120
12 176
18 176
19 248
27 248
28 352
40 352
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
5 88
6 88
7 88
8 120
9 120
n Size
11 120
12 176
18 176
19 248
27 248
28 352
40 352
41 504
59 504
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
5 88
6 88
7 88
8 120
9 120
n Size
11 120
12 176
18 176
19 248
27 248
28 352
40 352
41 504
59 504
60 704
n Size
84 704
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
5 88
6 88
7 88
8 120
9 120
n Size
11 120
12 176
18 176
19 248
27 248
28 352
40 352
41 504
59 504
60 704
n Size
84 704
85 1016
123 1016
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
5 88
6 88
7 88
8 120
9 120
n Size
11 120
12 176
18 176
19 248
27 248
28 352
40 352
41 504
59 504
60 704
n Size
84 704
85 1016
123 1016
124 1360
166 1360
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
5 88
6 88
7 88
8 120
9 120
n Size
11 120
12 176
18 176
19 248
27 248
28 352
40 352
41 504
59 504
60 704
n Size
84 704
85 1016
123 1016
124 1360
166 1360
167 2040
251 2040
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
5 88
6 88
7 88
8 120
9 120
n Size
11 120
12 176
18 176
19 248
27 248
28 352
40 352
41 504
59 504
60 704
n Size
84 704
85 1016
123 1016
124 1360
166 1360
167 2040
251 2040
252 2728
337 2728
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
5 88
6 88
7 88
8 120
9 120
n Size
11 120
12 176
18 176
19 248
27 248
28 352
40 352
41 504
59 504
60 704
n Size
84 704
85 1016
123 1016
124 1360
166 1360
167 2040
251 2040
252 2728
337 2728
338 4088
n Size
507 4088
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
5 88
6 88
7 88
8 120
9 120
n Size
11 120
12 176
18 176
19 248
27 248
28 352
40 352
41 504
59 504
60 704
n Size
84 704
85 1016
123 1016
124 1360
166 1360
167 2040
251 2040
252 2728
337 2728
338 4088
n Size
507 4088
509 5456
678 5456
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
5 88
6 88
7 88
8 120
9 120
n Size
11 120
12 176
18 176
19 248
27 248
28 352
40 352
41 504
59 504
60 704
n Size
84 704
85 1016
123 1016
124 1360
166 1360
167 2040
251 2040
252 2728
337 2728
338 4088
n Size
507 4088
509 5456
678 5456
679 8000
996 8000
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
5 88
6 88
7 88
8 120
9 120
n Size
11 120
12 176
18 176
19 248
27 248
28 352
40 352
41 504
59 504
60 704
n Size
84 704
85 1016
123 1016
124 1360
166 1360
167 2040
251 2040
252 2728
337 2728
338 4088
n Size
507 4088
509 5456
678 5456
679 8000
996 8000
997 8008
16/32 GC
Размер long-массива в Mono-x64-Sgen
n Size
0 40
1 40
2 56
3 56
4 88
5 88
6 88
7 88
8 120
9 120
n Size
11 120
12 176
18 176
19 248
27 248
28 352
40 352
41 504
59 504
60 704
n Size
84 704
85 1016
123 1016
124 1360
166 1360
167 2040
251 2040
252 2728
337 2728
338 4088
n Size
507 4088
509 5456
678 5456
679 8000
996 8000
997 8008
998 8016
999 8024
1000 8032
1001 8040
16/32 GC
Исходники SGen
// mono/mono/metadata/sgen-internal.c
/* keep each size a multiple of ALLOC_ALIGN */
#if SIZEOF_VOID_P == 4
static const int allocator_sizes [] = {
8, 16, 24, 32, 40, 48, 64, 80,
96, 128, 160, 192, 224, 248, 296, 320,
384, 448, 504, 528, 584, 680, 816, 1088,
1360, 2044, 2336, 2728, 3272, 4092, 5456, 8188 };
#else
static const int allocator_sizes [] = {
8, 16, 24, 32, 40, 48, 64, 80,
96, 128, 160, 192, 224, 248, 320, 328,
384, 448, 528, 584, 680, 816, 1016, 1088,
1360, 2040, 2336, 2728, 3272, 4088, 5456, 8184 };
#endif
// mono/mono/metadata/sgen-conf.h
#define SGEN_MAX_SMALL_OBJ_SIZE 8000
17/32 GC
Поговорим о JIT-компиляторе
18/32 JIT
Как дела с SIMD?
• P/Invoke
• Mono.SIMD
• Microsoft.Bcl.Simd
19/32 JIT
Размотка циклов
public int Sum997()
{
int sum = 0;
for (int i = 0; i < 997; i++)
sum += a[i];
return sum;
}
public int Sum1000()
{
int sum = 0;
for (int i = 0; i < 1000; i++)
sum += a[i];
return sum;
}
20/32 JIT
Размотка циклов
public int Sum997()
{
int sum = 0;
for (int i = 0; i < 997; i++)
sum += a[i];
return sum;
}
public int Sum1000()
{
int sum = 0;
for (int i = 0; i < 1000; i++)
sum += a[i];
return sum;
}
// Аналог Sum1000()
// с размоткой цикла (loop unrolling)
public int Sum1000Unrolled()
{
int sum = 0;
for (int i = 0; i < 1000; i += 4)
{
sum += a[i];
sum += a[i+1];
sum += a[i+2];
sum += a[i+3];
}
return sum;
}
20/32 JIT
Размотка циклов
NonStatic / Static
21/32 JIT
Размотка циклов
Различия крупным планом:
; NonStaticRun-x64.asm
; eax = nonStaticField[i]
mov eax, dword ptr [r9+r10+10h]
; i += 4
add r10, 10h
; StaticRun-x64.asm
; eax = staticField[i]
mov eax, dword ptr [r9+r8*4+10h]
; i += 4
add r8, 4
22/32 JIT
Кто быстрее?
// Целевой интерфейс
interface IFoo
{
int Inc(int x);
}
void Run(IFoo foo)
{
for (int i = 0; i < 1000000; i++)
foo.Inc(0);
}
// Запускаем Run()
// с двумя имлементациями IFoo
Run(new FastFoo());
Run(new SlowFoo());
class FastFoo : IFoo
{
public int Inc(int x)
{
return x + 1;
}
}
class SlowFoo : IFoo
{
public int Inc(int x)
{
return 1 + x;
}
}
23/32 JIT
Кто быстрее?
// Целевой интерфейс
interface IFoo
{
int Inc(int x);
}
void Run(IFoo foo)
{
for (int i = 0; i < 1000000; i++)
foo.Inc(0);
}
// Запускаем Run()
// с двумя имлементациями IFoo
Run(new FastFoo());
Run(new SlowFoo());
class FastFoo : IFoo
{
public int Inc(int x)
{
return x + 1;
}
}
class SlowFoo : IFoo
{
public int Inc(int x)
{
return 1 + x;
}
}
MS.NET: Time(Fast) Time(Slow)
Mono: Time(Fast) ≈ Time(Slow)
23/32 JIT
Поговорим о внутренностях рантайма
Осторожно, сложный пример!
24/32 CLR Internal
Внутреннее устройство массивов
var a = new object[1][]; // Address: 0x012410
a[0] = new object[1]; // Address: 0x012424
// Memory dump (a) //
// 0x01240C 000000 SyncBlockIndex (a) //
// 0x012410 1731d4 object[][] MethodTable // a
// 0x012414 000001 a.Length //
// 0x012418 854d7a object[] TypeHandle // !!!
// 0x01241C 012424 Address of a[0] //
// //
// Memory dump (a[0]) //
// 0x012420 000000 SyncBlockIndex (a[0]) //
// 0x012424 bfab98 object[] MethodTable // a[0]
// 0x012428 000001 a[0].Length //
// 0x01242C c4b060 Address of a[0][0] //
25/32 CLR Internal
Исходники MS CLI 2.0
// sscli20clrsrcvmtypehandle.h
// A TypeHandle is the FUNDAMENTAL concept of type identity in the CLR.
// That is two types are equal if and only if their type handles
// are equal. A TypeHandle, is a pointer sized struture that encodes
// everything you need to know to figure out what kind of type you are
// actually dealing with.
// At the present time a TypeHandle can point at two possible things
//
// 1) A MethodTable (Intrinsics, Classes, Value Types
// and their instantiations)
// 2) A TypeDesc (all other cases: arrays, byrefs, pointer types,
// function pointers, generic type variables)
//
// or with IL stubs, a third thing:
//
// 3) A MethodTable for a native value type.
26/32 CLR Internal
Исходники MS CLI 2.0
class ArrayBase : public Object
{
// ...
// What comes after this conceputally is:
// TypeHandle elementType;
// ...
// sscli20clrsrcvmobject.h
FORCEINLINE BOOL IsUnsharedMT() const {
LEAF_CONTRACT;
STATIC_CONTRACT_SO_TOLERANT;
return((m_asTAddr & 2) == 0);
}
FORCEINLINE BOOL IsTypeDesc() const {
WRAPPER_CONTRACT;
return(!IsUnsharedMT());
}
// MethodTable: 0, 1, 4, 5, 8, 9, C, D
// TypeDesc : 2, 3, 6, 7, A, B, E, F
27/32 CLR Internal
MethodTable или TypeDesc?
var types = new[] {
typeof(int), typeof(object), typeof(Stream),
typeof(int[]), typeof(int[][]), typeof(object[]) };
foreach (var type in types)
{
bool isTypeDesc = (((int)type.TypeHandle.Value)&2) > 0;
Console.WriteLine("{0}: {1}",
type.Name, isTypeDesc ? "TypeDesc" : "MethodTable");
}
28/32 CLR Internal
MethodTable или TypeDesc?
var types = new[] {
typeof(int), typeof(object), typeof(Stream),
typeof(int[]), typeof(int[][]), typeof(object[]) };
foreach (var type in types)
{
bool isTypeDesc = (((int)type.TypeHandle.Value)&2) > 0;
Console.WriteLine("{0}: {1}",
type.Name, isTypeDesc ? "TypeDesc" : "MethodTable");
}
MS.NET Mono
Int32 MethodTable MethodTable
Object MethodTable MethodTable
Stream MethodTable MethodTable
Int32[] TypeDesc MethodTable
Int32[][] TypeDesc MethodTable
Object[] TypeDesc MethodTable
28/32 CLR Internal
Чёрная магия
public class OneLong { public long X; }
public class TwoInt { public int Y1, Y2; }
public unsafe IntPtr GetAddress(object obj)
{
var typedReference = __makeref(obj);
return *(IntPtr*)(&typedReference);
}
public unsafe T Convert<T>(IntPtr address)
{
var fakeInstance = default(T);
var typedReference = __makeref(fakeInstance);
*(IntPtr*)(&typedReference) = address;
return __refvalue(typedReference, T);
}
public void Run()
{
var oneLong = new OneLong { X = 1 + (2L << 32) };
var twoInt = Convert<TwoInt>(GetAddress(oneLong));
Console.WriteLine(twoInt.Y1 + " " + twoInt.Y2); // 1 2
oneLong.X = 3 + (4L << 32);
Console.WriteLine(twoInt.Y1 + " " + twoInt.Y2); // 3 4
}
29/32 CLR Internal
Хорошие времена для .NET-чиков
30/32
Напутствие
Думайте про различия в рантаймах!
31/32
Вопросы?
Андрей Акиньшин, Энтерра
http://aakinshin.ru
andrey.akinshin@gmail.com
32/32

More Related Content

Поговорим о различных версиях .NET

  • 1. Поговорим о различных версиях .NET Андрей Акиньшин, Энтерра .NEXT 2014 Moscow 1/32
  • 2. О чём будем разговаривать? О разных версиях MS.NET и Mono, а именно: 1 Compilers 2 BCL 3 GC 4 JIT 5 CLR Internal 2/32
  • 4. Свёртка констант Console.WriteLine(checked(int.MinValue * -1)); Console.WriteLine(checked(int.MinValue / -1)); Console.WriteLine(unchecked(int.MinValue * -1)); Console.WriteLine(unchecked(int.MinValue / -1)); 4/32 Compilers
  • 5. Свёртка констант Console.WriteLine(checked(int.MinValue * -1)); Console.WriteLine(checked(int.MinValue / -1)); Console.WriteLine(unchecked(int.MinValue * -1)); Console.WriteLine(unchecked(int.MinValue / -1)); MS.NET Mono checked * Error CS0220 Error CS0220 / Error CS0220 Error CS0220 unchecked * -2147483648 -2147483648 / -2147483648 Error CS0220 4/32 Compilers
  • 6. Кодировки [Description(Value)] public class Program { public const string Value = "Xud800Y"; public static void Main() { var description = GetDescription(typeof(Program)); Dump("Attribute", description.Description); Dump("UTF8->String", Encoding.UTF8.GetString( new byte[] { 0x58, 0xED, 0xA0, 0x80, 0x59 })); Dump("String->UTF-8", Encoding.UTF8.GetBytes(Value)); } } 5/32 Compilers
  • 7. Кодировки [Description(Value)] public class Program { public const string Value = "Xud800Y"; public static void Main() { var description = GetDescription(typeof(Program)); Dump("Attribute", description.Description); Dump("UTF8->String", Encoding.UTF8.GetString( new byte[] { 0x58, 0xED, 0xA0, 0x80, 0x59 })); Dump("String->UTF-8", Encoding.UTF8.GetBytes(Value)); } } MS.NET Mono IL 58 ed a0 80 59 58 59 bf bd 00 Attribute 0058 0059 fffd fffd 0000 null UTF8->String 0058 fffd fffd 0059 0058 fffd fffd fffd 0059 String->UTF-8 58 ef bf bd 59 58 59 bf bd 00 5/32 Compilers
  • 8. Замыкания var numbers = new int[] { 1, 2, 3 }; var actions = new List<Action>(); foreach (var number in numbers) actions.Add(() => Console.WriteLine(number)); foreach (var action in actions) action(); 6/32 Compilers
  • 9. Замыкания var numbers = new int[] { 1, 2, 3 }; var actions = new List<Action>(); foreach (var number in numbers) actions.Add(() => Console.WriteLine(number)); foreach (var action in actions) action(); Compiler Command line Output MS.NET (C#3, 3.5.30729.7903) v3.5csc.exe 3 3 3 MS.NET (C#4, 4.0.30319.1) v4.0.30319csc.exe 3 3 3 MS.NET (C#5, 4.0.30319.33440) v4.0.30319csc.exe 1 2 3 MS.NET (C#5, 4.0.30319.33440) v4.0.30319csc.exe /langversion:4 1 2 3 Mono 2.4.4 gmcs 3 3 3 Mono 3.10 mcs 1 2 3 Mono 3.10 mcs -langversion:4 1 2 3 6/32 Compilers
  • 10. Поговорим о базовых классах 7/32 BCL
  • 12. ThreadPool.GetMaxThreads int workerThreads, completionPortThreads; ThreadPool.GetMaxThreads( out workerThreads, out completionPortThreads); Зависит от: runtime, hardware, OS. Возможные значения: workerThreads MS.NET 2.0 25 MS.NET 3.5 250 MS.NET 4.0 x86 1023 MS.NET 4.0 x64 32768 Mono 2.4.4, Ubuntu 14.04 Server 35 Mono 3.10, Ubuntu 14.04 Server 100 Mono 3.10, Kubuntu 14.04 400 Mono 3.3, Windows 8.1 800 8/32 BCL
  • 13. ValueType.GetHashCode var a1 = new KeyValuePair<int, int>(1, 2); var a2 = new KeyValuePair<int, int>(1, 3); Console.WriteLine( a1.GetHashCode() != a2.GetHashCode()); var b1 = new KeyValuePair<int, string>(1, "x"); var b2 = new KeyValuePair<int, string>(1, "y"); Console.WriteLine( b1.GetHashCode() != b2.GetHashCode()); 9/32 BCL
  • 14. ValueType.GetHashCode var a1 = new KeyValuePair<int, int>(1, 2); var a2 = new KeyValuePair<int, int>(1, 3); Console.WriteLine( a1.GetHashCode() != a2.GetHashCode()); var b1 = new KeyValuePair<int, string>(1, "x"); var b2 = new KeyValuePair<int, string>(1, "y"); Console.WriteLine( b1.GetHashCode() != b2.GetHashCode()); <int, int> <int, string> MS.NET True False Mono True True 9/32 BCL
  • 15. Uri.AbsoluteUri var uri = new Uri("http://x/%2F/y.?%3D%3F"); Console.WriteLine("AbsoluteUri: " + uri.AbsoluteUri); Console.WriteLine("ToString(): " + uri.ToString()); 10/32 BCL
  • 16. Uri.AbsoluteUri var uri = new Uri("http://x/%2F/y.?%3D%3F"); Console.WriteLine("AbsoluteUri: " + uri.AbsoluteUri); Console.WriteLine("ToString(): " + uri.ToString()); AbsoluteUri ToString() . %2F %3D %3F . %2F %3D %3F MS.NET 4.0 ∅ / %3D %3F ∅ / = ? MS.NET 4.0 Fix ∅ %2F %3D %3F ∅ / = ? MS.NET 4.5 . %2F %3D %3F . %2F %3D %3F Mono 3.2.8 . / %3D %3F . / = %3F Mono 3.10 . %2F %3D %3F . %2F %3D %3F 10/32 BCL
  • 17. TypeBuilder.CreateType private interface IFoo {} void Main() { var typeBuilder = moduleBuilder.DefineType( "Foo", TypeAttributes.Public, typeof(object), new[] { typeof(IFoo) }); typeBuilder.CreateType(); } 11/32 BCL
  • 18. TypeBuilder.CreateType private interface IFoo {} void Main() { var typeBuilder = moduleBuilder.DefineType( "Foo", TypeAttributes.Public, typeof(object), new[] { typeof(IFoo) }); typeBuilder.CreateType(); } Output MS.NET TypeLoadException Mono 3.8 OK Mono Bug 22059 (August 13, 2014) Fixed in 68e5cc3 (August 18, 2014) 11/32 BCL
  • 19. List.ForEach var list = new List<int> { 1, 2 }; list.ForEach(i => { if (i == 1) list.Add(3); Console.WriteLine(i); }); 12/32 BCL
  • 20. List.ForEach var list = new List<int> { 1, 2 }; list.ForEach(i => { if (i == 1) list.Add(3); Console.WriteLine(i); }); Output MS.NET 4.0 1 2 3 MS.NET 4.5 1 InvalidOperationException Mono 3.10 1 2 3 Mono Bug 24775 (November 24, 2014) Fixed in 5517c56 (November 25, 2014) 12/32 BCL
  • 21. Поговорим о сборщике мусора 13/32 GC
  • 22. Сколько весит объект? const int ObjectCount = 10000000; var array = new object[ObjectCount]; var before = GC.GetTotalMemory(true); for (int i = 0; i < ObjectCount; i++) array[i] = new object(); var after = GC.GetTotalMemory(true); var objectSize = (after - before) * 1.0 / ObjectCount; GC.KeepAlive(array); 14/32 GC
  • 23. Сколько весит объект? const int ObjectCount = 10000000; var array = new object[ObjectCount]; var before = GC.GetTotalMemory(true); for (int i = 0; i < ObjectCount; i++) array[i] = new object(); var after = GC.GetTotalMemory(true); var objectSize = (after - before) * 1.0 / ObjectCount; GC.KeepAlive(array); MS.NET x86 MS.NET x64 object 12 24 14/32 GC
  • 24. Размер объекта в Mono-x64 15/32 GC
  • 25. Размер объекта в Mono-x64 GC ObjectCount Size Boehm 1 0 15/32 GC
  • 26. Размер объекта в Mono-x64 GC ObjectCount Size Boehm 1 0 Boehm 10 0 15/32 GC
  • 27. Размер объекта в Mono-x64 GC ObjectCount Size Boehm 1 0 Boehm 10 0 Boehm 100 0 15/32 GC
  • 28. Размер объекта в Mono-x64 GC ObjectCount Size Boehm 1 0 Boehm 10 0 Boehm 100 0 Boehm 500 8.192 15/32 GC
  • 29. Размер объекта в Mono-x64 GC ObjectCount Size Boehm 1 0 Boehm 10 0 Boehm 100 0 Boehm 500 8.192 Boehm 600 13.653 15/32 GC
  • 30. Размер объекта в Mono-x64 GC ObjectCount Size Boehm 1 0 Boehm 10 0 Boehm 100 0 Boehm 500 8.192 Boehm 600 13.653 Boehm 1000 12.288 15/32 GC
  • 31. Размер объекта в Mono-x64 GC ObjectCount Size Boehm 1 0 Boehm 10 0 Boehm 100 0 Boehm 500 8.192 Boehm 600 13.653 Boehm 1000 12.288 Boehm 10000000 16 15/32 GC
  • 32. Размер объекта в Mono-x64 GC ObjectCount Size Boehm 1 0 Boehm 10 0 Boehm 100 0 Boehm 500 8.192 Boehm 600 13.653 Boehm 1000 12.288 Boehm 10000000 16 Sgen 1 16 15/32 GC
  • 33. Размер long-массива в Mono-x64-Sgen 16/32 GC
  • 34. Размер long-массива в Mono-x64-Sgen n Size 0 40 16/32 GC
  • 35. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 16/32 GC
  • 36. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 16/32 GC
  • 37. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 16/32 GC
  • 38. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 16/32 GC
  • 39. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 5 88 6 88 7 88 16/32 GC
  • 40. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 5 88 6 88 7 88 8 120 9 120 n Size 11 120 16/32 GC
  • 41. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 5 88 6 88 7 88 8 120 9 120 n Size 11 120 12 176 18 176 16/32 GC
  • 42. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 5 88 6 88 7 88 8 120 9 120 n Size 11 120 12 176 18 176 19 248 27 248 16/32 GC
  • 43. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 5 88 6 88 7 88 8 120 9 120 n Size 11 120 12 176 18 176 19 248 27 248 28 352 40 352 16/32 GC
  • 44. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 5 88 6 88 7 88 8 120 9 120 n Size 11 120 12 176 18 176 19 248 27 248 28 352 40 352 41 504 59 504 16/32 GC
  • 45. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 5 88 6 88 7 88 8 120 9 120 n Size 11 120 12 176 18 176 19 248 27 248 28 352 40 352 41 504 59 504 60 704 n Size 84 704 16/32 GC
  • 46. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 5 88 6 88 7 88 8 120 9 120 n Size 11 120 12 176 18 176 19 248 27 248 28 352 40 352 41 504 59 504 60 704 n Size 84 704 85 1016 123 1016 16/32 GC
  • 47. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 5 88 6 88 7 88 8 120 9 120 n Size 11 120 12 176 18 176 19 248 27 248 28 352 40 352 41 504 59 504 60 704 n Size 84 704 85 1016 123 1016 124 1360 166 1360 16/32 GC
  • 48. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 5 88 6 88 7 88 8 120 9 120 n Size 11 120 12 176 18 176 19 248 27 248 28 352 40 352 41 504 59 504 60 704 n Size 84 704 85 1016 123 1016 124 1360 166 1360 167 2040 251 2040 16/32 GC
  • 49. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 5 88 6 88 7 88 8 120 9 120 n Size 11 120 12 176 18 176 19 248 27 248 28 352 40 352 41 504 59 504 60 704 n Size 84 704 85 1016 123 1016 124 1360 166 1360 167 2040 251 2040 252 2728 337 2728 16/32 GC
  • 50. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 5 88 6 88 7 88 8 120 9 120 n Size 11 120 12 176 18 176 19 248 27 248 28 352 40 352 41 504 59 504 60 704 n Size 84 704 85 1016 123 1016 124 1360 166 1360 167 2040 251 2040 252 2728 337 2728 338 4088 n Size 507 4088 16/32 GC
  • 51. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 5 88 6 88 7 88 8 120 9 120 n Size 11 120 12 176 18 176 19 248 27 248 28 352 40 352 41 504 59 504 60 704 n Size 84 704 85 1016 123 1016 124 1360 166 1360 167 2040 251 2040 252 2728 337 2728 338 4088 n Size 507 4088 509 5456 678 5456 16/32 GC
  • 52. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 5 88 6 88 7 88 8 120 9 120 n Size 11 120 12 176 18 176 19 248 27 248 28 352 40 352 41 504 59 504 60 704 n Size 84 704 85 1016 123 1016 124 1360 166 1360 167 2040 251 2040 252 2728 337 2728 338 4088 n Size 507 4088 509 5456 678 5456 679 8000 996 8000 16/32 GC
  • 53. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 5 88 6 88 7 88 8 120 9 120 n Size 11 120 12 176 18 176 19 248 27 248 28 352 40 352 41 504 59 504 60 704 n Size 84 704 85 1016 123 1016 124 1360 166 1360 167 2040 251 2040 252 2728 337 2728 338 4088 n Size 507 4088 509 5456 678 5456 679 8000 996 8000 997 8008 16/32 GC
  • 54. Размер long-массива в Mono-x64-Sgen n Size 0 40 1 40 2 56 3 56 4 88 5 88 6 88 7 88 8 120 9 120 n Size 11 120 12 176 18 176 19 248 27 248 28 352 40 352 41 504 59 504 60 704 n Size 84 704 85 1016 123 1016 124 1360 166 1360 167 2040 251 2040 252 2728 337 2728 338 4088 n Size 507 4088 509 5456 678 5456 679 8000 996 8000 997 8008 998 8016 999 8024 1000 8032 1001 8040 16/32 GC
  • 55. Исходники SGen // mono/mono/metadata/sgen-internal.c /* keep each size a multiple of ALLOC_ALIGN */ #if SIZEOF_VOID_P == 4 static const int allocator_sizes [] = { 8, 16, 24, 32, 40, 48, 64, 80, 96, 128, 160, 192, 224, 248, 296, 320, 384, 448, 504, 528, 584, 680, 816, 1088, 1360, 2044, 2336, 2728, 3272, 4092, 5456, 8188 }; #else static const int allocator_sizes [] = { 8, 16, 24, 32, 40, 48, 64, 80, 96, 128, 160, 192, 224, 248, 320, 328, 384, 448, 528, 584, 680, 816, 1016, 1088, 1360, 2040, 2336, 2728, 3272, 4088, 5456, 8184 }; #endif // mono/mono/metadata/sgen-conf.h #define SGEN_MAX_SMALL_OBJ_SIZE 8000 17/32 GC
  • 57. Как дела с SIMD? • P/Invoke • Mono.SIMD • Microsoft.Bcl.Simd 19/32 JIT
  • 58. Размотка циклов public int Sum997() { int sum = 0; for (int i = 0; i < 997; i++) sum += a[i]; return sum; } public int Sum1000() { int sum = 0; for (int i = 0; i < 1000; i++) sum += a[i]; return sum; } 20/32 JIT
  • 59. Размотка циклов public int Sum997() { int sum = 0; for (int i = 0; i < 997; i++) sum += a[i]; return sum; } public int Sum1000() { int sum = 0; for (int i = 0; i < 1000; i++) sum += a[i]; return sum; } // Аналог Sum1000() // с размоткой цикла (loop unrolling) public int Sum1000Unrolled() { int sum = 0; for (int i = 0; i < 1000; i += 4) { sum += a[i]; sum += a[i+1]; sum += a[i+2]; sum += a[i+3]; } return sum; } 20/32 JIT
  • 61. Размотка циклов Различия крупным планом: ; NonStaticRun-x64.asm ; eax = nonStaticField[i] mov eax, dword ptr [r9+r10+10h] ; i += 4 add r10, 10h ; StaticRun-x64.asm ; eax = staticField[i] mov eax, dword ptr [r9+r8*4+10h] ; i += 4 add r8, 4 22/32 JIT
  • 62. Кто быстрее? // Целевой интерфейс interface IFoo { int Inc(int x); } void Run(IFoo foo) { for (int i = 0; i < 1000000; i++) foo.Inc(0); } // Запускаем Run() // с двумя имлементациями IFoo Run(new FastFoo()); Run(new SlowFoo()); class FastFoo : IFoo { public int Inc(int x) { return x + 1; } } class SlowFoo : IFoo { public int Inc(int x) { return 1 + x; } } 23/32 JIT
  • 63. Кто быстрее? // Целевой интерфейс interface IFoo { int Inc(int x); } void Run(IFoo foo) { for (int i = 0; i < 1000000; i++) foo.Inc(0); } // Запускаем Run() // с двумя имлементациями IFoo Run(new FastFoo()); Run(new SlowFoo()); class FastFoo : IFoo { public int Inc(int x) { return x + 1; } } class SlowFoo : IFoo { public int Inc(int x) { return 1 + x; } } MS.NET: Time(Fast) Time(Slow) Mono: Time(Fast) ≈ Time(Slow) 23/32 JIT
  • 64. Поговорим о внутренностях рантайма Осторожно, сложный пример! 24/32 CLR Internal
  • 65. Внутреннее устройство массивов var a = new object[1][]; // Address: 0x012410 a[0] = new object[1]; // Address: 0x012424 // Memory dump (a) // // 0x01240C 000000 SyncBlockIndex (a) // // 0x012410 1731d4 object[][] MethodTable // a // 0x012414 000001 a.Length // // 0x012418 854d7a object[] TypeHandle // !!! // 0x01241C 012424 Address of a[0] // // // // Memory dump (a[0]) // // 0x012420 000000 SyncBlockIndex (a[0]) // // 0x012424 bfab98 object[] MethodTable // a[0] // 0x012428 000001 a[0].Length // // 0x01242C c4b060 Address of a[0][0] // 25/32 CLR Internal
  • 66. Исходники MS CLI 2.0 // sscli20clrsrcvmtypehandle.h // A TypeHandle is the FUNDAMENTAL concept of type identity in the CLR. // That is two types are equal if and only if their type handles // are equal. A TypeHandle, is a pointer sized struture that encodes // everything you need to know to figure out what kind of type you are // actually dealing with. // At the present time a TypeHandle can point at two possible things // // 1) A MethodTable (Intrinsics, Classes, Value Types // and their instantiations) // 2) A TypeDesc (all other cases: arrays, byrefs, pointer types, // function pointers, generic type variables) // // or with IL stubs, a third thing: // // 3) A MethodTable for a native value type. 26/32 CLR Internal
  • 67. Исходники MS CLI 2.0 class ArrayBase : public Object { // ... // What comes after this conceputally is: // TypeHandle elementType; // ... // sscli20clrsrcvmobject.h FORCEINLINE BOOL IsUnsharedMT() const { LEAF_CONTRACT; STATIC_CONTRACT_SO_TOLERANT; return((m_asTAddr & 2) == 0); } FORCEINLINE BOOL IsTypeDesc() const { WRAPPER_CONTRACT; return(!IsUnsharedMT()); } // MethodTable: 0, 1, 4, 5, 8, 9, C, D // TypeDesc : 2, 3, 6, 7, A, B, E, F 27/32 CLR Internal
  • 68. MethodTable или TypeDesc? var types = new[] { typeof(int), typeof(object), typeof(Stream), typeof(int[]), typeof(int[][]), typeof(object[]) }; foreach (var type in types) { bool isTypeDesc = (((int)type.TypeHandle.Value)&2) > 0; Console.WriteLine("{0}: {1}", type.Name, isTypeDesc ? "TypeDesc" : "MethodTable"); } 28/32 CLR Internal
  • 69. MethodTable или TypeDesc? var types = new[] { typeof(int), typeof(object), typeof(Stream), typeof(int[]), typeof(int[][]), typeof(object[]) }; foreach (var type in types) { bool isTypeDesc = (((int)type.TypeHandle.Value)&2) > 0; Console.WriteLine("{0}: {1}", type.Name, isTypeDesc ? "TypeDesc" : "MethodTable"); } MS.NET Mono Int32 MethodTable MethodTable Object MethodTable MethodTable Stream MethodTable MethodTable Int32[] TypeDesc MethodTable Int32[][] TypeDesc MethodTable Object[] TypeDesc MethodTable 28/32 CLR Internal
  • 70. Чёрная магия public class OneLong { public long X; } public class TwoInt { public int Y1, Y2; } public unsafe IntPtr GetAddress(object obj) { var typedReference = __makeref(obj); return *(IntPtr*)(&typedReference); } public unsafe T Convert<T>(IntPtr address) { var fakeInstance = default(T); var typedReference = __makeref(fakeInstance); *(IntPtr*)(&typedReference) = address; return __refvalue(typedReference, T); } public void Run() { var oneLong = new OneLong { X = 1 + (2L << 32) }; var twoInt = Convert<TwoInt>(GetAddress(oneLong)); Console.WriteLine(twoInt.Y1 + " " + twoInt.Y2); // 1 2 oneLong.X = 3 + (4L << 32); Console.WriteLine(twoInt.Y1 + " " + twoInt.Y2); // 3 4 } 29/32 CLR Internal
  • 71. Хорошие времена для .NET-чиков 30/32