Search code examples
c#arrayspointersmarshallingunmanaged

How can you fill an entire array pointer with a single value with a single write operation?


I have a pointer to a byte array, and I need to set the values of a certain region of this array to 0. I'm quite familiar with the methods available through the Marshal/Buffer/Array classes, and this problem is not at all hard.

The problem, however, is that I do not want to create excessive arrays, or write every byte one-by-one. All the methods I'm familiar with require full arrays, though, and they obviously don't work with single values.

I've seen several C methods that would achieve the result I'm looking for, but I don't have believe I have access to these methods without including the whole C library, or without writing platform-specific code.

My current solution is shown below, but I'd like to achieve this without allocating a new byte array.

Marshal.Copy(new byte[Length], 0, ptr + offset, length);

So is there a method in C#, or in an unmanaged language/library that I can use to fill an array (via a pointer) at a certain offset and for a certain length, with one single value (0)?


Solution

  • Miraculously, ChatGPT came rather close when I asked what would be a good solution to this problem. It didn't figure it out, but it suggested that I use spans.

    As such, this is the solution I've come up with:

    Span<byte> span = new Span<byte>(ptr + offset, Length);
    span.Fill(0);
    

    This solution is about 25 times faster than having to allocate a byte array for very large arrays.

    Example benchmarks:

        int size = 100_000;
        nint ArrayPointer = Marshal.AllocHGlobal(size);
        int trials = 1_000_000;
    
        // Runtime was 1582ms
        Benchmark("Fill with span", () =>
        {
            Span<byte> span = new Span<byte>((void*) ArrayPointer, size);
            span.Fill(0);
        }, trials);
    
        // Runtime was 40681ms
        Benchmark("Fill with allocation", () =>
        {
            Marshal.Copy(new byte[size], 0, ArrayPointer, size);
        }, trials);
    
        // Far too slow to get a result with these settings
        Benchmark("Fill individually", () =>
        {
            for (int i = 0; i < size; i++)
            {
                Marshal.WriteByte(ArrayPointer + i, 0);
            }
        }, trials);
    
        // Results with size = 100_000 and trials = 100_000
        // Fill with span: 176ms
        // Fill with allocation: 4382ms
        // Fill individually: 24672ms