我有一个函数可以将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个图像测试的,下面是时间:
第一步:7秒
第二步:93秒
第三步:6秒
为什么保存到流这么慢?有什么优化建议吗?
使用 Delphi XE7
编辑
下面是一个简单的bmp转换为PNG然后保存到流中的例子(MCVE)。为了另一个验证,我添加了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;
3条答案
按热度按时间mitkmikd1#
这是用70000个图像测试的,下面是时间:
第一步:7秒
第二步:93秒
第三步:6秒
为什么保存到Stream这么慢?
让我们来分析一下数字:
第1步:7秒= 7000毫秒。7000 / 70000 =每张图像0.1毫秒
第2步:93秒= 93000毫秒。93000 / 70000 =每张图像约1.33毫秒
第3步:6秒= 6000毫秒。6000 / 70000 =每张图像约0.086毫秒
你认为1.33毫秒每
SaveToStream()
是慢的吗?你只是做了很多,所以它们会随着时间的推移而增加,仅此而已。也就是说,内存中的PNG数据没有被压缩。当数据被保存时,它被压缩了。所以这是速度变慢的一个原因。而且,保存PNG会对流进行大量的写入,这会导致流执行多次内存(重新)分配(
TPNGImage
在保存过程中也会执行内部内存分配),所以这是另一个速度变慢的原因。有什么优化建议吗?
对于压缩开销您无能为力,但至少可以在调用
SaveToStream()
之前将TMemoryStream.Capacity
预先设置为一个合理的值,以减少TMemoryStream
在写入过程中需要执行的内存重新分配。您不需要对它进行精确的设置。如果写入流导致其Size
超过其当前的Capacity
,它只会相应地增加它的Capacity
。既然你已经处理了70000张图片,取它们的平均大小,再增加几个KB,然后用它作为你的初始Capacity
。如果这对你来说还不够快,考虑使用线程来并行处理多个图像,不要按顺序处理它们。
hlswsv352#
你指定压缩级别了吗?我没有注意到
它可以在0到9的范围内。默认情况下,它是7。如果将它设置为1,则速度会明显加快,而输出流大小的增加将可以忽略不计。
bjp0bcyl3#
也许不是直接相关的,但我有一个问题,读/写(在RAM中),因为我使用的是
TMemoryStream
(逐字节)。在我的情况下,我可以调整我的代码,使其在纯RAM中工作,使用
SetLength(MyEncripted,TheSize);
和SetLength(MyClear,TheSize);
,而不是TMemoryStream.Read
和TMemoryStream.Write
。我所做的是一个简单的“概念”(可以用纯RAM字符串变量来完成),用一个TMemoryStream来写
MyEncripted[i]:=Chr(Ord(MyClear[i]) xor Ord(MyKey[i]));
。我的测量时间:
注:我没有进一步的时间,因为55小时是真的很大,足以看到发生了什么;但它看起来是线性的。
TMemoryStream太慢了,太慢了,在RAM中不到一秒钟就能完成的事情,在使用TMemoryStream时可能要花两天以上的时间。写入(以逐字节逻辑)。
所以我很久以前就决定再也不使用任何TMemoryStream了。
希望这能帮助理解TMemoryStream与直接RAM String变量相比是什么级别的SLOW。