<snip<
Here are the problems fixed in your code:
- The GC and the C# compiler are "VERY" smart
- When you call the constructor on the GCHandle the second
time it sees that it is the same object and does nothing.
Hmm... but during the time when it's not pinned, the garbage collector
*could* have moved the object.
- the iter count has been normalized so that now you measure
allocation of the same amount of memory.
No, *that's* a problem. You're not only allocating the same amount of
memory that way, you're performing fewer iterations - fewer pinning
operations. In other words, you're skewing all your figures.
As you see the cost of pinning for arrays shorter than 1024
elements is "substantial".
Well, pinning isn't free - but your stats are inappropriate IMO.
Here's an alternative version which still takes into account the
allocation time, and still allocates a new array each time - but
performs the same *number of pins* for each size.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
class Test
{
const int Iterations = 1000000;
static void Main(string[] args)
{
for (int index = 2; index < 100000; index *= 2)
{
int allocationOnly = TimeAllocation(index);
int allocAndPinning = TimeAllocAndPin(index);
Console.WriteLine("{0}: {1}", index,
allocAndPinning-allocationOnly);
}
}
static int TimeAllocation(int index)
{
Stopwatch sw = Stopwatch.StartNew();
for (int j = 0; j < Iterations; j++)
{
byte[] values = new byte[index];
values[1] = 2;
}
sw.Stop();
GC.Collect();
GC.WaitForPendingFinalizers();
return (int) sw.ElapsedMilliseconds;
}
static int TimeAllocAndPin(int index)
{
Stopwatch sw = Stopwatch.StartNew();
for (int j = 0; j < Iterations; j++)
{
byte[] values = new byte[index];
GCHandle h = GCHandle.Alloc(values,GCHandleType.Pinned);
values[1] = 2;
h.Free();
}
sw.Stop();
GC.Collect();
GC.WaitForPendingFinalizers();
return (int) sw.ElapsedMilliseconds;
}
}
I've used byte arrays instead of int arrays to make it clearer what the
size involved is. Here are my results - each figure is the number of
milliseconds difference between "just allocate" and "allocate and pin".
2: 457
4: 474
8: 394
16: 359
32: 441
64: 571
128: 592
256: 576
512: 254
1024: 369
2048: 190
4096: 232
8192: -264
16384: -1129
32768: -2814
65536: -3277
Now, I suspect that by the end the garbage collector is affecting the
results significantly, hence the odd nature - but the important thing
is that there doesn't appear to be the escalation you expect. If
copying were involved, the numbers would go up linearly with the size,
with a certain constant involved as well. Indeed, if you add in a loop
to manually set the values of the byte array in the pinning version
(reproducing copying) then you get that effect.
I still don't believe there's any copying involved, and I don't think
you've produced any evidence that there is. You certainly haven't
produced any documentation backing up your assertion. Wouldn't you
expect there to be some mention of the copying involved *somewhere*?