为什么在长方法中会出现StackOverflow错误?

问题描述 投票:5回答:1

我有一个小型只读数据库(基于来自服务的数据)。加载时间至关重要,因此无法从DB,XML等中提取数据。我在生成的C#文件中使用数据的直接构造:

void Load(){
    var aa = new A[ 100500 ];
    aa[0] = new A( ... );
    ...
    aa[100499] = new A( ... );
    AA = aa;
}

这里没有递归,堆栈锁等。但是我遇到了StackOverflow错误。

在反汇编器窗口中,我发现JIT将此代码转换为:

    var aa = new A[ 100500 ];
    var a0 = new A( ... );
    aa[0] = a0;
    ...
    var a100499 = new A( ... );
    aa[100499] = a100499;

因此它在堆栈中创建了100500个变量aXXXXX:

-- aa[32] = new A( qwerty, asdf );
mov         ecx,185C10h                 -- var a32 = new A
call        001730F4                  
mov         dword ptr [ebp-4Ch],eax     -- different for different rows

push        dword ptr ds:[4403528h]     -- a32.ctor( ... )
mov         edx,dword ptr ds:[4403524h]  
mov         ecx,dword ptr [ebp-4Ch]  
call        00790068                  

push        dword ptr [ebp-4Ch]       -- aa[32] = a32
mov         ecx,dword ptr [ebp-3Ch]  
mov         edx,20h  
call        71CCCD30  

这些更改无济于事:

// 1. explicit variable 
A a;
a = new A( ... );  // a32 = new A(...); a = a32;
aa[32] = a

// 2. limited visibility
{ aa[32] = new A( ... ); } // for each created object

此更改有帮助,但太难看了:

// Works in Release version only
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void f32( A[] aa ) 
    => aa[32] = new A( qwerty, asdf );

...
f32( aa );  -- inlined without a32

移除这些吞噬者有什么优雅的方法吗?

提前感谢。

c# optimization stack-overflow
1个回答
0
投票

coreclr存储库中存在未解决的问题,尚未解决:https://github.com/dotnet/coreclr/issues/14103。它于2017年开放,因此似乎不太可能很快修复它。 JIT编译器中存在一个问题,因为IL代码看起来不错(IL有其自己的堆栈,并且可以重用堆栈中的变量)。

为了保留美观的代码,我可以推荐2种方法:

  1. 创建子方法,例如Load_000000_001000Load_001000_002000,...,然后从Load调用它们。
  2. 将必要的数据保存在资源中,然后从资源中加载/创建阵列。您提到加载时间很关键,因此也许更快。 JIT不必编译大部分IL代码。
© www.soinside.com 2019 - 2024. All rights reserved.