Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/285.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# 创建堆栈大小为默认值50倍的线程时有什么危险?_C#_.net_Memory_Stack Memory - Fatal编程技术网

C# 创建堆栈大小为默认值50倍的线程时有什么危险?

C# 创建堆栈大小为默认值50倍的线程时有什么危险?,c#,.net,memory,stack-memory,C#,.net,Memory,Stack Memory,我目前正在开发一个性能非常关键的程序,我决定探索一条可能有助于减少资源消耗的途径,那就是增加工作线程的堆栈大小,这样我就可以将我将要访问的大部分数据(float[]s)移动到堆栈上(使用) 我知道线程的默认堆栈大小是1MB,所以为了移动我所有的float[]s,我必须将堆栈扩展大约50倍(到50MB~) 我知道这通常被认为是“不安全”的,不建议这样做,但在对照这种方法对我当前的代码进行基准测试之后,我发现处理速度提高了530%!因此,我不能在没有进一步调查的情况下简单地通过这个选项,这导致了我的

我目前正在开发一个性能非常关键的程序,我决定探索一条可能有助于减少资源消耗的途径,那就是增加工作线程的堆栈大小,这样我就可以将我将要访问的大部分数据(
float[]
s)移动到堆栈上(使用)

我知道线程的默认堆栈大小是1MB,所以为了移动我所有的
float[]
s,我必须将堆栈扩展大约50倍(到50MB~)

我知道这通常被认为是“不安全”的,不建议这样做,但在对照这种方法对我当前的代码进行基准测试之后,我发现处理速度提高了530%!因此,我不能在没有进一步调查的情况下简单地通过这个选项,这导致了我的问题;将烟囱增加到如此大的尺寸会有什么危险(可能会出现什么问题),我应该采取什么预防措施来最小化这些危险

我的测试代码

public static unsafe void TestMethod1()
{
    float* samples = stackalloc float[12500000];

    for (var ii = 0; ii < 12500000; ii++)
    {
        samples[ii] = 32768;
    }
}

public static void TestMethod2()
{
    var samples = new float[12500000];

    for (var i = 0; i < 12500000; i++)
    {
        samples[i] = 32768;
    }
}
公共静态不安全void TestMethod1()
{
浮动*样本=stackalloc浮动[12500000];
对于(var ii=0;ii<12500000;ii++)
{
样本[ii]=32768;
}
}
公共静态void TestMethod2()
{
var样本=新浮动[12500000];
对于(变量i=0;i<12500000;i++)
{
样本[i]=32768;
}
}

可能出错的一件事是,您可能没有获得这样做的许可。除非在完全信任模式下运行,否则框架将忽略对更大堆栈大小的请求(请参阅
线程构造函数(ParameterizedThreadStart,Int32)上的MSDN


与其将系统堆栈大小增加到如此巨大的数量,我建议您重写代码,以便在堆上使用迭代和手动堆栈实现

>P>高性能阵列可能以与普通C语言相同的方式访问,但这可能是麻烦的开始:考虑下面的代码:

float[] someArray = new float[100]
someArray[200] = 10.0;
您希望出现越界异常,这完全有道理,因为您试图访问元素200,但允许的最大值是99。如果您转到stackalloc路由,那么将不会有对象缠绕在要绑定检查的数组周围,并且以下内容不会显示任何异常:

Float* pFloat =  stackalloc float[100];
fFloat[200]= 10.0;

在上面,您分配了足够的内存来容纳100个浮点,并且您正在设置sizeof(float)内存位置,该位置从该内存的起始位置开始+200*sizeof(float),以容纳浮点值10。毫不奇怪,这个内存在为浮动分配的内存之外,没有人知道该地址中可以存储什么。如果幸运的话,您可能使用了一些当前未使用的内存,但同时可能会覆盖一些用于存储其他变量的位置。总而言之:不可预测的运行时行为。

我在那里有一个预约,我根本不知道如何预测它——权限、GC(需要扫描堆栈)等等——所有这些都可能受到影响。我很想改用非托管内存:

var ptr = Marshal.AllocHGlobal(sizeBytes);
try
{
    float* x = (float*)ptr;
    DoWork(x);
}
finally
{
    Marshal.FreeHGlobal(ptr);
}
我发现处理速度提高了530%

这是迄今为止我所说的最大的危险。您的基准测试有严重错误,表现出这种不可预测的行为的代码通常在某个地方隐藏着一个讨厌的bug

在.NET程序中消耗大量堆栈空间是非常非常困难的,除了过度递归。托管方法的堆栈框架的大小是一成不变的。方法的参数和方法中的局部变量之和。减去可以存储在CPU寄存器中的那些,您可以忽略它们,因为它们太少了

增加堆栈大小并不能完成任何事情,您只需要保留一堆永远不会被使用的地址空间。当然,没有任何机制可以解释不使用内存导致的性能提高

这与本机程序不同,特别是用C编写的程序,它还可以在堆栈帧上为数组保留空间。堆栈缓冲区溢出后的基本恶意软件攻击向量。在C#中也可能,您必须使用
stackalloc
关键字。如果您这样做,那么明显的危险是必须编写受到此类攻击的不安全代码,以及随机堆栈帧损坏。很难诊断bug。我认为从.NET4.0开始,在以后的抖动中,有一种针对这种情况的对策,抖动会生成代码,在堆栈帧上放置一个“cookie”,并在方法返回时检查它是否仍然完好无损。立即崩溃到桌面,如果发生这种情况,则无法拦截或报告事故。那是。。。对用户的精神状态有危险

程序的主线程(由操作系统启动的线程)默认情况下将有1 MB堆栈,在编译针对x64的程序时为4 MB。增加,这需要在生成后事件中使用/STACK选项运行Editbin.exe。通常,在32位模式下运行时,在程序启动出现问题之前,您可以请求高达500 MB的内存。当然,线程也可以轻松得多,对于32位程序,危险区通常在90MB左右。当您的程序已运行很长时间且地址空间从以前的分配中支离破碎时触发。总地址空间使用率必须已经很高,超过一个gig,才能获得此故障模式

三次检查你的代码,有非常错误的地方。除非您显式地编写代码以利用x5的加速比,否则无法获得具有更大堆栈的x5加速比。这总是需要不安全的代码。在C#中使用指针总是能够创建更快的代码,它不受数组边界检查的约束。

EDIT:(代码和度量的微小变化会导致结果的巨大变化)

首先,我在调试器(F5)中运行了优化的代码,但这是错误的。它应该在没有调试器的情况下运行(Ctrl+F5)。
    public static unsafe float TestMethod3()
    {
        float[] samples = new float[5000000];

        for (var ii = 0; ii < 5000000; ii++)
        {
            samples[ii] = 32768 + (ii != 0 ? samples[ii - 1] : 0);
        }

        return samples[5000000 - 1];
    }

    public static unsafe float TestMethod4()
    {
        float[] prev = new float[5000000];
        fixed (float* samples = &prev[0])
        {
            for (var ii = 0; ii < 5000000; ii++)
            {
                samples[ii] = 32768 + (ii != 0 ? samples[ii - 1] : 0);
            }

            return samples[5000000 - 1];
        }
    }

    public static unsafe float TestMethod5()
    {
        var ptr = Marshal.AllocHGlobal(5000000 * sizeof(float));
        try
        {
            float* samples = (float*)ptr;

            for (var ii = 0; ii < 5000000; ii++)
            {
                samples[ii] = 32768 + (ii != 0 ? samples[ii - 1] : 0);
            }

            return samples[5000000 - 1];
        }
        finally
        {
            Marshal.FreeHGlobal(ptr);
        }
    }
Stack-allocated array time: 00:00:00.2224429
Globally-allocated array time: 00:00:00.2206767
Heap-allocated array time: 00:00:00.1842670
------------------------------------------
Fastest: Heap.

  |    S    |    G    |    H    |
--+---------+---------+---------+
S |    -    | 100.80 %| 120.72 %|
--+---------+---------+---------+
G |  99.21 %|    -    | 119.76 %|
--+---------+---------+---------+
H |  82.84 %|  83.50 %|    -    |
--+---------+---------+---------+
Rates are calculated by dividing the row's value to the column's.
Stack-allocated array time: 00:00:00.4504903
Globally-allocated array time: 00:00:00.4020328
Heap-allocated array time: 00:00:00.3439016
------------------------------------------
Fastest: Heap.

  |    S    |    G    |    H    |
--+---------+---------+---------+
S |    -    | 112.05 %| 130.99 %|
--+---------+---------+---------+
G |  89.24 %|    -    | 116.90 %|
--+---------+---------+---------+
H |  76.34 %|  85.54 %|    -    |
--+---------+---------+---------+
Rates are calculated by dividing the row's value to the column's.
Standard       10,589.00  (1.00)
UnsafeStandard 10,612.00  (1.00)
Stackalloc     12,088.00  (1.14)
FixedStandard  10,715.00  (1.01)
GlobalAlloc    12,547.00  (1.18)
Standard       14,787.00   (1.02)
UnsafeStandard 14,549.00   (1.00)
Stackalloc     15,830.00   (1.09)
FixedStandard  14,824.00   (1.02)
GlobalAlloc    18,744.00   (1.29)
public static float Standard(int size) {
    float[] samples = new float[size];
    for (var ii = 0; ii < size; ii++) {
        samples[ii] = 32768 + (ii != 0 ? samples[ii - 1] : 0);
    }
    return samples[size - 1];
}

public static unsafe float UnsafeStandard(int size) {
    float[] samples = new float[size];
    for (var ii = 0; ii < size; ii++) {
        samples[ii] = 32768 + (ii != 0 ? samples[ii - 1] : 0);
    }
    return samples[size - 1];
}

public static unsafe float Stackalloc(int size) {
    float* samples = stackalloc float[size];
    for (var ii = 0; ii < size; ii++) {
        samples[ii] = 32768 + (ii != 0 ? samples[ii - 1] : 0);
    }
    return samples[size - 1];
}

public static unsafe float FixedStandard(int size) {
    float[] prev = new float[size];
    fixed (float* samples = &prev[0]) {
        for (var ii = 0; ii < size; ii++) {
            samples[ii] = 32768 + (ii != 0 ? samples[ii - 1] : 0);
        }
        return samples[size - 1];
    }
}

public static unsafe float GlobalAlloc(int size) {
    var ptr = Marshal.AllocHGlobal(size * sizeof(float));
    try {
        float* samples = (float*)ptr;
        for (var ii = 0; ii < size; ii++) {
            samples[ii] = 32768 + (ii != 0 ? samples[ii - 1] : 0);
        }
        return samples[size - 1];
    } finally {
        Marshal.FreeHGlobal(ptr);
    }
}

static void Main(string[] args) {
    int inputSize = 100000;
    var results = TestSuite.Create("Tests", inputSize, Standard(inputSize)).
        Add(Standard).
        Add(UnsafeStandard).
        Add(Stackalloc).
        Add(FixedStandard).
        Add(GlobalAlloc).
        RunTests();
    results.Display(ResultColumns.NameAndIterations);
}
IL_0011:  ldloc.0 
IL_0012:  ldloc.1 
IL_0013:  ldc.i4.4 
IL_0014:  mul 
IL_0015:  add 
IL_0016:  ldc.r4 32768.
IL_001b:  stind.r4 // <----------- This one
IL_001c:  ldloc.1 
IL_001d:  ldc.i4.1 
IL_001e:  add 
IL_001f:  stloc.1 
IL_0020:  ldloc.1 
IL_0021:  ldc.i4 12500000
IL_0026:  blt IL_0011
IL_0012:  ldloc.0 
IL_0013:  ldloc.1 
IL_0014:  ldc.r4 32768.
IL_0019:  stelem.r4 // <----------- This one
IL_001a:  ldloc.1 
IL_001b:  ldc.i4.1 
IL_001c:  add 
IL_001d:  stloc.1 
IL_001e:  ldloc.1 
IL_001f:  ldc.i4 12500000
IL_0024:  blt IL_0012
stind.r4: Store value of type float32 into memory at address
System.NullReferenceException
stelem.r4: Replace array element at index with the float32 value on the stack.
System.NullReferenceException
System.IndexOutOfRangeException
System.ArrayTypeMismatchException