Подтвердить что ты не робот

Небезопасный С# трюк для улучшения скорости

Я не использую код с указателями (например, С++), а также с небезопасными островами: только "безопасный" С#. Теперь я хотел бы реализовать функцию в С# для .NET 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 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;
    }
}

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

    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;
    }

Примечание. Я сократил код, но думаю, что он достаточно ясен.

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

4b9b3361

Ответ 1

Я не думаю, что код в безопасности. После _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 байтов.

Ответ 2

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

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

Ответ 3

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

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

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

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