Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/258.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#&。网络:stackalloc_C#_.net_Performance_Stack_Stackalloc - Fatal编程技术网

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];
    }
    }
    }
    
  • 堆栈分配的平均结果为280个刻度,堆分配的平均结果通常为1-0个刻度(在我的个人计算机上,英特尔Core i7)

    在我现在使用的计算机(Intel Core 2 Duo)上,结果比以前的结果更有意义(可能是因为优化代码未签入VS): 460~ticks用于堆栈分配,约380 ticks用于堆分配

    但这仍然没有意义。为什么会这样?我猜CLR注意到我们没有使用数组,所以可能它甚至没有分配它

  • 我不能给出确切的答案,但是
    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