0

For testing purposes, I want to do the following thing:

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

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

How could I implement InitializeArrayToRandomData() without using the 'unsafe' keyword?

One idea was to use Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T)) * 1000000) to allocate a chunk of unmanaged memory, then use Marshal.Copy(Byte[], Int32, IntPtr, Int32) to fill that memory with random data and then use something like

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

Is there a better way?

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
matthias_buehlmann
  • 4,641
  • 6
  • 34
  • 76
  • 3
    To what end? What type of testing requires you to create potentially invalid objects? – Damien_The_Unbeliever Sep 06 '17 at 10:43
  • May be using reflection you could retrieve fields of `T` and fill them up with random values. – Andrew Sklyarevsky Sep 06 '17 at 10:44
  • Performance tests in which I don't want the compiler to optimize things away because it sees that all the structs are default initialized for example. – matthias_buehlmann Sep 06 '17 at 10:44
  • I like using the Marshal methods in this instance. I'm not sure why the PtrToStructure is inside the for loop. – jdweng Sep 06 '17 at 10:46
  • The problem is, anything you do *is* going to be unsafe since there's no way to express via generics that the `struct` can only *contain* `struct` members. The first time you call this method with a `struct` containing a reference and initialize that reference to a random value, hilarity will ensue. – Damien_The_Unbeliever Sep 06 '17 at 10:51
  • Damien: will hilarity ensue upon initialization or upon attempt to access the reference? – matthias_buehlmann Sep 06 '17 at 10:53
  • @jdweng: it's in the loop because I think I need to marshal each element of the array separately - or is there a Marshal method that marshals the entire array directly? It seems to me that works only for arrays of primitive types (through Marshal.Copy) – matthias_buehlmann Sep 06 '17 at 10:55
  • If you have a root class that contains an array of structures then the data is continuous in memory and can be copied with one instructions. – jdweng Sep 06 '17 at 11:06
  • @jdweng how? in C# the array is always reference type, i.e. a pointer. – matthias_buehlmann Sep 06 '17 at 11:16
  • In your static method, `array` is not defined. – Little Endian Jun 05 '18 at 16:41

4 Answers4

2

If you want to turn off optimization for a specific method or property you can use

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

which will prevent the compiler and later the JITter from optimizing or inlining that method. You are then able to proceed using the default values without the compiler eventually optimizing things.

CShark
  • 1,413
  • 15
  • 25
  • The thing is, I still want optimizations (as it's performance tests). But I don't want any optimizations that would base on the fact that all my structs contents are default initialized. – matthias_buehlmann Sep 06 '17 at 10:56
0

i'am not sure if that what you are searching for but you can use implicit operator in the struct that initialize it just like this example :

public static implicit operator MyStruct(T[] value) {
    return new MyStruct() { structValue = value, Structlength = value.Length };
  }
moath naji
  • 663
  • 1
  • 4
  • 20
0

How about this :

        [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; }
        }
jdweng
  • 33,250
  • 2
  • 15
  • 20
  • How does this populate an array with random data? – David Heffernan Sep 06 '17 at 13:59
  • This isn't about populating with random data since the posted code didn't do it. It is about moving the data from unmanaged to managed. I'm just recommending eliminating the for loops in the posted code. – jdweng Sep 06 '17 at 14:10
  • The right way to do that is to pin the array using `GCHandle.Alloc(arr, GCHandleType.Pinned` and then `AddrOfPinnedObject` and then `Marshal.Copy`. – David Heffernan Sep 06 '17 at 14:16
0

If you wish to copy from unmanaged memory into a managed array, the most effective way is to pin the array and copy using 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();
    }
}

Note that rather than allocating unmanaged memory, and then filling that unmanaged memory with random data, I've coded this with a byte array to hold the source random data. There's no need to use unmanaged memory for this.

Note that this entire approach does rather assume that T is a blittable type.

Finally, if efficiency matters, it would be better to do the following:

  1. Allocate your managed array.
  2. Pin the array using GCHandle.Alloc.
  3. Write random data into the unmanaged array returned by AddrOfPinnedObject.
  4. Unpin the array.

This would avoid needing to write to one buffer and then copy to another.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • David : isn't the Marshal.Alloc putting the structure in unmanaged memory? All pin does is stops the garbage collection from disposing the memory. – jdweng Sep 06 '17 at 15:00
  • @jdweng There is no `Marshal.Alloc` here – David Heffernan Sep 06 '17 at 15:11
  • Ok, but it is still unmanaged :GCHandle Provides a way to access a managed object from unmanaged memory. – jdweng Sep 06 '17 at 15:59
  • @jdweng Pinning does not make the memory unmanaged. It simply stops the GC from moving the memory (https://stackoverflow.com/questions/2490912/what-are-pinned-objects). It is still managed memory. You are wrong when you say that pinning just prevents GC collection. That's already prevented by the fact that we have references to the object. – David Heffernan Sep 07 '17 at 09:28