计算器上启动的大集合初始化器初始化、大集合、器上

2023-09-03 16:08:47 作者:心痛谁能懂

我要建,它使用比较大的表来工作的应用程序( LR表 ,为precise)。正如我产生code不管怎样,该表不是的是的大,我决定通过生成一个使用C#集合初始化语法上初始化表code连载我的表我生成的程序的启动:

 公共静态只读INT [,] gotoTable =新INT [,]
{
    {
        0,1,0,0,0,0,0,0,0,0,0,0,0,0,(...)
    },
    {
        0,0,4,0,5,6,0,0,0,0,0,7,0,0,(...)
    },
    (...)
 

奇怪的是,当我生成一个表,只有一对夫妇十万项,我产生的崩溃,在启动一个StackOverflowException的应用程序。 C#编译器编译它就好了;该表生成的应用程序也运行得很好。事实上,当我切换到发布模式,应用程序也启动。一个OutOfMemoryException可能已经取得了一定的意义,但即使这样,我用的是表的方式小,一个OutOfMemoryException。

code重现这样的:

警告的:尝试下面的code在发布模式崩溃的Visual Studio 2010为我;注意丢失未保存的工作。此外,如果您生成$ C $下,编译器生成大量的错误,Visual Studio将挂起也是如此。

  //发电项目,main.cs:
使用(StreamWriter的作家=新的StreamWriter(../../../ VictimProject / Tables.cs))
{
    writer.WriteLine(使用系统;);
    writer.WriteLine(公共静态类表);
    writer.WriteLine({);
    writer.WriteLine(公共静态只读元组LT; INT> [] bigArray =新的记录< INT> []);
    writer.WriteLine({);
    的for(int i = 0; I< 300000;我++)
        writer.WriteLine(新元组&其中; INT>(+ I +),);
    writer.WriteLine(};);
    writer.WriteLine(});
}
//受害人项目,main.cs:
的for(int i = 0; I< 1234;我++)
{
    // $ P $从去除Tables.bigArray pventing抖动
    如果(Tables.bigArray [I] .Item1 == 10)
        Console.WriteLine(找到了!);
}
Console.ReadKey(真正的);
 

竞选Tables.cs文件的第一个项目,然后第二个程序,以获得StackOverflowException。请注意,在我的电脑上崩溃:它可能无法在不同的平台等;尝试增加30万,如果它没有。

使用的调试模式释放模式,而不是好像稍微增加了极限,因为我的项目并不在释放模式崩溃。然而,code以上两种模式崩溃的我。

使用文字 INT s或字符串的S代替元组LT; INT> s不引起崩溃,也不会新INT()(但是,很可能被转换成文字0)。使用结构与单个 INT 字段的确实的导致崩溃。这似乎是与使用一个构造函数初始化。

我的猜测是,集合初始化器以某种方式递归实现,这可以解释堆栈溢出。然而,这是一个非常奇怪的事情作为一个迭代的解决方案似乎更加简单,更高效。 C#编译器本身不具有与该程序的任何问题,并对其进行编译速度非常快(它可以处理更大的集合好,但它崩溃的积极巨大的收藏品,如预期)。

我猜有可能是某种方式直接写我的表二进制文件,然后链接文件,但我还没有看那个呢。

我想我有两个问题:为什么上面的发生,以及如何解决它。

编辑:拆卸的.exe经过一些有趣的细节:

  .maxstack 4
.locals的init([0]类[mscorlib程序] System.Tuple`1< INT32> [] CS $ 0 $ 0000)
IL_0000:ldc.i4 0x493e0
IL_0005:newarr类[mscorlib程序] System.Tuple`1< INT32>
IL_000a:stloc.0
IL_000b:ldloc.0
IL_000c:ldc.i4.0
IL_000d:ldc.i4.0
IL_000e:newobj例如空类[mscorlib程序] System.Tuple`1< INT32> ::构造函数(!0)
IL_0013:stelem.ref
IL_0014:ldloc.0
IL_0015:ldc.i4.1
IL_0016:ldc.i4.1
IL_0017:newobj例如空类[mscorlib程序] System.Tuple`1< INT32> ::构造函数(!0)
IL_001c:stelem.ref
(推移和)
 
小明计算器 科学型计算器 V3.4免费版下载

这表明,抖动确实有一个堆栈溢出试图JIT这种方法崩溃。不过,这是奇怪的是它,特别是,我得到一个异常出来。

解决方案   

为什么上面发生

我怀疑它可能是JIT崩溃。您将产生的巨大的类型初始化(.cctor在IL成员)。每个值将是5 IL指令。我并不完全感到惊讶1.5亿条指令的成员造成问题...

  

和我怎么解决它?

包括数据到一个嵌入的资源文件来代替,并加载它在类型初始化,如果您需要。我假设这是生成的数据 - 所以把它所属的数据,在一个二进制文件,而不是字面code

I'm building an application which uses relatively large tables to do its work (LR tables, to be precise). As I'm generating code anyway and the table isn't that large, I decided to serialize my table by generating code that uses the C# collection initializer syntax to initialize the table on startup of my generated program:

public static readonly int[,] gotoTable = new int[,]
{
    {
        0,1,0,0,0,0,0,0,0,0,0,0,0,0,(...)
    },
    {
        0,0,4,0,5,6,0,0,0,0,0,7,0,0,(...)
    },
    (...)

Oddly enough, when I generated a table that had only a couple hundred thousand entries, the application that I generated crashes with a StackOverflowException on startup. The C# compiler compiles it just fine; the table generation application also runs just fine. In fact, when I switched to Release mode, the application did start up. An OutOfMemoryException might have made some sense, but even then the table I use is way to small for an OutOfMemoryException.

Code to reproduce this:

Warning: trying the code below in release mode crashed Visual Studio 2010 for me; watch out for losing unsaved work. Additionally, if you generate code for which the compiler generates lots of errors, Visual Studio will hang as well.

//Generation Project, main.cs:
using (StreamWriter writer = new StreamWriter("../../../VictimProject/Tables.cs"))
{
    writer.WriteLine("using System;");
    writer.WriteLine("public static class Tables");
    writer.WriteLine("{");
    writer.WriteLine("    public static readonly Tuple<int>[] bigArray = new Tuple<int>[]");
    writer.WriteLine("    {");
    for (int i = 0; i < 300000; i++)
        writer.WriteLine("        new Tuple<int>(" + i + "),");
    writer.WriteLine("    };");
    writer.WriteLine("}");
}
//Victim Project, main.cs:
for (int i = 0; i < 1234; i++)
{
    // Preventing the jitter from removing Tables.bigArray
    if (Tables.bigArray[i].Item1 == 10)
        Console.WriteLine("Found it!");
}
Console.ReadKey(true);

Run the first project for the Tables.cs file, and then the second program to get the StackOverflowException. Note that the above crashes on my computer: it might not on different platforms etc; try increasing 300000 if it doesn't.

Using release mode instead of debug mode seems to increase the limit slightly, as my project doesn't crash in release mode. However, the code above crashes in both modes for me.

Using literal ints or strings instead of Tuple<int>s doesn't cause the crash, nor does "new int()" (but that might get converted into a literal 0). Using a struct with a single int field does cause the crash. It seems to be related to using a constructor as initializer.

My guess is that the collection initializer is somehow implemented recursively, which would explain the stack overflow. However, that is a very weird thing to do as an iterative solutions seems a lot simpler and more efficient. The C# compiler itself doesn't have any problems with the program and compiles it very fast (it handles even larger collections well, but it does crash on positively huge collections, as expected).

I guess there's probably some way to write my table directly to a binary file and then link that file, but I haven't looked at that yet.

I guess I have two questions: why does the above happen, and how do I work around it?

Edit: some interesting details after disassembling the .exe:

.maxstack  4
.locals init ([0] class [mscorlib]System.Tuple`1<int32>[] CS$0$0000)
IL_0000:  ldc.i4     0x493e0
IL_0005:  newarr     class [mscorlib]System.Tuple`1<int32>
IL_000a:  stloc.0
IL_000b:  ldloc.0
IL_000c:  ldc.i4.0
IL_000d:  ldc.i4.0
IL_000e:  newobj     instance void class [mscorlib]System.Tuple`1<int32>::.ctor(!0)
IL_0013:  stelem.ref
IL_0014:  ldloc.0
IL_0015:  ldc.i4.1
IL_0016:  ldc.i4.1
IL_0017:  newobj     instance void class [mscorlib]System.Tuple`1<int32>::.ctor(!0)
IL_001c:  stelem.ref
(goes on and on)

This suggests that the jitter indeed crashes with a stack overflow trying to jit this method. Still, it's weird that it does, and in particular, that I get an exception out of it.

解决方案

why does the above happen

I suspect it may be the JIT crashing. You will be generating an enormous type initializer (.cctor member in IL). Each value is going to be 5 IL instructions. I'm not entirely surprised a member with 1.5 million instructions causes problems...

and how do I work around it?

Include the data into an embedded resource file instead, and load it in the type initializer if you need to. I'm assuming this is generated data - so put data where it belongs, in a binary file rather than as literal code.

 
精彩推荐
图片推荐