【问题标题】:C# fill array of struct with random dataC#用随机数据填充结构数组
【发布时间】:2018-02-14 19:38:31
【问题描述】:

出于测试目的,我想做以下事情:

class ArrayOfStructWithRandomData<T> where T : struct {
  private T[] array;

  ArrayOfStructWithRandomData() {
    array = new T[1000000];
    InitializeArrayToRandomData();
  }
}

如何在不使用 'unsafe' 关键字的情况下实现 InitializeArrayToRandomData()?

一个想法是使用Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T)) * 1000000) 分配一块非托管内存,然后使用Marshal.Copy(Byte[], Int32, IntPtr, Int32) 用随机数据填充该内存,然后使用类似

static T[] GetArrayFromNativePointer<T>(IntPtr unmanaged_memory, int length) {
  T[] result = new T[length];
  if (IntPtr.Size == 4) {
    int size = Marshal.SizeOf(typeof(T));

    // 32-bit system.
    for (int i = 0; i < result.Length; i++) {
      result[i] = (T)Marshal.PtrToStructure(unmanaged_memory, typeof(T));
      unmanaged_memory= new IntPtr(unmanaged_memory.ToInt32() + size);
    }
  } else {
    long size = Marshal.SizeOf(typeof(T));

    // Probably 64-bit system.
    for (int i = 0; i < result.Length; i++) {
      result[i] = (T)Marshal.PtrToStructure(unmanaged_memory, typeof(T));
      unmanaged_memory= new IntPtr(array.ToInt64() + size);
    }
  }
  return result;
}

有没有更好的办法?

【问题讨论】:

  • 目的是什么?哪种类型的测试需要您创建可能无效的对象?
  • 可能使用反射,您可以检索T 的字段并用随机值填充它们。
  • 性能测试,我不希望编译器优化东西,因为它看到所有的结构都是默认初始化的。
  • 我喜欢在这个例子中使用 Marshal 方法。我不确定为什么 PtrToStructure 在 for 循环中。
  • 问题是,你所做的任何事情 将是不安全的,因为没有办法通过泛型表达struct 只能包含 struct 成员。当您第一次使用包含引用的 struct 调用此方法并将该引用初始化为随机值时,就会引起欢闹。

标签: c# random struct


【解决方案1】:

如果您希望从非托管内存复制到托管数组,最有效的方法是固定数组并使用Marshal.Copy 进行复制。

void BlitFromByteArrayToArray(byte[] src, T[] dst)
{
    Debug.Assert(src.Length == dst.Length * Marshal.SizeOf(typeof(T)));

    GCHandle handle = GCHandle.Alloc(dst, GCHandleType.Pinned);
    try
    {
        Marshal.Copy(src, 0, handle.AddrOfPinnedObject(), src.Length);
    }
    finally
    {
        handle.Free();
    }
}

请注意,我没有分配非托管内存,然后用随机数据填充该非托管内存,而是使用字节数组对其进行编码以保存源随机数据。无需为此使用非托管内存。

请注意,整个方法确实假定T 是可位类型。

最后,如果效率很重要,最好做到以下几点:

  1. 分配您的托管阵列。
  2. 使用GCHandle.Alloc 固定阵列。
  3. 将随机数据写入AddrOfPinnedObject返回的非托管数组。
  4. 取消固定阵列。

这将避免需要先写入一个缓冲区,然后再复制到另一个。

【讨论】:

  • 大卫:Marshal.Alloc 不是将结构放在非托管内存中吗?所有 pin 所做的就是阻止垃圾收集处理内存。
  • @jdweng 这里没有Marshal.Alloc
  • 好的,但它仍然是非托管的:GCHandle 提供了一种从非托管内存访问托管对象的方法。
  • @jdweng Pinning 不会使内存不受管理。它只是阻止 GC 移动内存 (stackoverflow.com/questions/2490912/what-are-pinned-objects)。它仍然是托管内存。当您说固定只会阻止 GC 收集时,您错了。我们已经引用了该对象,这已经阻止了这一点。
【解决方案2】:

这个怎么样:

        [StructLayout(LayoutKind.Sequential)]
        struct Root
        {
            [MarshalAs(UnmanagedType.Struct, SizeConst = 1000000)]
            Child[] children { get; set; }
        }
        struct Child
        {
            int property1 { get; set; }
            int property2 { get; set; }
            int property3 { get; set; }
            int property4 { get; set; }
            int property5 { get; set; }
            int property6 { get; set; }
        }

【讨论】:

  • 如何用随机数据填充数组?
  • 这不是关于填充随机数据,因为发布的代码没有这样做。它是关于将数据从非托管移动到托管。我只是建议消除已发布代码中的 for 循环。
  • 正确的做法是使用GCHandle.Alloc(arr, GCHandleType.Pinned 固定阵列,然后使用AddrOfPinnedObjectMarshal.Copy
【解决方案3】:

如果您想关闭特定方法或属性的优化,您可以使用

[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]

这将阻止编译器和以后的 JITter 优化或内联该方法。然后,您可以继续使用默认值,而无需编译器最终优化。

【讨论】:

  • 问题是,我仍然想要优化(因为它是性能测试)。但我不想要任何基于我的所有结构内容都默认初始化这一事实的优化。
【解决方案4】:

我不确定你是否正在搜索,但你可以在初始化它的结构中使用隐式运算符,就像这个例子一样:

public static implicit operator MyStruct(T[] value) {
    return new MyStruct() { structValue = value, Structlength = value.Length };
  }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-23
    • 1970-01-01
    • 2017-06-04
    • 2013-09-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多