C#&。网络:stackalloc
我对C#&。网络:stackalloc,c#,.net,performance,stack,stackalloc,C#,.net,Performance,Stack,Stackalloc,我对stackalloc操作符的功能有几个问题 它实际上是如何分配的?我觉得它有点像: void* stackalloc(int sizeInBytes) { void* p = StackPointer (esp); StackPointer += sizeInBytes; if(StackPointer exceeds stack size) throw new StackOverflowException(...); return p; }
stackalloc
操作符的功能有几个问题
void* stackalloc(int sizeInBytes)
{
void* p = StackPointer (esp);
StackPointer += sizeInBytes;
if(StackPointer exceeds stack size)
throw new StackOverflowException(...);
return p;
}
但是我做了一些测试,我不确定它是如何工作的。我们不能确切地知道它是做什么的,它是如何做的,但我想知道它的基本原理 class Program
{
static void Main(string[] args)
{
Stopwatch sw1 = new Stopwatch();
sw1.Start();
StackAllocation();
Console.WriteLine(sw1.ElapsedTicks);
Stopwatch sw2 = new Stopwatch();
sw2.Start();
HeapAllocation();
Console.WriteLine(sw2.ElapsedTicks);
}
static unsafe void StackAllocation()
{
for (int i = 0; i < 100; i++)
{
int* p = stackalloc int[100];
}
}
static void HeapAllocation()
{
for (int i = 0; i < 100; i++)
{
int[] a = new int[100];
}
}
}
类程序
{
静态void Main(字符串[]参数)
{
秒表sw1=新秒表();
sw1.Start();
StackAllocation();
Console.WriteLine(sw1.ElapsedTicks);
秒表sw2=新秒表();
sw2.Start();
HeapAllocation();
Console.WriteLine(sw2.ElapsedTicks);
}
静态不安全的void StackAllocation()
{
对于(int i=0;i<100;i++)
{
int*p=stackalloc int[100];
}
}
静态空洞位置()
{
对于(int i=0;i<100;i++)
{
int[]a=新的int[100];
}
}
}
stackalloc
是使用IL操作码实现的。我查看了由stackalloc
的发布版本生成的机器代码,它比我预期的更复杂。我不知道localloc
是否会按照if
的指示检查堆栈大小,或者当硬件堆栈实际溢出时CPU是否检测到堆栈溢出
对此答案的注释表明,提供给localloc
的链接从“本地堆”分配空间。问题是除了PDF格式的实际标准外,MSIL没有很好的在线参考。上面的链接来自System.Reflection.Emit.OpCodes
类,该类与MSIL无关,而是用于生成MSIL的库
然而,在标准文件中有一个更精确的描述:
每个方法状态的一部分是本地内存池。可以使用localloc
指令从本地内存池显式分配内存。在方法退出时回收本地内存池中的所有内存,这是回收本地内存池内存的唯一方法(没有提供用于释放此方法调用期间分配的本地内存的指令)。本地内存池用于分配在编译时类型或大小未知且程序员不希望在托管堆中分配的对象
因此,“本地内存池”基本上就是所谓的“堆栈”,C语言使用stackalloc
操作符从这个池中进行分配HeapAllocation
的调用,从而大大缩短执行时间。在使用stackalloc
时,它似乎不够聪明,无法执行相同的优化。如果您关闭优化或以某种方式使用分配的缓冲区,您将看到stackalloc
稍微快一点stackalloc速度更快的情况:
private static volatile int _dummy; // just to avoid any optimisations
// that have us measuring the wrong
// thing. Especially since the difference
// is more noticable in a release build
// (also more noticable on a multi-core
// machine than single- or dual-core).
static void Main(string[] args)
{
System.Diagnostics.Stopwatch sw1 = new System.Diagnostics.Stopwatch();
Thread[] threads = new Thread[20];
sw1.Start();
for(int t = 0; t != 20; ++t)
{
threads[t] = new Thread(DoSA);
threads[t].Start();
}
for(int t = 0; t != 20; ++t)
threads[t].Join();
Console.WriteLine(sw1.ElapsedTicks);
System.Diagnostics.Stopwatch sw2 = new System.Diagnostics.Stopwatch();
threads = new Thread[20];
sw2.Start();
for(int t = 0; t != 20; ++t)
{
threads[t] = new Thread(DoHA);
threads[t].Start();
}
for(int t = 0; t != 20; ++t)
threads[t].Join();
Console.WriteLine(sw2.ElapsedTicks);
Console.Read();
}
private static void DoSA()
{
Random rnd = new Random(1);
for(int i = 0; i != 100000; ++i)
StackAllocation(rnd);
}
static unsafe void StackAllocation(Random rnd)
{
int size = rnd.Next(1024, 131072);
int* p = stackalloc int[size];
_dummy = *(p + rnd.Next(0, size));
}
private static void DoHA()
{
Random rnd = new Random(1);
for(int i = 0; i != 100000; ++i)
HeapAllocation(rnd);
}
static void HeapAllocation(Random rnd)
{
int size = rnd.Next(1024, 131072);
int[] a = new int[size];
_dummy = a[rnd.Next(0, size)];
}
本规范与问题中的规范之间的重要差异:
stackalloc
经常被用作使用fixed
将数组固定在堆上的替代方法。固定数组对堆性能有害(不仅对该代码有害,对使用同一堆的其他线程也有害),因此,如果声明的内存在任何合理的时间长度内使用,那么对性能的影响将更大
虽然我的代码演示了一个案例,其中stackalloc
提供了性能优势,但在这个问题中,这可能更接近于大多数情况,即有人可能会急切地通过使用它来“优化”。希望这两段代码结合在一起能显示出整个stackalloc
可以提高性能,同时也会严重影响性能
通常,你不应该考虑<代码> StActoLoC/<代码>,除非你需要使用固定内存来与非托管代码进行交互,而且它应该被认为是“代码>固定< /代码>的替代品,而不是通用堆分配的替代品。在这种情况下使用仍然需要谨慎,在开始之前要有先见之明,在完成之后还要进行分析
在其他情况下使用可能会带来好处,但它应该远远低于您将尝试的性能改进列表 编辑: 回答问题的第一部分。Stackalloc在概念上与您描述的一样。它获取堆栈内存块,然后返回指向该块的指针。它不会检查内存是否适合,而是检查是否尝试获取memo