Доклад для Middle и Senior .NET-программистов о различиях в рантаймах. Вы узнаете:
* чем отличается среда исполнения MS.NET от Моno;
* чем отличаются разные версии компилятора и BCL;
* как работает JIT-компилятор на различных архитектурах;
* что еще следует помнить, если вы пишете кроссплатформенные программы под .NET.
Доклад будет полезен всем разработчикам, которые хоть раз сталкивались с «неожиданным» поведением рантайма.
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
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
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
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
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