[.net] Most efficient way of doing a IntPtr to IntrPtr copy ?

Started by
8 comments, last by Fiddler 14 years, 4 months ago
as the title says I guess, I suppose one could use unmanaged code, whether there is a faster/better solution I have no idea, I'm suprised there is no Marshal.Copy(intptr,intptr) the closest one is intptr,intptr[], any thoughts ?
Advertisement
An IntPtr is just a container for an int (or a long on 64 bit platforms). When you're coping one, you're just coping an int, which is just 4 bytes (8 if it's a long). You really can't get much faster than that.

You can also do:

IntPtr foo = new IntPtr(1);
IntPtr bar = foo;

Since IntPtr's are value types, the contents of foo are copied to bar.

Now this is all assuming I'm not misunderstanding your question.

However, if you mean that you want to marshal an IntPtr to a different type, such as structs, arrays, or delegates, there's a whole bunch of functions that do that in the Marshal class.
Yes, essentially doing a memcpy from one intptr to another, however, Marshal.Copy only does conversion, so, I was just wondering, if you had to do it manually is th most effecient way to work with unsafe pointers ? I've seen some examples where they have a few intptr's and do a .ReadInt* .WriteInt* which I assumed to hardly be effecient, maybe I'm wrong :)
I seriously doubt there's a method that's more efficient than doing:

IntPtr foo = new IntPtr(1);
IntPtr bar = foo;

But no, I doubt using unsafe pointers would be faster. There's actually a performance penalty when entering unsafe mode. So one should only do that when you're absolutely certain that it's worth it. I can pretty much guarentee that this isn't one of them. I can't comment specifically on the examples you mention, but I doubt their code is ideal.

Thus far I've only seen one situation where using unsafe pointers gave me a benefit: locking a Bitmap, and working with the raw data that the unsafe pointer it returns points to, rather than marshaling the data to a .net array, which would involve creating a copy of all the data. Hmm, hooray for run on sentences :P .
Are you trying to copy the memory that's pointed to by one IntPtr into the memory pointed to by another IntPtr? Doing IntPtr bar = foo; copies the value of the pointer, but does nothing to the memory being pointed at. Maybe that's what you want, but it's not clear to me.

If you want to copy the data being pointed at, and the data is small (less than say, a few megabytes), then an easy way might be to make a byte array of the right size and use Marshal.Copy to copy into managed memory, and then use another Marshal.Copy overload to copy from the byte[] to your other unmanaged block. The Marshal.Copy functions are meant to copy data between managed and unmanaged memory spaces. I don't know of a method in .NET to copy data between two unmanaged memory spaces. If this is what you want to do, you should maybe describe your problem in more detail because maybe there's a simpler solution?
Yes, I guess the subject title is a little misleading, but yes, copying the data from one IntPtr location to another, hence my original post saying about Marshal.Copy not having an IntPtr(source) and IntPtr(Destination) Override, rightly so being a converter so to speak.

I don't think marshalling to and from a managed type is effecient, and re-reading the other posters posts, it shouldn't have any overhead using unsafe pointers, as you're not Marshalling to and from anything, IntPtr (I think) is easily castable to (byte/int/etc)*, I was mainly curious, there isn't much call for this sort of thing, however, say you have a custom clpiboard format and within your format you wish to store the pixels from a Bitmap, then, this is where it would be useful.

Well, I wrote this little program to test:

using System;using System.Collections.Generic;using System.Windows.Forms;using System.Runtime.InteropServices;namespace TestCS{    class Program    {        unsafe struct copystruct2        {            fixed long l[2];        }        unsafe struct copystruct4        {            fixed long l[4];        }        unsafe struct copystruct16        {            fixed long l[16];        }        unsafe struct copystruct128        {            fixed long l[128];        }        unsafe static void Main(string[] args)        {            const int size = 1048576;            const int count = 100;            IntPtr ptr = Marshal.AllocHGlobal(size);            IntPtr ptr2 = Marshal.AllocHGlobal(size);            byte[] bytes = new byte[size];            Console.WriteLine("Performing tests on copying {0} kilobytes of memory.",                size / (double)1024);            System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();            // make sure methods are JIT'd (they probably are, since there            // should be native images of the standard library, but just to be safe..)            watch.Reset();            watch.Start();            watch.Stop();            Marshal.Copy(bytes, 0, ptr, size);            Marshal.Copy(ptr2, bytes, 0, size);            watch.Reset();            watch.Start();            for (int i = 0; i < count; i++)            {                Marshal.Copy(bytes, 0, ptr, size);                Marshal.Copy(ptr2, bytes, 0, size);            }            watch.Stop();            double averageTime = watch.ElapsedMilliseconds / (double)count;            Console.WriteLine("Average time for Marshal is {0} ms.", averageTime);            {                watch.Reset();                watch.Start();                byte* src = (byte*)ptr;                byte* dest = (byte*)ptr2;                for (int j = 0; j < count; j++)                {                    for (int i = 0; i < size / sizeof(byte); i++)                    {                        dest = src;                    }                }                watch.Stop();                averageTime = watch.ElapsedMilliseconds / (double)count;            }            Console.WriteLine("Average time byte copy is {0} ms.", averageTime);            {                watch.Reset();                watch.Start();                int* src = (int*)ptr;                int* dest = (int*)ptr2;                for (int j = 0; j < count; j++)                {                    for (int i = 0; i < size / sizeof(int); i++)                    {                        dest = src;                    }                }                watch.Stop();                averageTime = watch.ElapsedMilliseconds / (double)count;            }            Console.WriteLine("Average time int copy is {0} ms.", averageTime);            {                watch.Reset();                watch.Start();                long* src = (long*)ptr;                long* dest = (long*)ptr2;                for (int j = 0; j < count; j++)                {                    for (int i = 0; i < size / sizeof(long); i++)                    {                        dest = src;                    }                }                watch.Stop();                averageTime = watch.ElapsedMilliseconds / (double)count;            }            Console.WriteLine("Average time long copy is {0} ms.", averageTime);            watch.Reset();            watch.Start();            {                copystruct2* src = (copystruct2*)ptr;                copystruct2* dest = (copystruct2*)ptr2;                for (int j = 0; j < count; j++)                {                    for (int i = 0; i < size / sizeof(copystruct2); i++)                    {                        dest = src;                    }                }                watch.Stop();                averageTime = watch.ElapsedMilliseconds / (double)count;            }            Console.WriteLine("Average time copystruct2 copy is {0} ms.", averageTime);            watch.Reset();            watch.Start();            {                copystruct4* src = (copystruct4*)ptr;                copystruct4* dest = (copystruct4*)ptr2;                for (int j = 0; j < count; j++)                {                    for (int i = 0; i < size / sizeof(copystruct4); i++)                    {                        dest = src;                    }                }                watch.Stop();                averageTime = watch.ElapsedMilliseconds / (double)count;            }            Console.WriteLine("Average time copystruct4 copy is {0} ms.", averageTime);            watch.Reset();            watch.Start();            {                copystruct16* src = (copystruct16*)ptr;                copystruct16* dest = (copystruct16*)ptr2;                for (int j = 0; j < count; j++)                {                    for (int i = 0; i < size / sizeof(copystruct16); i++)                    {                        dest = src;                    }                }                watch.Stop();                averageTime = watch.ElapsedMilliseconds / (double)count;            }            Console.WriteLine("Average time copystruct16 copy is {0} ms.", averageTime);            watch.Reset();            watch.Start();            {                copystruct128* src = (copystruct128*)ptr;                copystruct128* dest = (copystruct128*)ptr2;                for (int j = 0; j < count; j++)                {                    for (int i = 0; i < size / sizeof(copystruct128); i++)                    {                        dest = src;                    }                }                watch.Stop();                averageTime = watch.ElapsedMilliseconds / (double)count;            }            Console.WriteLine("Average time copystruct128 copy is {0} ms.", averageTime);            Marshal.FreeHGlobal(ptr);            Marshal.FreeHGlobal(ptr2);            Console.ReadKey();        }    }}


Try it out and see what results you get (make sure to compile with unsafe code). From my tests, doing a Marshal.Copy to managed memory and unmanaged memory is not terribly inefficient, it's slightly slower than using int* in a loop to copy from one memory location to another.

Using long* to copy is about twice as fast as int*. For me it seems that using structures with fixed size long arrays can be even faster, but that depends on the amount of memory being copied. On my machine (P4 3.0 GHz) the structure with an array of 128 longs is 50% faster still than using a single long to copy when copying only 1MB, but when doing 10 or 100 MB all the structures take the same amount of time as the long to perform the whole copy. So maybe there's some cache memory somewhere which is speeding things up for smaller copies.
[DllImport("msvcrt.dll", EntryPoint = "memcpy")]
public unsafe static extern void CopyMemory(IntPtr pDest, IntPtr pSrc, int length);

It performs as fast as the 128 byte struct copy in the previous post but performs much faster when running with optimisations off (debugging)
Since this thread was bumped and I didn't see it mentioned, I use this:

[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
static extern void CopyMemory(IntPtr Destination, IntPtr Source, uint Length);

I compared it to the one mtopley posted and got same throughput.
Quote:Original post by mtopley
[DllImport("msvcrt.dll", EntryPoint = "memcpy")]
public unsafe static extern void CopyMemory(IntPtr pDest, IntPtr pSrc, int length);

It performs as fast as the 128 byte struct copy in the previous post but performs much faster when running with optimisations off (debugging)


The difference is that kanato's approach is cross-platform, while this one isn't. Since you don't actually gain any performance, what's the point in losing cross-platform support?

[OpenTK: C# OpenGL 4.4, OpenGL ES 3.0 and OpenAL 1.1. Now with Linux/KMS support!]

This topic is closed to new replies.

Advertisement