zlib最快的压缩/解压写法 |
|
mustapha.wang
資深會員 發表:89 回覆:409 積分:274 註冊:2002-03-13 發送簡訊給我 |
首先说明,这里不是横向比较zlib与别的引擎(rar,leo,powerarc...),是探索如何发挥zlib压缩/解压的最大效率。
先看看如下代码在效率上的差异:
var MS:TMemoryStream; (1): begin MS:=TMemoryStream.Create; MS.Size:=$400000;//4M ------------------------------------------------ (2): var i:integer; begin MS:=TMemoryStream.Create; for i:=1 to 1024 do MS.Size:=MS.Size 4096;你会发现,方法(1)只要1个毫秒,方法(2)却要20秒。 因此,如果把解压缩程序写成下面这样,会非常没有效率: procedure ZlibDeCompress(instream,outStream:TStream); var ACS:TDeCompressionStream; buf:array[1..4096] of byte; numread:integer; begin inStream.Position:=0; ACS:=TDeCompressionStream.Create(inStream); try repeat numRead:=ACS.Read(buf,sizeof(buf)); if numread>0 then outStream.Write(buf,numRead); until (numRead=0); finally ACS.Free; end; end;如果我们知道原始资料的大小,一次确定outStream.Size,效率就可以提高几十倍。方法很简单,我们可以在压缩时,把原始资料的Size写在压缩Stream的头部,如,写一个LongWord的大小,解压时就可以先读出Size,因此,最有效率的解压程序为: procedure ZlibDecompressStream2(Source,Dest:TMemoryStream); var zstream: TZStreamRec; SourceLen,DestLen:LongWord; begin FillChar(zstream,SizeOf(TZStreamRec),0); SourceLen:=Source.Size; Source.Position:=0; Source.Read(DestLen,SizeOf(LongWord)); Dest.Size:=DestLen; zstream.next_in:=Pointer(LongWord(Source.Memory) SizeOf(LongWord)); zstream.avail_in:=SourceLen-SizeOf(LongWord); zstream.next_out:=Dest.Memory; zstream.avail_out:=DestLen; ZDecompressCheck(InflateInit(zstream)); try ZDecompressCheck(inflate(zstream,Z_NO_FLUSH)); finally ZDecompressCheck(inflateEnd(zstream)); end; end;用一个4M的文件试试,效率提高近70倍。 同样道理,在压缩的时候,如果能预先知道压缩后的大小,也能提高效率不少,但这似乎是不可能的,也不能盲目的给outStream.Size一个"足够大"的数值,只能按引擎的原理估算一个最接近的数值,zlib推荐的为: ((SourceLen (SourceLen div 10) 12) 255) and not 255 因此,最有效率的压缩程序为: procedure ZlibCompressStream2(Source,Dest:TMemoryStream; CompressLevel:TZCompressionLevel=zcFastest); var zstream: TZStreamRec; SourceLen,DestLen:LongWord; begin FillChar(zstream,SizeOf(TZStreamRec),0); SourceLen:=Source.Size; DestLen:=SizeOf(LongWord) ((SourceLen (SourceLen div 10) 12) 255) and not 255; Dest.Size:=DestLen; Dest.Position:=0; Dest.Write(SourceLen,Sizeof(LongWord)); zstream.next_in:=Source.Memory; zstream.avail_in:=SourceLen; zstream.next_out:=Pointer(LongWord(Dest.Memory) SizeOf(LongWord)); zstream.avail_out:=DestLen-SizeOf(longWord); ZCompressCheck(DeflateInit(zstream,ZLevels[CompressLevel])); try ZCompressCheck(deflate(zstream,Z_FINISH)); finally ZCompressCheck(deflateEnd(zstream)); end; Dest.Size:=zstream.total_out SizeOf(LongWord); end;=============== 久病成良医-多试 發表人 - mustapha.wang 於 2003/06/19 14:55:01
------
江上何人初见月,江月何年初照人 |
bundur
一般會員 發表:16 回覆:44 積分:22 註冊:2002-11-30 發送簡訊給我 |
请问您的 ZLib 版本是多少?
我用 Delphi7.0 编译您的程序,找不到以下的东东呀!
[Error] Unit1.pas(41): Undeclared identifier: 'ZDecompressCheck'
[Error] Unit1.pas(41): Undeclared identifier: 'InflateInit'
[Error] Unit1.pas(50): Undeclared identifier: 'TZCompressionLevel'
[Error] Unit1.pas(50): Undeclared identifier: 'zcFastest'
[Error] Unit1.pas(65): Undeclared identifier: 'ZCompressCheck'
[Error] Unit1.pas(65): Undeclared identifier: 'DeflateInit'
[Error] Unit1.pas(65): Undeclared identifier: 'ZLevels' 而且我发现 TZCompressionLevel 应该是 TCompressionLevel 等等... 我在用 Delphi6 时,用 Zlib 也做了一个类似 WinZip 的小软件,可以压缩多个文件或整个目录,可查看压缩包中的文件,及解压其中的某些文件等。 我的做法与您所讲的类似。只是在压缩的时候,我直接用了
TCompressionStream 的 CopyFrom 功能。
|
mustapha.wang
資深會員 發表:89 回覆:409 積分:274 註冊:2002-03-13 發送簡訊給我 |
我用的1.1.4版的ZlibEx.pas,旧版的写法为:
procedure ZlibCompressStream2(Source,Dest:TMemoryStream; CompressLevel:TCompressionLevel=clFastest); const ZLevels: array [TCompressionLevel] of ShortInt = (Z_NO_COMPRESSION, Z_BEST_SPEED, Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION); var zstream: TZStreamRec; SourceLen,DestLen:LongWord; begin FillChar(zstream,SizeOf(TZStreamRec),0); SourceLen:=Source.Size; DestLen:=SizeOf(LongWord) ((SourceLen (SourceLen div 10) 12) 255) and not 255; Dest.Size:=DestLen; Dest.Position:=0; Dest.Write(SourceLen,Sizeof(LongWord)); zstream.next_in:=Source.Memory; zstream.avail_in:=SourceLen; zstream.next_out:=Pointer(LongWord(Dest.Memory) SizeOf(LongWord)); zstream.avail_out:=DestLen-SizeOf(longWord); DeflateInit_(zstream,ZLevels[CompressLevel],ZLIB_VERSION,SizeOf(TZStreamRec)); try deflate(zstream,Z_FINISH); finally deflateEnd(zstream); end; Dest.Size:=zstream.total_out SizeOf(LongWord); end; procedure ZlibDecompressStream2(Source,Dest:TMemoryStream); var zstream: TZStreamRec; SourceLen,DestLen:LongWord; begin FillChar(zstream,SizeOf(TZStreamRec),0); SourceLen:=Source.Size; Source.Position:=0; Source.Read(DestLen,SizeOf(LongWord)); Dest.Size:=DestLen; zstream.next_in:=Pointer(LongWord(Source.Memory) SizeOf(LongWord)); zstream.avail_in:=SourceLen-SizeOf(LongWord); zstream.next_out:=Dest.Memory; zstream.avail_out:=DestLen; InflateInit_(zstream,ZLIB_VERSION,SizeOf(TZStreamRec)); try inflate(zstream,Z_NO_FLUSH); finally inflateEnd(zstream); end; end;=============== 久病成良医-多试
------
江上何人初见月,江月何年初照人 |
bundur
一般會員 發表:16 回覆:44 積分:22 註冊:2002-11-30 發送簡訊給我 |
可我用你的方法,压缩任何文件,得到的都是 1k (当然是错的).
为什么? 你的 ZLib 1.1.4 版的pas在哪可以下载?
我还是使用我以前 Delphi6 中的Zlib的写法.
我本来想比较一下,哪个快,我就改用哪个. 其实,不管你一次性把源文件大小给它,它还是以每次 $F000 (即60K) 的大小处理的. 不知道我说的对不对.
而如果知道它每次处理的最大值,就可以以最佳方式做压缩并显示其进度.那不是很好吗? 还有,以TMemoryStream的方法是有缺陷的,如果一个文件很大时,如200MB,比如是SQL的备份数据等,这样的压缩过程就会死机.如果改用TFileStream的话就可以了. =============================
我的QQ是: 83699609 發表人 - bundur 於 2003/06/20 13:14:26
|
mustapha.wang
資深會員 發表:89 回覆:409 積分:274 註冊:2002-03-13 發送簡訊給我 |
哦,对不起,我对照改了改就post上来了,没想到会有错误,修订了一下,试过了,注意看有下划线的部分。
procedure ZlibCompressStream2(Source,Dest:TMemoryStream; CompressLevel:TCompressionLevel=clFastest); const ZLevels: array [TCompressionLevel] of ShortInt = (Z_NO_COMPRESSION, Z_BEST_SPEED, Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION); var zstream: TZStreamRec; SourceLen,DestLen:LongWord; begin FillChar(zstream,SizeOf(TZStreamRec),0); SourceLen:=Source.Size; DestLen:=SizeOf(LongWord) ((SourceLen (SourceLen div 10) 12) 255) and not 255; Dest.Size:=DestLen; Dest.Position:=0; Dest.Write(SourceLen,Sizeof(LongWord)); zstream.zalloc:=zlibAllocMem; zstream.zfree:=zlibFreeMem; zstream.next_in:=Source.Memory; zstream.avail_in:=SourceLen; zstream.next_out:=Pointer(LongWord(Dest.Memory) SizeOf(LongWord)); zstream.avail_out:=DestLen-SizeOf(longWord); CCheck(DeflateInit_(zstream,ZLevels[CompressLevel],ZLIB_VERSION,SizeOf(TZStreamRec))); try CCheck(deflate(zstream,Z_FINISH)); finally CCheck(deflateEnd(zstream)); end; Dest.Size:=zstream.total_out SizeOf(LongWord); end; procedure ZlibDecompressStream2(Source,Dest:TMemoryStream); var zstream: TZStreamRec; SourceLen,DestLen:LongWord; begin FillChar(zstream,SizeOf(TZStreamRec),0); SourceLen:=Source.Size; Source.Position:=0; Source.Read(DestLen,SizeOf(LongWord)); Dest.Size:=DestLen; zstream.zalloc:=zlibAllocMem; zstream.zfree:=zlibFreeMem; zstream.next_in:=Pointer(LongWord(Source.Memory) SizeOf(LongWord)); zstream.avail_in:=SourceLen-SizeOf(LongWord); zstream.next_out:=Dest.Memory; zstream.avail_out:=DestLen; CCheck(InflateInit_(zstream,ZLIB_VERSION,SizeOf(TZStreamRec))); try CCheck(inflate(zstream,Z_NO_FLUSH)); finally CCheck(inflateEnd(zstream)); end; end;你再试试 ====================== 久病成良医--多试 千人之诺诺,不如一士之谔谔--多听取反面意见 發表人 - mustapha.wang 於 2003/06/20 14:52:24
------
江上何人初见月,江月何年初照人 |
mustapha.wang
資深會員 發表:89 回覆:409 積分:274 註冊:2002-03-13 發送簡訊給我 |
|
bundur
一般會員 發表:16 回覆:44 積分:22 註冊:2002-11-30 發送簡訊給我 |
引言: 确实,这是个空间换时间的问题,用TMemoryStream表示你要把资料全部放到Memory,只适合一般大小的资料(??M以下),处理一般系统的应用也够了。 但对于文件压缩工具等应用,要处理大资料,就只能在buffer里read-write-read-write......了。 ====================== 久病成良医--多试 千人之诺诺,不如一士之谔谔--兼听在这里可以讨论一下 ZLib 我想是个好主意. 经过我的测试(测试源文件大小为19,153KB, 压缩后为 2,275KB), 我发现我原先的方法要比您提供的方法要快很多, 特别是在解压方面 以下是测试数据(单位毫秒): 次数 压 (原) 解 (原) 1 1772 1712 2013 601 2 1673 1663 2053 611 3 1712 1673 2033 601 4 1823 1692 2123 591 5 1722 1703 1682 601 6 1693 1642 1843 591 仔细研究后发现,你的方法与ZLib本身的DecompressBuf的方法相似, 我认为DecompressBuf中已经对读写的过程进行了优化了.所以直接 用它提供的类TCompressionStream才是最优化的方案. 因为我的项目中要用到它,所以我特别关心它的速度能不能提高. 使用ZLib进行处理,要比RAR和WINZIP都快好几倍到几十倍,但压缩比要比 RAR差太多了, 用最大的压缩比压出的文件比WINZIP的普通压缩比还不及. 不过对我的项目来说已经够用了. ================ 交流是进步的良师 發表人 - bundur 於 2003/06/20 18:00:45 |
mustapha.wang
資深會員 發表:89 回覆:409 積分:274 註冊:2002-03-13 發送簡訊給我 |
我认为TCompressionStream和TDecompressionStream都没有什么优化,它是为了方便我们使用而包装的,都是利用TZStreamRec调用obj里面的一些function,也是buffer read--write-read-write在作。
我们不能知晓它如何压缩/解压,只能在如何使用它的函数上来想办法,因此,最快的就是在Memory足够情况下,少作loop,直接用obj文件里的function一次完成。
你的解压程序用了600ms,而ZlibUncompressStream2用了2000ms吗?太棒了,能把你的解压代码post上来大家看看吗?
(我加了你的icq,希望直接讨论一下) ======================
久病成良医--多试
千人之诺诺,不如一士之谔谔--兼听
------
江上何人初见月,江月何年初照人 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |