Последние посты доступны только в английской версии блога


LegacyJIT-x86 и первый вызов метода



Сегодня я расскажу вам об одном из моих любимых бенчмарков (данный метод не возвращает ничего полезного, он нам нужен только в качестве примера):

[Benchmark]
public string Sum()
{
    double a = 1, b = 1;
    var sw = new Stopwatch();
    for (int i = 0; i < 10001; i++)
        a = a + b;
    return string.Format("{0}{1}", a, sw.ElapsedMilliseconds);
}

Интересный факт: если вы вызовете Stopwatch.GetTimestamp() перед первым вызовом метода Sum, то это увеличит скорость работы метода в несколько раз (фокус работает только для LegacyJIT-x86).

Читать дальше    Комментарии


Visual Studio и ProjectTypeGuids.cs



Это история о том, как я несколько часов пытался открыть проект в Visual Studio. Как-то раз я решил немножко поработать: стянул себе последние коммиты из репозитория, открыл Visual Studio и собрался программировать. Увы, один из моих проектов не открылся, а в окошке Output я увидел странное сообщение:

error  : The operation could not be completed.

В Solution Explorer, рядом с названием проекта была надпись "load failed", а вместо файлов было написано следующее: "The project requires user input. Reload the project for more information." Хмм, ну ок, я попробовал перегрузить проект. Увы, не помогло, я получил ещё два уже знакомых сообщения об ошибке:

error  : The operation could not be completed.
error  : The operation could not be completed.

Читать дальше    Комментарии


Blittable-типы



Вопрос дня: что выведет нижеприведённый код?

[StructLayout(LayoutKind.Explicit)]
public struct UInt128
{
    [FieldOffset(0)]
    public ulong Value1;
    [FieldOffset(8)]
    public ulong Value2;
}
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
    public UInt128 UInt128;
    public char Char;
}
class Program
{
    public static unsafe void Main()
    {
        var myStruct = new MyStruct();
        var baseAddress = (int)&myStruct;
        var uInt128Adress = (int)&myStruct.UInt128;
        Console.WriteLine(uInt128Adress - baseAddress);
        Console.WriteLine(Marshal.OffsetOf(typeof(MyStruct), "UInt128"));
    }
}

Если вы подумали, что в консоли напечатается два нуля (или просто два одинаковых значения), то вам нужно узнать больше про внутреннее устройство структур в .NET. Ниже представлены результаты выполнения кода в зависимости от рантайма:

MS.NET-x86MS.NET-x64Mono
uInt128Adress - baseAddress 480
Marshal.OffsetOf(typeof(MyStruct), "UInt128")000

Чтобы разобраться с ситуацией, нам необходимо узнать больше про blittable-типы.

Читать дальше    Комментарии


DotNext 2015 Moscow: Уже скоро



Нынче удивительное время для .NET-разработчиков: Microsoft развивают свою экосистему семимильными шагами, причём ребята не только постоянно опенсорсят новые штуки, но и существенно развивают свои старые и добрые продукты. Появляется всё больше возможностей писать самые разнообразные приложения с использованием новейших технологий и инструментов. А вместе с этим появляется необходимость постоянного саморазвития, ведь нужно же поддерживать свой уровень квалификации и как-то оставаться в курсе текущего состояния индустрии. Но как же угнаться за бешенными темпами научно-технического прогресса?

Конференция DotNext спешит на помощь! Это такое замечательное мероприятие, на котором специально обученные люди целый день будут рассказывать крутые доклады про самые разные аспекты .NET-разработки: от общефилософских рассуждений про технологии до кровавых кишков рантайма. Но самое главное — в одном месте единовременно соберётся несколько сотен крутых .NET-разработчиков: можно наконец-то лично встретиться с коллегами по цеху, обменяться опытом и обсудить насущные программистские проблемы.

Конференции такого уровня бывают не часто, так что если есть возможность, то советую забежать на огонёк: Москва, 11 декабря 2015, гостиница «Radisson Славянская». Если возможности забежать нет, то к событию можно присоединиться онлайн. А пока что можно насладиться записями докладов предыдущей конференции, если вы этого ещё не сделали. Всем продуктивной .NET-разработки.

Читать дальше    Комментарии


RyuJIT RC и свёртка констант



Update: Нижеприведённый материал справедлив для релизной версии RyuJIT (часть .NET Framework 4.6).

Задачка дня: какой из методов быстрее?

public double Sqrt13()
{
    return Math.Sqrt(1) + Math.Sqrt(2) + Math.Sqrt(3) + Math.Sqrt(4) + Math.Sqrt(5) + 
           Math.Sqrt(6) + Math.Sqrt(7) + Math.Sqrt(8) + Math.Sqrt(9) + Math.Sqrt(10) + 
           Math.Sqrt(11) + Math.Sqrt(12) + Math.Sqrt(13);
}
public double Sqrt14()
{
    return Math.Sqrt(1) + Math.Sqrt(2) + Math.Sqrt(3) + Math.Sqrt(4) + Math.Sqrt(5) + 
           Math.Sqrt(6) + Math.Sqrt(7) + Math.Sqrt(8) + Math.Sqrt(9) + Math.Sqrt(10) + 
           Math.Sqrt(11) + Math.Sqrt(12) + Math.Sqrt(13) + Math.Sqrt(14);
}

Я померил скорость работы с помощью BenchmarkDotNet для RyuJIT RC (часть .NET Framework 4.6 RC) получил следующие результаты:

// BenchmarkDotNet=v0.7.4.0
// OS=Microsoft Windows NT 6.2.9200.0
// Processor=Intel(R) Core(TM) i7-4702MQ CPU @ 2.20GHz, ProcessorCount=8
// CLR=MS.NET 4.0.30319.0, Arch=64-bit  [RyuJIT]
Common:  Type=Math_DoubleSqrtAvx  Mode=Throughput  Platform=X64  Jit=RyuJit  .NET=Current  

Method | AvrTime | StdDev | op/s | ------- |--------- |---------- |------------- | Sqrt13 | 55.40 ns | 0.571 ns | 18050993.06 | Sqrt14 | 1.43 ns | 0.0224 ns | 697125029.18 |

Как же так? Добавление в выражение одно дополнительного Math.Sqrt ускорило метод в 40 раз! Давайте разберёмся.

Читать дальше    Комментарии


Размотка маленьких циклов в разных версиях JIT



Вопрос дня: что выведет нижеприведённый код?

struct Point
{
    public int X;
    public int Y;
}
static void Print(Point p)
{
    Console.WriteLine(p.X + " " + p.Y);
}
static void Main()
{
    var p = new Point();
    for (p.X = 0; p.X < 2; p.X++)
        Print(p);
}

Правильный ответ: зависит. В JIT-x86 под CLR2 был баг, который портил эту замечательную программу. А проблема кроется в оптимизации, которая назвается раскрутка маленького цикла. Тема интересная, давайте обсудим её подробно.

Читать дальше    Комментарии


RyuJIT CTP5 и размотка циклов



Уже скоро нам будет доступен RyuJIT, JIT-компилятор следующего поколения для .NET-приложений. Microsoft любит рассказывать нам о преимуществах использования SIMD и сокращением времени JIT-компиляции. Но что можно сказать о базовых оптимизациях кода, за которые обычно отвечает компилятор? Сегодня мы поговорим о такой оптимизации как размотка (раскрутка) цикла. Если кратко, то это оптимизации кода вида

for (int i = 0; i < 1024; i++)
    Foo(i);

превращается в

for (int i = 0; i < 1024; i += 4)
{
    Foo(i);
    Foo(i + 1);
    Foo(i + 2);
    Foo(i + 3);
}

Подобный подход может заметно увеличить производительность вашего кода. Итак, как же обстоят дела с раскруткой цикла в .NET?

Читать дальше    Комментарии


Определение версии JIT в рантайме



Иногда мне в моих маленьких C#-экспериментах нужно определять версию используемого JIT-компилятора. Понятно, что её можно определить заранее исходя из окружения. Но порой мне хочется знать её в рантайме, чтобы выполнять специфичный код для текущего JIT-компилятора. Строго говоря, я хочу получать значение из следующего перечисления:

public enum JitVersion
{
    Mono, MsX86, MsX64, RyuJit
}

Я могу легко определить, что работаю под Mono, по наличию класса Mono.Runtime. Если это не так, то можно считать, что мы работаем с JIT от Microsoft. JIT-x86 легко узнать с помощью IntPtr.Size == 4. А вот чтобы отличить старый JIT-x64 от нового RyuJIT необходимо немного призадуматься. Далее я покажу, как это можно сделать с помощью бага JIT-x64, который я описывал в предыдущем посте.

Читать дальше    Комментарии


История про баг в JIT-x64



Можете ли вы сказать, что выведет следующий код для step=1?

public void Foo(int step)
{
    for (int i = 0; i < step; i++)
    {
        bar = i + 10;
        for (int j = 0; j < 2 * step; j += step)
            Console.WriteLine(j + 10);
    }
}

Если вы назвали конкретные числа, то ошиблись. Правильный ответ: зависит. Заголовок подсказывает нам, что под x64 программа может вести себя не так, как мы от неё ожидаем.

Читать дальше    Комментарии


История про инлайнинг под JIT-x86 и starg



Порой можно узнать много интересного во время чтения исходников .NET. Взглянем на конструктор типа Decimal из .NET Reference Source (mscorlib/system/decimal.cs,158):

// Constructs a Decimal from an integer value.
//
public Decimal(int value) {
    //  JIT today can't inline methods that contains "starg" opcode.
    //  For more details, see DevDiv Bugs 81184: x86 JIT CQ: Removing the inline striction of "starg".
    int value_copy = value;
    if (value_copy >= 0) {
        flags = 0;
    }
    else {
        flags = SignMask;
        value_copy = -value_copy;
    }
    lo = value_copy;
    mid = 0;
    hi = 0;
}

В комментарии сказано, что если метод содержит IL-опкод starg, то он не может быть заинлайнен под x86. Любопытно, не правда ли?

Читать дальше    Комментарии



Подписаться: RSS Atom