【问题标题】:can i use this same as with byte to other types values / objects?我可以将它与字节一起用于其他类型的值/对象吗?
【发布时间】:2015-10-04 18:02:06
【问题描述】:

因为我的目标是超越 List<T> 我正在测试数组,发现很少有开始测试的起点 在尝试从屏幕上捕获位图之前,我已经对此进行了测试, 并且测试证明使用就足够了。 我的问题是除了 byte[]

之外,哪些数据类型可以使用此 Copy() 代码

说我想要一个数据存储单元来利用非托管/不安全的优势

        public unsafe struct NusT
        {
            public unsafe int vi;
            public unsafe bool vb;
        }

而不是填充列表

我按如下方式初始化结构:1)

NusT n; 
n.vi= 90;
n.vb=true

我在测试以下内容后对此进行了测试:2)

NusT n = new NusT(){vi=90, vb=true};

这个测试是在测试之后进行的 :3)

NusT n = new NusT("90", true);

我认为最后两个结果相同,但第一个结果非常快,因为我没有创建对象所以

NusT n-> instructions- 1
n.vi=90 -> instructions- 1
n.vb=true -> instructions- 1 

现在我尽量减少了我可以做的事情,这从一门课开始: whitch 甚至比上面的 2 和 3 还要糟糕,因为它也使用属性

class bigAndSlow
{
     public int a { get; private set;}
     public bool b { get; private set;}
     public string  c { get; private set;}

     public bigAndSlow(int .. ,boo .. , string.. )
     {
        initialise ...
     }
}

所以现在是最终决定的时候

        public unsafe struct NusT
        {
            public unsafe int vi;
            public unsafe bool vb;
        }

我怎样才能实现这个极快的数据单元来使用 Copy() on

NusT[] NustyArr;


    static unsafe void Copy(byte[] src, int srcIndex,
            byte[] dst, int dstIndex, int count)
        {
            if (src == null || srcIndex < 0 ||
                dst == null || dstIndex < 0 || count < 0)
            {
                throw new ArgumentException();
            }
            int srcLen = src.Length;
            int dstLen = dst.Length;
            if (srcLen - srcIndex < count ||
                dstLen - dstIndex < count)
            {
                throw new ArgumentException();
            }


            // The following fixed statement pins the location of
            // the src and dst objects in memory so that they will
            // not be moved by garbage collection.          
            fixed (byte* pSrc = src, pDst = dst)
            {
                byte* ps = pSrc;
                byte* pd = pDst;

                // Loop over the count in blocks of 4 bytes, copying an
                // integer (4 bytes) at a time:
                for (int n = 0; n < count / 4; n++)
                {
                    *((int*)pd) = *((int*)ps);
                    pd += 4;
                    ps += 4;
                }

                // Complete the copy by moving any bytes that weren't
                // moved in blocks of 4:
                for (int n = 0; n < count % 4; n++)
                {
                    *pd = *ps;
                    pd++;
                    ps++;
                }
            }
        }


        static void Main(string[] args)
        {
            byte[] a = new byte[100];
            byte[] b = new byte[100];
            for (int i = 0; i < 100; ++i)
                a[i] = (byte)i;
            Copy(a, 0, b, 0, 100);
            Console.WriteLine("The first 10 elements are:");
            for (int i = 0; i < 10; ++i)
                Console.Write(b[i] + " ");
            Console.WriteLine("\n");
        }

【问题讨论】:

  • 只是一个旁注。当结构标记为不安全时,您不必将字段标记为不安全。
  • @M.kazemAkhgary 谢谢我不确定,我什至想把 * nex 放到 int 和 bool int*...
  • 我不明白你想要达到什么目的。你想复制“NusT”的数组吗?使用 memcpy。

标签: c# performance unit-testing data-structures unsafe


【解决方案1】:

是的,您可以使用任何 blittable 类型执行此操作。 blittable 类型是原始类型(整数和浮点类型,但不是 bool)、可 blittable 类型的一维数组和仅包含 blittable 类型字段的结构。

结构NusT 是不可blittable,因为它包含bool 字段。只需将其更改为byte,您将获得一个可以获取指针的 blittable 结构。

这是适用于任何类型的代码:

static unsafe void UnsafeCopy<T>(T[] src, int srcIndex, T[] dst, int dstIndex, int count) where T : struct
{
    if (src == null || srcIndex < 0 || dst == null || dstIndex < 0 || count < 0 || srcIndex + count > src.Length || dstIndex + count > dst.Length)
    {
        throw new ArgumentException();
    }

    int elem_size = Marshal.SizeOf(typeof(T));

    GCHandle gch1 = GCHandle.Alloc(src, GCHandleType.Pinned);
    GCHandle gch2 = GCHandle.Alloc(dst, GCHandleType.Pinned);

    byte* ps = (byte*)gch1.AddrOfPinnedObject().ToPointer() + srcIndex * elem_size;
    byte* pd = (byte*)gch2.AddrOfPinnedObject().ToPointer() + dstIndex * elem_size;
    int len = count * elem_size;

    try
    {
        // Loop over the count in blocks of 4 bytes, copying an
        // integer (4 bytes) at a time:
        for (int n = 0; n < len / 4; n++)
        {
            *((int*)pd) = *((int*)ps);
            pd += 4;
            ps += 4;
        }

        // Complete the copy by moving any bytes that weren't
        // moved in blocks of 4:
        for (int n = 0; n < len % 4; n++)
        {
            *pd = *ps;
            pd++;
            ps++;
        }
    }
    finally
    {
        gch1.Free();
        gch2.Free();
    }
}

但我强烈建议您使用Array.Copy。它已经是复制数组的最有效方式。请参阅以下复制 1M 元素数组的基准:

byte[] Array.Copy: 57,491 us

byte[] FastCopy:138,198 us

byte[] JustCopy:792,399 我们

byte[] UnsafeCopy: 138,575 us

byte[] MemCpy:57,667 us

NusT[] Array.Copy:1,197 毫秒

NusT[] JustCopy:1,843 毫秒

NusT[] UnsafeCopy:1,550 毫秒

NusT[] MemCpy:1,208 毫秒

FastCopy是你的复制函数,UnsafeCopy是我的模板函数,JustCopy是一个简单的实现for (int i = 0; i &lt; src.Length; i++) dst[i] = src[i];MemCpy 是 msvcrt memcpy 函数的 PInvoke 调用。

结论是:在 C# 中使用指针来提高性能是一种不好的做法。 JIT 不会优化不安全的代码。最好的解决方案是将性能关键代码移至本机 DLL。

【讨论】:

  • “这已经是最有效的复制数组的方法了” 我认为这不太确定。至少取决于数组大小。也许您的副本没有优化好或在调试模式下运行。另一种可能性是调用原生memcpy
  • 这是没有调试器的发布模式。添加了 memcpy 的结果。
  • 好的,Array.Copy 似乎是 memcpy 加上启动开销。对于小型阵列,启动成本最低的方法将胜出。对于大阵列,你是对的。无论如何,没有办法知道这是否是 OP 所关心的。他需要做出反应。
  • @usr 我在本主题开头描述的观点是,尝试击败 list 的性能,正如您从非常友善的@Andrey 的回答中看到的那样,他已经测试并得出了结果与我的自我收集论点一样,操纵它也是如此,例如减法添加、查询等。我在想托管和内置方法/类必须采取了一些步骤......可能会被剃掉..
  • 对于小型数组(大约 10 个元素),Array.Copy 的优势更加明显。不安全的方法(memcpy、c# 指针)需要修复内存中的对象,而Array.Copy 不需要。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-14
  • 1970-01-01
  • 2023-01-05
  • 1970-01-01
  • 2023-03-15
  • 2011-07-31
相关资源
最近更新 更多