views:

59

answers:

3

I have a small C# library with a static class that holds a very large, but simple four-dimensional byte array representing a multi-dimensional decision table (about 90K bytes in total).

There's one other data structure of note in there, a Dictionary which helps index into the first dimension of the decision table.

This decision table is a static private data member, initialized by array static initializers. The code is generated in Visual Studio 2010 with T4, from Excel documents.

Decisions are obtained via a static method that indexes into the multi-dimensional array.

When I run a simple test app using this library, it bombs with a "System.TypeLoadException: Internal limitation: too many fields." Exception, at the first invocation of the static decision method.

The one remotely related topic on Stackoverflow mentions libraries with "too many symbols". I may be wrong but my library seems to have very few symbols indeed.

What's going on here?

Code Snippet:

        private static byte[][][][] decisions = new byte[][][][] {
    new byte[][][] { 
    new byte[][] { 
    new byte[]{5,6,6},new byte[]{5,6,6},
    new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},
    new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},
    new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},
    new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},
    new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},
    new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},
    new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6}

    },
    new byte[][] { 
    new byte[]{5,6,6},new byte[]{5,6,6},
    new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},
    new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},
    new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},
    new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},
    new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},
    new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},
    new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6},new byte[]{5,6,6}

    },

...and so on...

A: 

Wow. Looks fun. I can't comment on that specific error (although I expect it is actually too many intermediate locals), but in something remarkably similar (codegen based decision engine) I populated the data via serialisation (binary in my case, but any should work). This meant a two step load (create obj, load from file) but it worked very well.

As an aside: My constructor was enough to make reflector explode in a messy pile (very terminal error) so I feel your pain.

Marc Gravell
+3  A: 

new byte[]{5,6,6}

The problem here is that the initializer {5, 6, 6} creates a static field. You can see it with Ildasm.exe. The CLR imposes a maximum of, I believe, 65535 fields in a class. Your auto-generated code is exceeding it.

You are going to have to do this differently. A file jumps to mind.

Hans Passant
I think encoding the data in a file is the best option. If you really want to prevent the user from modifying the data, you can embed the file as a resource.
Dan Bryant
I love Stackoverflow!! My goals are quick lookup and obscurity (decision data should be hard to reverse-engineer). A resource file, probably as a serialized object
Udi Bar-On
(cont) - is one way to go. Given the time I over-spent on this problem, I went with my initial code-gen approach, with a simplified array (see my answer below).
Udi Bar-On
A: 

You could try emitting the code in the static constructor explicitly and index directly in the sub-arrays rather than using array initializer syntax. This will prevent the creation of the static fields and instead encode all of the data setup into a massive blob of IL. I'm not sure if there are similar limits on the amount of IL that can go in a single method, but I'm sure you'll find out if you modify the T4 accordingly.

It would look like:

static MyClass()
{
    decisions = new byte[N1][][][];
    decisions[0] = new byte[N2][][];
    ....
    decisions[0][0][0][0] = 5;
    decisions[0][0][0][1] = 6;
    decisions[0][0][0][2] = 6;
    ...
}
Dan Bryant
Thanks, Dan. I'm marking your first answer as the accepted answer, because it identified the root of my problem! I ended up simplifying my array even more than you suggest, to a plain one-dimensional array. Given the code-gen approach I took, and assuming each dimension has a known size at code-gen time (which it does), the solutions are equivalent, IMO.
Udi Bar-On
Just noticed the answer I marked belongs to Hans. Sorry Hans and thanks to all!
Udi Bar-On