簡體   English   中英

有什么方法可以加快 TPNGImage 上的 SaveToStream 嗎?

[英]Any way to speed up SaveToStream on TPNGImage?

我有一個函數可以將 TBitmap(我繪制的)轉換為 TPngImage,然后將其保存到流中,以便其他方法可以使用它。 使用 Png 是因為它為報告輸出(excel、html)創建了較小的圖像。 問題是 SaveToStream 似乎花費了太多時間,比將 TBitmap 轉換為 TPngImage 或將 TStream 與 png 一起使用要多 15 倍。 這是代碼:

var
 BitmapImage: TBitmap;      
 PNGImage: TPngImage;
 PngStream: TStream;        
begin
  // draw on BitmapImage
  ...
  PNGImage := TPngImage.Create;
  PNGStream := TMemoryStream.Create;
  Try
     PNGImage.Assign(BitmapPicture.Bitmap); // Step 1: assign TBitmap to PNG
     PNGImage.SaveToStream(PNGStream);  // Step 2: save PNG to stream
     WS.Shapes.AddPicture(PNGStream,PNGImage.Width,PNGImage.Height); // Step 3: Add PNG from Stream to Excel
  finally
     PNGImage.Free;
     PNGStream.Free;
  end;
...

這是使用 70000 張圖像進行測試的,時間如下:
第 1 步:7 秒

第 2 步:93 秒

第 3 步:6 秒

為什么保存到 Stream 這么慢? 有什么建議可以優化嗎?

使用德爾福 XE7

編輯

這是帶有簡單 bmp 的示例(MCVE),它被轉換為 PNG,然后保存到流中。 只是為了再次驗證,我添加了 SaveToFile,這當然需要更長的時間,但它正在保存到磁盤,所以我認為可以接受。

img1.bmp 為 49.5KB,保存的 PNG 為 661 字節。 鏈接到 img1.bmp = http://www.filedropper.com/img1_1

TMemoryStreamAccess = class(TMemoryStream)
  end;

procedure TForm1.Button1Click(Sender: TObject);
var BitmapImage:TBitmap;
  PNGImage:TPngImage;
  PNGStream:TMemoryStream;//TStream;
  i,t1,t2,t3,t4,t5,t6: Integer;
  vFileName:string;
begin

  BitmapImage:=TBitmap.Create;
  BitmapImage.LoadFromFile('c:\tmp\img1.bmp');

  t1:=0; t2:=0; t3:=0; t4:=0; t5:=0; t6:=0;

  for i := 1 to 70000 do
  begin

    PNGImage:=TPngImage.Create;
    PNGStream:=TMemoryStream.Create;
    try

      t1:=GetTickCount;
      PNGImage.Assign(BitmapImage);
      t2:=t2+GetTickCount-t1;

      t3:=GetTickCount;
      TMemoryStreamAccess(PNGStream).Capacity := 1000;
      PNGImage.SaveToStream(PNGStream);
      // BitmapImage.SaveToStream(PNGStream); <-- very fast!
      t4:=t4+GetTickCount-t3;

    finally
      PNGImage.Free;
      PNGstream.Free
    end;

  end;

   showmessage('Assign = '+inttostr(t2)+' - SaveToStream = '+inttostr(t4));
end;

這是使用 70000 張圖像進行測試的,時間如下:

第 1 步:7 秒

第 2 步:93 秒

第 3 步:6 秒

為什么保存到 Stream 這么慢?

讓我們計算一些數字:

第 1 步:7 秒 = 7000 毫秒。 7000 / 70000 = 0.1ms 每張圖片

第 2 步:93 秒 = 93000 毫秒。 93000 / 70000 = 每張圖像約 1.33 毫秒

第 3 步:6 秒 = 6000 毫秒。 6000 / 70000 = 每張圖像約 0.086 毫秒

你認為每個SaveToStream() 1.33 毫秒很慢嗎? 你只是做了很多,所以它們會隨着時間的推移而增加,僅此而已。

話雖如此,內存中的 PNG 數據沒有被壓縮。 保存數據時它會被壓縮。 所以這是放緩的原因之一。 此外,保存 PNG 會對流進行大量寫入,這可能會導致流執行多次內存(重新)分配( TPNGImage在保存期間也會執行內部內存分配),因此這是另一個減慢速度。

有什么建議可以優化嗎?

對於壓縮開銷,您無能為力,但您至少可以在調用SaveToStream()之前將TMemoryStream.Capacity預設為合理的值,以減少TMemoryStream在寫入期間需要執行的內存重新分配。 你不需要精確地處理它。 如果寫入流導致其Size超過其當前的Capacity ,它將相應地增加其Capacity 由於您已經處理了 70000 張圖像,因此取它們的平均大小並添加更多 KB,並將其用作您的初始Capacity

type
  TMemoryStreamAccess = class(TMemoryStream)
  end;

var
  BitmapImage: TBitmap;      
  PNGImage: TPngImage;
  PngStream: TMemoryStream;        
begin
  // draw on BitmapImage
  ...
  PNGImage := TPngImage.Create;
  Try
    PNGImage.Assign(BitmapPicture.Bitmap); // Step 1: assign TBitmap to PNG
    PNGStream := TMemoryStream.Create;
    try
      TMemoryStreamAccess(PNGStream).Capacity := ...; // some reasonable value
      PNGImage.SaveToStream(PNGStream);  // Step 2: save PNG to stream
      WS.Shapes.AddPicture(PNGStream, PNGImage.Width, PNGImage.Height); // Step 3: Add PNG from Stream to Excel
    finally
      PNGStream.Free;
    end;
  finally
    PNGImage.Free;
  end;
  ...

如果這對您來說仍然不夠快,請考慮使用線程並行處理多個圖像。 不要按順序處理它們。

您是否指定了壓縮級別? 我沒有注意到類似的東西

PNGImage.CompressionLevel := 1;

在你的代碼中。 它可以在 0 到 9 的范圍內。默認情況下,它是 7。如果將其設置為 1,它會明顯更快,而輸出流大小的增加可以忽略不計。

也許沒有直接關系,但我遇到了讀/寫(在 RAM 中)的問題,因為我使用的是TMemoryStream (逐字節)。

在我的情況下,我可以使用SetLength(MyEncripted,TheSize); & SetLength(MyClear,TheSize); 而不是TMemoryStream.ReadTMemoryStream.Write

我所做的是MyEncripted[i]:=Chr(Ord(MyClear[i]) xor Ord(MyKey[i])); 使用 TMemoryStream.Write 逐字節邏輯。

我測量的時間:

  • 使用 TMemoryStream 獲得 633 KiloBytes => 3'21"
  • 使用 633 KiloBytes 的 RAM 字符串類型變量 => 接近瞬時,小於 0.1"
  • 使用 TMemoryStream 獲得 6.3 兆字節 => 33'30"
  • 使用 6.3 兆字節的 RAM 字符串類型變量 => 接近瞬時,小於 0.1"
  • 將 TMemoryStream 用於 63 兆字節 => 5h35'00"
  • 使用 63 兆字節的 RAM 字符串類型變量 => 接近瞬時,小於 0.1"
  • 將 TMemoryStream 用於 633 兆字節 => 55h50'00"
  • 使用 633 兆字節的 RAM 字符串類型變量 => 小於 1"

注意:我沒有進一步計時,因為 55 小時真的很長,足以看到正在發生的事情; 但它看起來是“線性”的。

TMemoryStream 太慢了,以至於在使用 TMemoryStream.Write(逐字節邏輯)時,可以在 RAM 中花費不到一秒的時間完成的事情可能需要兩天以上的時間。

所以我很久以前就決定再也不使用任何 TMemoryStream 了。

希望這可以幫助理解 TMemoryStream 與直接 RAM 字符串變量的 SLOW 級別。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM