2010-07-17 39 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天數進入一個時代