2013-06-27 62 views
0

我有數以百萬計的類數據實例,我尋求優化建議。有很多實例優化場景的.NET小類?

有沒有辦法以任何方式對其進行優化 - 例如通過以某種方式序列化內存來節省內存,儘管它會損害檢索速度,這一點也很重要。也許把這個類變成結構體 - 但看起來這個類對於結構體非常大。

對這個對象的查詢每次可能需要數以億計的這些對象。他們坐在一個列表中並由DateTime查詢。結果以不同方式彙總,可以應用許多計算。

[Serializable] 
[DataContract] 
public abstract class BaseData {} 

[Serializable] 
public class Data : BaseData { 
    public byte  member1; 
    public int  member2; 
    public long  member3; 
    public double member4; 
    public DateTime member5; 
} 
+2

看起來也許你可以在這裏使用一個數據庫... –

+0

這是不是一種選擇,因爲太多的代碼已經編寫並集成在C# – taminov

回答

2

不幸的是,當你沒有指定要「優化」,你沒有指定具體是什麼問題,你的意思,以解決。所以我不能給你更多的一般建議。

序列化不會幫助你。您的Data對象已經以字節存儲在內存中。也不會將它變成結構化的幫助。結構和類之間的區別在於它們的尋址和引用行爲,而不是它們的內存佔用。

我能想到的減少具有「數億」這些對象的集合的內存佔用空間的唯一方法是序列化並壓縮整個事情。但這是不可行的。在訪問它之前,你總是必須解壓縮整個東西,這會使你的性能下降到實際上,實際上訪問內存消耗幾乎增加一倍(壓縮和解壓縮的數據都在內存中)。

我可以給你的最好的一般建議不是嘗試自己優化這個場景,而是使用專門的軟件。通過專門的軟件,我的意思是(內存)數據庫。您可以在內存中使用的數據庫的一個示例是,您在.NET框架中已經擁有了您需要的所有內容,其中包括SQLite

1

我認爲,正如你似乎暗示的那樣,你有一個擁有許多成員的類,擁有大量的實例,並且需要同時將它們全部保存在內存中以執行計算。

我跑了一些測試,看看你是否可以爲你描述的類實際獲得不同的大小。

我用這個簡單的方法用於查找內存大小的物體:

private static void MeasureMemory() 
{ 
    int size = 10000000; 
    object[] array = new object[size]; 

    long before = GC.GetTotalMemory(true); 
    for (int i = 0; i < size; i++)    
    { 
     array[i] = new Data(); 
    } 
    long after = GC.GetTotalMemory(true); 

    double diff = after - before; 

    Console.WriteLine("Total bytes: " + diff); 
    Console.WriteLine("Bytes per object: " + diff/size); 
} 

這可能是原始的,但我發現它工作正常,像這樣的情況。

正如所料,對該類幾乎沒有什麼(將其轉換爲結構體,刪除繼承或方法屬性)會影響單個實例使用的內存。就內存使用情況而言,它們都是等同的。但是,儘量擺弄實際的類並通過給定的代碼運行它們。

您實際上可以減少實例的內存佔用量的唯一方法是使用較小的結構來保存數據(例如,int代替long)。如果你有大量的布爾值,你可以將它們分組成一個字節或整數,並且有簡單的屬性包裝器來處理它們(一個布爾值需要一個字節的內存)。在大多數情況下,這些可能是微不足道的東西,但對於一億個對象,刪除布爾值可能會導致百MB內存的差異。此外,請注意,您爲應用程序選擇的平臺目標可能會影響對象的內存佔用空間(x64構建佔用更多內存,然後x86構建)。

序列化數據不太可能有幫助。內存數據庫具有優勢,特別是在執行復雜查詢時。但是,實際上不太可能減少數據的內存使用量。不幸的是,減少基本數據類型的佔用空間的方法並不多。在某些時候,你只需移動到基於文件的數據庫。

但是,這裏有一些想法。請注意,它們非常有條件,會降低計算性能,並會使代碼難以維護。

  1. 它往往是在不同狀態的對象將僅具有一些性質填充大型數據結構的情況下,和其他將被設置爲空或默認值。如果你可以識別這些屬性組,也許你可以將它們移動到一個子類中,並且有一個引用可以爲null,而不是有幾個屬性佔用空間。然後,只有在需要時纔會實例化子類。你可以編寫可以隱藏代碼其餘部分的屬性包裝器。請記住,這裏最糟糕的情況是您需要將所有屬性保存在內存中,再加上幾個對象頭和指針。

  2. 您可能會將可能採用默認值的成員轉換爲二進制表示形式,然後將它們打包到字節數組中。你會知道哪些字節位置表示哪些數據成員,並可以編寫可以讀取它們的屬性。將最有可能具有默認值的屬性定位在字節數組的末尾(例如幾個long,通常爲0)。然後,在創建對象時,調整字節數組大小以排除具有默認值的屬性,從列表末尾開始,直到您擊中具有非默認值的第一個成員。當外部代碼請求屬性時,可以檢查字節數組是否足夠大以容納該屬性,如果不是,則返回默認值。這樣,你可以節省一些空間。最好的情況是,你將有一個指向字節數組的空指針,而不是幾個數據成員。最糟糕的情況是,您將擁有完整的字節數組,佔用原始數據的空間,併爲數組增加一些開銷。實用性取決於實際數據,並且假定您做的寫操作相對較少,因爲重新計算數組將會很昂貴。

任何希望這有助於:)