2017-09-21 16 views
1

我需要在處理大型數組時使用物理內存。在我目前的代碼版本我爲每個數據類型定義的類像這樣針對不同數據類型的大型陣列的低內存佔用空間

TMyData_01 = class 
     Const ID=$0001; 
     public 
     Data : Integer; 
     end; 

TMyData_02 = class 
     Const ID=$0002; 
     public 
     Data : String; 
     end; 
TMyData_03 = class 
     Const ID=$0003; 
     public 
     Data : TDateTime; 
     Index : Integer; 
     end; 
TMyData_04 = class 
    Const ID=$0004; 
    public 
    Data : TDateTime; 
    Value : Real; 
    end; 
    ... 

我有〜50種不同類基本類型和這些類的一些組合。

MyCombinedData = class 
     Const ID=$0002; 
     public 
     Data_1 : TMyData_01; 
     Data_2 : TMyData_02; 
     Data_3 : TMyData_03; 
     Data_4 : TMyData_04; 
     end; 

在當前的代碼版本,我可以閱讀和我的數據寫入到這麼多的類類型和運行所有這些類的列表,列表/陣列內的項目。

但現在我運行在內存不足的問題,因爲我的方法是無效的 因爲每個類類型消耗16字節的訪問+數據本身的額外字節。

而且如果在一個組合的等級並不是所有的子類與數據 充滿了我這個靜態appoach創造了很多不用途不同使用的內存空間。

  • MyCombinedData [1]具有在子類數據1,2,3
  • MyCombinedData [2]具有在子類數據2,3
  • MyCombinedData [3]在子類1,2,3數據, 4
  • MyCombinedData [4]具有在子類2,3,4-
  • 數據..........

在Delphi任何更好的方法來存儲不同的數據以較低的存儲器腳打印?

+0

很難給出具體的建議。這些問題通常需要算法方法。有時候數據結構設計。有時需要重新設計整個應用。有時候足以讓應用程序能夠識別大地址。也許64位解決了這個問題。我們甚至不知道這個問題。甚至可能是內存碎片。除非你瞭解它們,否則不要試圖解決問題。 –

+0

如果負載與開銷相比非常大,那麼您可能會出錯。 –

+0

我正在查找一個數據容器,只是將INDEX和CLASS_TYPEID和字節傳遞到此存儲區,使用容器中的INDEX回讀數據作爲字節和TYPEID。基於TYPEID我知道如何將這些數據轉換爲正確的數據格式(字符串,INT,Real,dateTime,......)或其他任何有效的解決方案(delphi中針對此問題的任何現代語言功能) – user1769184

回答

2

你可以做這樣的事情(如低內存佔用率超過代碼簡潔更重要):

const 
    MaxBufferSize = 16; // the largest size you actually need, in this example sizeof(TMyData_04)... 

type 
    TMyData = packed record 
    TypeID: Byte; 
    Buffer: array[0..MaxBufferSize-1] of Byte; 
    end; 

    PMyData_01 = ^TMyData_01; 
    TMyData_01 = packed record 
    Data: Integer; 
    end; 

    PMyData_02 = ^TMyData_02; 
    TMyData_02 = packed record 
    Data: String; 
    end; 

    PMyData_03 = ^TMyData_03; 
    TMyData_03 = packed record 
    Data: TDateTime; 
    Index: Integer; 
    end; 

    PMyData_04 = ^TMyData_04; 
    TMyData_04 = packed record 
    Data: TDateTime; 
    Value: Real; 
    end; 

    ... 

然後,您可以分配TMyData實例的數組到需要的長度,並呼籲System.Initialize()初始化

var 
    Arr: array of TMyData; 

SetLength(Arr, ...); 

//... 

Arr[Index].TypeID := $01; 
System.Initialize(PMyData_01(@Arr[Index].Buffer)^); 
// populate PMyData_01(@Arr[Index].Buffer)^ fields as needed... 

// and so on ... 

不要忘記調用上的項目System.Finalize()重新分配前陣,以避免任何內存泄漏:

包含編譯器管理的數據類型的任何項目
var 
    I: Integer; 
begin 
    for I := Low(Arr) to High(Arr) do 
    begin 
    case Arr[I].TypeID of 
     $01: System.Finalize(PMyData_01(@Arr[i].Buffer)^); 
     $02: System.Finalize(PMyData_02(@Arr[i].Buffer)^); 
     $03: System.Finalize(PMyData_03(@Arr[i].Buffer)^); 
     $04: System.Finalize(PMyData_04(@Arr[i].Buffer)^); 
     // and so on ... 
    end; 
    end; 
end; 

不理想,但它是功能...


泛型掛羊頭賣狗肉的一點點,你可以清理一下代碼:

const 
    MaxBufferSize = 16; // the largest size you actually need, in this example sizeof(TMyData_04)... 

type 
    TMyDataHelper<T: record> = record 
    type PtrType = ^T; 
    class function GetDataTypeID: Byte; static; 
    end; 

    TMyData = packed record 
    TypeID: Byte; 
    Buffer: array[0..MaxBufferSize-1] of Byte; 
    procedure InitializeBuffer<T: record>; 
    procedure FinalizeBuffer; 
    procedure SetBufferData<T: record>(const NewData: T); 
    function BufferAs<T: record>: TMyDataHelper<T>.PtrType; 
    end; 

    TMyData_01 = packed record 
    Data: Integer; 
    end; 

    TMyData_02 = packed record 
    Data: String; 
    end; 

    TMyData_03 = packed record 
    Data: TDateTime; 
    Index: Integer; 
    end; 

    TMyData_04 = packed record 
    Data: TDateTime; 
    Value: Real; 
    end; 

    // and so on ... 

class function TMyDataHelper<T>.GetDataTypeID: Byte; 
begin 
    if TypeInfo(T) = TypeInfo(TMyData_01) then 
    Result := $01 
    else 
    if TypeInfo(T) = TypeInfo(TMyData_02) then 
    Result := $02 
    else 
    if TypeInfo(T) = TypeInfo(TMyData_03) then 
    Result := $03 
    else 
    if TypeInfo(T) = TypeInfo(TMyData_04) then 
    Result := $04 
    // and so on ... 
    else 
    Result := $00; 
end; 

procedure TMyData.InitializeBuffer<T>; 
var 
    LTypeID: Byte; 
begin 
    LTypeID := TMyDataHelper<T>.GetDataTypeID; 
    if TypeID <> LTypeID then 
    begin 
    FinalizeBuffer; 
    System.Initialize(BufferAs<T>^); 
    TypeID := LTypeID; 
    end; 
end; 

procedure TMyData.FinalizeBuffer; 
begin 
    case TypeID of 
    $01: Finalize(BufferAs<TMyData_01>^); 
    $02: Finalize(BufferAs<TMyData_02>^); 
    $03: Finalize(BufferAs<TMyData_03>^); 
    $04: Finalize(BufferAs<TMyData_04>^); 
    // and so on ... 
    else 
    FillChar(Buffer, SizeOf(Buffer), $00); 
    end; 
end; 

procedure TMyData.SetBufferData<T>(const NewData: T); 
begin 
    InitializeBuffer<T>; 
    BufferAs<T>^ := NewData; 
end; 

function TMyData.BufferAs<T>: TMyDataHelper<T>.PtrType; 
begin 
    Result := TMyDataHelper<T>.PtrType(@Buffer); 
end; 

var 
    Arr: array of TMyData; 

SetLength(Arr, ...); 
FillChar(Arr[0], Length(Arr)*SizeOf(TMyData), $0); 

//... 

Arr[Index].InitializeBuffer<TMyData_01>; 
populate Arr[Index].BufferAs<TMyData_01>^ fields as needed... 
or: 
Arr[Index].SetBufferData<TMyData_01>(...); 

// and so on ... 

var 
    I: Integer; 
begin 
    for I := Low(Arr) to High(Arr) do 
    Arr[I].FinalizeBuffer; 
end;