记录在运行时在.net中确定的字段

时间:2010-09-20 22:54:29

标签: .net

我想将数百万条记录存入记忆中。记录具有无法在编译时确定的字段。字段可能有不同的类型,一些双打,一些整数,一些字符串等。由于我必须存储这么多的字段,我希望这些记录的内存中表示尽可能高效。

在C ++中,我将每个记录设置为一个固定大小的缓冲区,用于保存所有数据,并确定缓冲区中的读取位置以便将数据取出。在C#中,我不能那样做(可以吗?)。

这是怎么回事?使用ILGenerator在运行时构建结构?托管C ++?使用byte []?

数组

6 个答案:

答案 0 :(得分:1)

您可以使用发射IL生成动态类型。关于这种技术的非常好的文章是关于codeproject:http://www.codeproject.com/KB/cs/Creating_Dynamic_Types2.aspx

答案 1 :(得分:1)

我不确定这些限制是否会使其无法使用,但您可以在C#(不安全代码)中使用固定大小的缓冲区。见the MSDN docs

答案 2 :(得分:1)

这听起来像是你使用C / C ++联盟的东西。那是(如果我记得我的C):

union Thing
{
  int iThing;
  uint uThing;
  char * stringThing;
  double doubleThing;
};

它占用的内存量与其中定义的最大类型相同。所以在这里我猜它是8个字节(对于双倍)。

如果你知道事物的类型,你可以访问相应的字段:

Thing myThing = GetThing();
int i = myThing.iThing;  // if you know it's an int

你怎么知道它的类型取决于你。

无论如何,正如您现在可能知道的那样,在C#中没有联合这样的东西,但您可以使用System.Runtime.InteropServices中的StructLayout属性非常有效地模拟一个:

[StructLayout(LayoutKind.Explicit)]
struct Thing
{
    [FieldOffset(0)]
    int iThing;
    [FieldOffset(0)]
    uint uThing;
    [FieldOffset(0)]
    string stringThing;
    [FieldOffset(0)]
    double doubleThing;
}

你可以创建一个数组或List个数组,没问题。当然,这是一种值类型,因此您必须记住值类型语义。另请注意,尽管此结构的大小仅为8个字节(或者您存储的最大值类型的大小),但它包含对存储在堆上的字符串的引用。也就是说,字符串的成本是4个字节(64位中的8个)加上字符串本身的存储空间。

顺便提一下,有更有效的方法来存储字符串。如何存储它们取决于您是否要修改它们以及您需要多快地引用它们,但是您可以轻松地节省非常接近50%的空间,以便用英语和大多数西欧语言存储字符串

答案 3 :(得分:0)

使用List<List<object>>会使您的任务变得更加简单,除非我完全遗漏了某些内容。您可以根据需要进行的操作选择更好的收藏类型。

答案 4 :(得分:0)

你能使用泛型吗?我认为这是合理有效的。如:

class Record
{
    UntypedSpecialField f;
}

...

abstract class UntypedSpecialField f { }
class SpecialField<T> : UntypedSpecialField { }

...
List<Record> database;

这样做的好处是,如果存在与相关字段相关的代码共性,它们可以进入UntypedSpecialField,您可以获得结构良好的OO系统。

答案 5 :(得分:0)

我同意ChaosPandion:List&gt;可能是在.NET中使用的最简单,最有效的构造。单个列表&lt;&gt;可以容纳大约20亿条记录(int.MaxValue),或2GB(32位.NET中单个对象的最大数据大小)。由于记录本身是对其他对象的引用,因此每个元素都是一个4(或8)字节的IntPtr,使得外部List的最大大小约为5亿个元素(它使用引擎盖下的数组)。内部列表是大多数记录的结果,除非其中一个字段是BLOB-ish。

如果您告诉我们这些数据是如何被带入系统的,那么更好的答案会更容易给出。它来自文件吗?一个数据库?像气象站这样的外围设备的数据流?

说真的,我只是让托管运行时完成它的工作。甚至.NET中的数组在索引零之前也有一些开销,这与非托管C / C ++不同,其中数组只是一个块内存,其中&amp; array [0] =&amp; array。您几乎必须使用64位架构来处理您正在讨论的内存类型(在32位架构中,整个进程的内存使用量必须小于2GB,包括CLR,程序集,调用堆栈,对象开销和实际数据),当你处于64位时,内存受RAM和页面文件空间的限制;系统寻址存储器的能力比当前硬件高几个数量级。即使增加了托管对象的开销,您也应该有足够的空间。