2010-07-17 49 views
1

我有一个应用程序为了性能方面的原因在内存中保存了大量实例,而我不想想将其写入到磁盘或任何其他把它放在内存中。优化具有大量实例的.NET应用程序的内存占用量

public class MyObject 
{ 
    public string Name; 
    public object Tag; 
    public DateTime DateTime1; 
    public DateTime DateTime2; 
    public DateTime DateTime3; 
    public long Num1; 
    public uint Num2; 
    public uint Num3; 
    public ushort Num4; 
} 

在很多情况下,我实际上并没有使用所有的字段,也没有充分利用字段的整体大小。所以我想也许把这个整个类转移到一个带有属性的接口中,并创建许多实现类,以不同的方式存储数据:使用较小的字段(例如int而不是long)并省略一些未使用的字段。

例如:

public interface IMyObject 
{ 
    string Name { get; set; } 
    object Tag { get; set; } 
    DateTime DateTime1 { get; set; } 
    DateTime DateTime2 { get; set; } 
    DateTime DateTime3 { get; set; } 
    long Num1 { get; set; } 
    uint Num2 { get; set; } 
    uint Num3 { get; set; } 
    ushort Num4 { get; set; } 
} 

public class MyObject1 : IMyObject 
{ 
    public string Name { get; set; } 
    public object Tag { get; set; } 
    public DateTime DateTime1 { get; set; } 
    public DateTime DateTime2 { get; set; } 
    public DateTime DateTime3 { get; set; } 
    public long Num1 { get; set; } 
    public uint Num2 { get; set; } 
    public uint Num3 { get; set; } 
    public ushort Num4 { get; set; } 
} 

public class MyObject2 : IMyObject 
{ 
    private int _num1; 

    public string Name { get; set; } 
    public object Tag { get; set; } 
    public DateTime DateTime1 { get; set; } 
    public DateTime DateTime2 { get; set; } 
    public DateTime DateTime3 { get; set; } 
    public long Num1 
    { 
     get { return _num1; } 
     set { _num1 = (int)value; } 
    } 
    public uint Num2 { get; set; } 
    public uint Num3 { get; set; } 
    public ushort Num4 { get; set; } 
} 

public class MyObject3 : IMyObject 
{ 
    public string Name { get; set; } 
    public object Tag { get; set; } 
    public DateTime DateTime1 
    { 
     get { return DateTime.MinValue; } 
     set { throw new NotSupportedException(); } 
    } 
    public DateTime DateTime2 { get; set; } 
    public DateTime DateTime3 { get; set; } 
    public long Num1 { get; set; } 
    public uint Num2 { get; set; } 
    public uint Num3 { get; set; } 
    public ushort Num4 { get; set; } 
} 

// ... 

从理论上讲,这种方法其实我可以减少内存占用量,但实际上你看,这种做法的问题是,它会导致与较小的所有病例笛卡尔乘积省略了字段,以后编写后不能维护的丑陋和大代码。

约琴弦另一种思考:

在UTF-16编码表示的.NET应用程序的所有字符串。如果我只能使它以UTF-8编码,它会减少字符串使用的内存的x2倍。

+0

这是有点含糊不清说明。你能否更详细地解释你的情况?你的应用程序在做什么?为什么它需要使用那么多的内存? – 2010-07-17 10:02:24

+0

所有对象都是唯一的还是有很多重复项(包含相同数据的单独实例)? – 2010-07-17 10:03:37

+0

没有重复。 – DxCK 2010-07-17 10:05:54

回答

0

怎么样使用System.Tuple? 您可以动态指定要使用的字段。

编辑:
我肯定看看String实习。

此外,还有System.Dynamic.ExpandoObject

+0

我需要一个标准的接口来访问所有的字段。元组可以提供吗? – DxCK 2010-07-17 10:17:19

+0

定义你的标准接口的含义? – 2010-07-17 10:22:54

+0

请参阅问题中的接口IMyObject。我想要一个数组,IMyObject [] – DxCK 2010-07-17 10:31:04

1

从看您的个人资料,我要采取平底船和猜测的“名称”属性,其实是一个文件路径。如果空间比时间更重要,那么您可以使用编码方案来表示路径,其中可能存在大量重复数据。

将文件路径表示为一个Path,它是一个ints数组,并且FileName是一个字符串和实际的文件名(这可能更独特,因此不值得编码)。您可以将路径拆分为其组成部分,然后使用几个字典存储正向和反向查找。通过这种方式,您可以减少一个输入数组的路径。比字符串小得多。在UTF8

1

存储字符串:

byte[] asciiStr = System.Text.Encoding.UTF8.GetBytes("asdf"); 

string text = System.Text.Encoding.UTF8.GetString(asciiStr); 

(编辑:思运希望在第一个ASCII)

理念1: 如果您希望大部分值将不会在大多数填写这个时候,你可以将每个字段存储在某种单独的键值查找数据结构中 - 一个字典,一个带二进制搜索的有序列表,一个二叉树等等。一个有二进制搜索的有序列表可能是最多的空间有效的,尽管查找会是O(log n)。

因此,而不是为MyObject []对象,你就必须

Dictionary<int, string> names; // or List<Tuple<int,string>> names; 
Dictionary<int, object> tags; 
Dictionary<int, DateTime> datetime1s; 
... 

其中每个值INT关键条目的ID。

理念2: 如果你相信,这些DateTime是否是一个合理的范围小(约30岁),说2010年1月内,你可以将其转换为32位的int值,表示多少秒这是自那以后的日期。这将削减每个DateTime 4个字节。

理念3: 你可能会考虑一个非常节省空间的序列化方案,其中,每个字段的第一个字节中所指定的后续字节召开类别的字段。字符串值可能只是用\ n或其他东西分隔。将这整个事物存储在一个字节数组中,并按需将其反序列化。

所以这样的事情,没有空格,而且在二进制值酌情:

1 //indicates field 1 (Name) 

beck.asf\n //the value 

6 //indicates field 6 (Num1) 

3545623 //the value, in a 64-bit binary int 

如果代码是指活的对象,则可能需要随便扔在包装结构分别系列化外。或者,就像第一个想法一样,您可以只存储一个int,标识标签,然后使用List> outside来保存对标签的实际引用。

+0

关于“将字符串存储为ASCII” - OP实际上希望将它们存储为UTF-8而不是默认的UTF-16。 – stakx 2010-07-17 21:41:51

+0

哎呦,我的坏。 – 2010-07-17 21:57:13

2

思考:

  • 任何字符串的共享?您可以在加载数据时使用自定义interner,以确保没有这些数据是重复的(注意:不要使用内置的 interner,因为您会使其饱和;即使是Dictonary<string,string>也会这样做)
  • 有没有其他常见的元素,可能明智地可能是重复的,并可能被移入对象?您仍然有参考字段的成本,但希望这个(和新对象)是一个净增益
  • 如果您有大量相似的实体,他们中的任何一个都可以被建模为不可变的值类型?这通常不是我的首选,但随后的好处是,你可以在一个阵列他们坚持:
    • 你获得一个对象引用的价格和每个实体对象头
    • 可以使用在数组中的偏移(int)而不是引用;对于64位,这是一个体面的保存时加起来
  • 你似乎建议一个稀疏的对象方法;实际上你想避免笛卡尔产品,但同时对于你描述的成员数量较少一个财产袋可能是更多在内存中昂贵;加上自从你提到你是为了表现而做这件事,我怀疑它也会伤害到CPU
  • DateTime s - 他们是否(例如)总是整天?你会很惊讶什么,你可以通过使用int天数进入一个时代