Небезопасный трюк C # для улучшения скорости - PullRequest
13 голосов
/ 14 марта 2011

Я не привык кодировать с помощью указателей (например, C ++) и небезопасных островов: только «безопасный» C #. Теперь я хотел бы реализовать функцию в C # для .Net Micro Framework, где компактность и производительность очень важны. По сути, я хотел бы собрать 4-кратные шорты и таким образом заполнить буфер (например, байтовый массив). Допустим, что каждый образец такой:

struct MyStruct
{
    public short An1;
    public short An2;
    public short An3;
    public short An4;
}

Каждый сэмпл собирается с помощью события таймера, поэтому я не могу выполнить цикл (есть несколько причин). У меня есть много способов, чтобы эффективно сделать это, но наиболее эффективный, кажется, этот:

unsafe struct MyStruct2
{
    public fixed byte Buffer[Program.BufferSize];
}


unsafe class Program
{
    public const int BufferSize = 0x1000;
    public const int ArraySize = BufferSize / 8;

    static MyStruct2 _struct2 = new MyStruct2();
    static MyStruct* _structPtr;


    unsafe static void Main(string[] args)
    {
        int iter = 5000;  //just for simulate many cycles

        for (int i = 0; i < iter; i++)
        {
            //let's make a trick!
            fixed (byte* ptr = _struct2.Buffer)
                _structPtr = (MyStruct*)ptr;

            _structIndex = 0;
            do
            {
                Test5();
            } while (++_structIndex < ArraySize);
        }


        Console.ReadKey();
    }


    unsafe static void Test5()
    {
        _structPtr->An1 = (short)An1();
        _structPtr->An2 = (short)An2();
        _structPtr->An3 = (short)An3();
        _structPtr->An4 = (short)An4();
        _structPtr++;
    }


    //simulations of ADC reading
    static int An1()
    {
        return 0x1111;
    }

    static int An2()
    {
        return 0x2222;
    }

    static int An3()
    {
        return 0x3333;
    }

    static int An4()
    {
        return 0x4444;
    }
}

Улучшение по сравнению с этим следующим более безопасным способом, например, не так высоко (177 мс против 224 мс), но оно в любом случае является значительным.

    static MyStruct Test3()
    {
        var data = new MyStruct();
        data.An1 = (short)An1();
        data.An2 = (short)An2();
        data.An3 = (short)An3();
        data.An4 = (short)An4();
        return data;
    }

Примечание: я вырезал некоторый код, но я думаю, что он достаточно ясен.

Мой вопрос: «трюк», который я сделал, копируя «фиксированный» указатель на другой незафиксированный, мог быть надежным или нет? ... Однако вы можете предположить, что все данные статически размещены, поэтому должны быть закреплены. Заранее спасибо. Приветствия

Ответы [ 3 ]

1 голос
/ 15 марта 2011

Я не думаю, что код безопасен.После _structPtr = (MyStruct*)ptr, который находится в пределах фиксированной области, вы затем продолжаете помещать данные в _structPtr в предположении, что _struct2 не будет перемещаться.Хотя вы правы в том, что он не будет GCed, это не значит, что GC не будет перемещать его во время сжатия памяти..NET Compact Framework по-прежнему собирает мусор, и я предполагаю, что он уплотняет память, а не оставляет ее фрагментированной.

Если, например, временный (не статичный) объект, выделенный в куче до _struct2, удаляетсяGC затем память, используемая этой структурой, может быть перемещена в свободное пространство, используемое этим временным объектом.В этот момент _structPtr указывает на неиспользуемую память.

Будет ли изменение Test3 () для получения помощи ref MyStruct data?

Кроме того, извлекайте [StructLayout(LayoutKind.Explicit)] и [FieldOffset(...)], что позволит вамиметь единую структуру с несколькими способами доступа к одним и тем же данным внутри нее.В вашем случае либо 4 байта, либо 1 int, либо (возможно) 1 массив из 4 байтов.

0 голосов
/ 15 марта 2011

Я не думаю, что твой трюк - это проблема. Никого не волнует, как вы индексируете память или сколько смещений вы используете для этого. Я думаю, что вам нужно прислушаться к советам других и убедиться, что вы управляете макетом вашей структуры с помощью StructLayout.Sequential или .Explicit. Смотрите также параметры размера и упаковки.

Другая проблема, о которой уже говорилось, заключается в том, что вам необходимо выполнять всю работу в фиксированном блоке.

fixed (byte* ptr = struct2.Buffer)
{
    var structPtr = (MyStruct*)ptr;
    var structIndex = 0;
    do
    {
        Test5(structPtr);
    } while (++structIndex < ArraySize);
}

Лично я думаю, что вы наткнулись на какой-нибудь театр микрооптимизации, и вам лучше будет использовать безопасный код C #. Исходя из заданных вами чисел, (224x10 ^ -3 - 177x10 ^ -3) дает 47 мс, разделенных на 5000 итераций, что дает вам 9.4us (9.4x10 ^ -6) за итерацию (при условии, что windows не делает что-то еще при время).

0 голосов
/ 15 марта 2011

Посмотрите на этот пример из другой публикации, в некотором коде, который я широко использовал.У вас есть правильная идея, вам просто нужно установить значения FieldOffset(), а затем использовать ключевое слово fixed() при доступе к данным.

Этот метод достаточно надежен, но не намного быстрее.Я использую его потому, что когда у меня много полей, мне нужен быстрый способ доступа к ним как к массиву.

...