Unsafe C# trick to improve speed

空扰寡人 提交于 2019-12-09 14:40:29

问题


I am not used to code with pointers (e.g. C++), nor with unsafe islands: only "safe" C#. Now I'd like to implement a function in C# for the .Net Micro Framework, where the compactness and the performance are very important. Basically, I would to collect 4-ples of shorts and thus fill a buffer (e.g. byte-array). Let's say that every sample is such:

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

Each sample is collected via a timer-event, so that I can't loop (there are several reasons). I have tries many way to efficiently do that, but the most performing seems to be this one:

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

The improvement over this following safer way -for example- is not so high (177ms vs 224ms), but it is significant anyway.

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

Note: I have cut some code, but I think it's clear enough.

My question is: the "trick" I have made by copying the "fixed" pointer to another unfixed could be reliable or not?...However you can assume that all the data is statically allocated, so should be pinned. Thank you in advance. Cheers


回答1:


I don't think the code is safe. After _structPtr = (MyStruct*)ptr which is within the fixed scope, you then go on to put data into _structPtr on the assumption that _struct2 won't move. Whilst you are correct that it won't be GCed, that doesn't mean that the GC won't move it during memory compaction. The .NET Compact Framework still garbage collects, and I assume it compacts memory rather than leaving it fragmented.

If, for instance, a transient (non static) object allocated on the heap prior to _struct2 is removed by the GC then the memory in use by that struct may be shifted into the free space used by that transient object. At that point _structPtr is pointing to unused memory.

Would modifying Test3() to take a ref MyStruct data help?

Also, checkout [StructLayout(LayoutKind.Explicit)] and [FieldOffset(...)] which will allow you to have a single struct with multiple ways of accessing the same data within it. In your case either as 4 bytes or 1 int or (possibly) 1 array of 4 bytes.




回答2:


Look at this example from a different posting, on some code I have used extensively. You have the correct idea, just need to set the FieldOffset() values, and then use the fixed() keyword when accessing the data.

This method is quite reliable, but not really much faster. The reason I use it, is because when I have lots of fields I want a quick way of accessing them as an array.




回答3:


I don't think your "trick" is a problem. Nobody cares how you index memory, or how many offsets you use to accomplish it. I do think you need to heed the advice of others and make sure that you are controlling layout of your struct with StructLayout.Sequential, or .Explicit. Watch the Size and Pack options also.

The other issue, which has been mentioned, is that you need to do all of your work in the fixed block.

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

Personally, I think you've stumbled into some Micro-Optimization theatre and would be better served by using safe C# code. Based on the numbers that you have given, (224x10^-3 - 177x10^-3) yields 47ms, divided by 5000 iterations nets you 9.4us (9.4x10^-6) per iteration (assuming windows isn't doing something else at the time).



来源:https://stackoverflow.com/questions/5302153/unsafe-c-sharp-trick-to-improve-speed

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!