[英]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.Read
和TMemoryStream.Write
。
我所做的是MyEncripted[i]:=Chr(Ord(MyClear[i]) xor Ord(MyKey[i]));
使用 TMemoryStream.Write 逐字節邏輯。
我測量的時間:
注意:我沒有進一步計時,因為 55 小時真的很長,足以看到正在發生的事情; 但它看起來是“線性”的。
TMemoryStream 太慢了,以至於在使用 TMemoryStream.Write(逐字節邏輯)時,可以在 RAM 中花費不到一秒的時間完成的事情可能需要兩天以上的時間。
所以我很久以前就決定再也不使用任何 TMemoryStream 了。
希望這可以幫助理解 TMemoryStream 與直接 RAM 字符串變量的 SLOW 級別。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.