// LzmaBench.cs using System; using System.IO; namespace SevenZip { /// /// LZMA Benchmark /// internal abstract class LzmaBench { const UInt32 kAdditionalSize = (6 << 20); const UInt32 kCompressedAdditionalSize = (1 << 10); const UInt32 kMaxLzmaPropSize = 10; class CRandomGenerator { UInt32 A1; UInt32 A2; public CRandomGenerator() { Init(); } public void Init() { A1 = 362436069; A2 = 521288629; } public UInt32 GetRnd() { return ((A1 = 36969 * (A1 & 0xffff) + (A1 >> 16)) << 16) ^ ((A2 = 18000 * (A2 & 0xffff) + (A2 >> 16))); } }; class CBitRandomGenerator { CRandomGenerator RG = new CRandomGenerator(); UInt32 Value; int NumBits; public void Init() { Value = 0; NumBits = 0; } public UInt32 GetRnd(int numBits) { UInt32 result; if (NumBits > numBits) { result = Value & (((UInt32)1 << numBits) - 1); Value >>= numBits; NumBits -= numBits; return result; } numBits -= NumBits; result = (Value << numBits); Value = RG.GetRnd(); result |= Value & (((UInt32)1 << numBits) - 1); Value >>= numBits; NumBits = 32 - numBits; return result; } }; class CBenchRandomGenerator { CBitRandomGenerator RG = new CBitRandomGenerator(); UInt32 Pos; public UInt32 BufferSize; public Byte[] Buffer = null; public CBenchRandomGenerator() { } public void Init() { RG.Init(); } public void Set(UInt32 bufferSize) { Buffer = new Byte[bufferSize]; Pos = 0; BufferSize = bufferSize; } UInt32 GetRndBit() { return RG.GetRnd(1); } /* UInt32 GetLogRand(int maxLen) { UInt32 len = GetRnd() % (maxLen + 1); return GetRnd() & ((1 << len) - 1); } */ UInt32 GetLogRandBits(int numBits) { UInt32 len = RG.GetRnd(numBits); return RG.GetRnd((int)len); } UInt32 GetOffset() { if (GetRndBit() == 0) return GetLogRandBits(4); return (GetLogRandBits(4) << 10) | RG.GetRnd(10); } UInt32 GetLen() { if (GetRndBit() == 0) return RG.GetRnd(2); if (GetRndBit() == 0) return 4 + RG.GetRnd(3); return 12 + RG.GetRnd(4); } public void Generate() { while (Pos < BufferSize) { if (GetRndBit() == 0 || Pos < 1) Buffer[Pos++] = (Byte)(RG.GetRnd(8)); else { UInt32 offset = GetOffset(); while (offset >= Pos) offset >>= 1; offset += 1; UInt32 len = 2 + GetLen(); for (UInt32 i = 0; i < len && Pos < BufferSize; i++, Pos++) Buffer[Pos] = Buffer[Pos - offset]; } } } }; class CrcOutStream : System.IO.Stream { public CRC CRC = new CRC(); public void Init() { CRC.Init(); } public UInt32 GetDigest() { return CRC.GetDigest(); } public override bool CanRead { get { return false; } } public override bool CanSeek { get { return false; } } public override bool CanWrite { get { return true; } } public override Int64 Length { get { return 0; } } public override Int64 Position { get { return 0; } set { } } public override void Flush() { } public override long Seek(long offset, SeekOrigin origin) { return 0; } public override void SetLength(long value) { } public override int Read(byte[] buffer, int offset, int count) { return 0; } public override void WriteByte(byte b) { CRC.UpdateByte(b); } public override void Write(byte[] buffer, int offset, int count) { CRC.Update(buffer, (uint)offset, (uint)count); } }; class CProgressInfo : ICodeProgress { public Int64 ApprovedStart; public Int64 InSize; public System.DateTime Time; public void Init() { InSize = 0; } public void SetProgress(Int64 inSize, Int64 outSize) { if (inSize >= ApprovedStart && InSize == 0) { Time = DateTime.UtcNow; InSize = inSize; } } } const int kSubBits = 8; static UInt32 GetLogSize(UInt32 size) { for (int i = kSubBits; i < 32; i++) for (UInt32 j = 0; j < (1 << kSubBits); j++) if (size <= (((UInt32)1) << i) + (j << (i - kSubBits))) return (UInt32)(i << kSubBits) + j; return (32 << kSubBits); } static UInt64 MyMultDiv64(UInt64 value, UInt64 elapsedTime) { UInt64 freq = TimeSpan.TicksPerSecond; UInt64 elTime = elapsedTime; while (freq > 1000000) { freq >>= 1; elTime >>= 1; } if (elTime == 0) elTime = 1; return value * freq / elTime; } static UInt64 GetCompressRating(UInt32 dictionarySize, bool isBT4, UInt64 elapsedTime, UInt64 size) { UInt64 numCommandsForOne; if (isBT4) { UInt64 t = GetLogSize(dictionarySize) - (19 << kSubBits); numCommandsForOne = 2000 + ((t * t * 68) >> (2 * kSubBits)); } else { UInt64 t = GetLogSize(dictionarySize) - (15 << kSubBits); numCommandsForOne = 1500 + ((t * t * 41) >> (2 * kSubBits)); } UInt64 numCommands = (UInt64)(size) * numCommandsForOne; return MyMultDiv64(numCommands, elapsedTime); } static UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 outSize, UInt64 inSize) { UInt64 numCommands = inSize * 250 + outSize * 21; return MyMultDiv64(numCommands, elapsedTime); } static UInt64 GetTotalRating( UInt32 dictionarySize, bool isBT4, UInt64 elapsedTimeEn, UInt64 sizeEn, UInt64 elapsedTimeDe, UInt64 inSizeDe, UInt64 outSizeDe) { return (GetCompressRating(dictionarySize, isBT4, elapsedTimeEn, sizeEn) + GetDecompressRating(elapsedTimeDe, inSizeDe, outSizeDe)) / 2; } static void PrintValue(UInt64 v) { string s = v.ToString(); for (int i = 0; i + s.Length < 6; i++) System.Console.Write(" "); System.Console.Write(s); } static void PrintRating(UInt64 rating) { PrintValue(rating / 1000000); System.Console.Write(" MIPS"); } static void PrintResults( UInt32 dictionarySize, bool isBT4, UInt64 elapsedTime, UInt64 size, bool decompressMode, UInt64 secondSize) { UInt64 speed = MyMultDiv64(size, elapsedTime); PrintValue(speed / 1024); System.Console.Write(" KB/s "); UInt64 rating; if (decompressMode) rating = GetDecompressRating(elapsedTime, size, secondSize); else rating = GetCompressRating(dictionarySize, isBT4, elapsedTime, size); PrintRating(rating); } const string bt2 = "BT2"; const string bt4 = "BT4"; static public int LzmaBenchmark(Int32 numIterations, UInt32 dictionarySize, bool isBT4) { if (numIterations <= 0) return 0; if (dictionarySize < (1 << 19) && isBT4 || dictionarySize < (1 << 15)) { System.Console.WriteLine("\nError: dictionary size for benchmark must be >= 19 (512 KB)"); return 1; } System.Console.Write("\n Compressing Decompressing\n\n"); Compression.LZMA.Encoder encoder = new Compression.LZMA.Encoder(); Compression.LZMA.Decoder decoder = new Compression.LZMA.Decoder(); CoderPropID[] propIDs = { CoderPropID.DictionarySize, CoderPropID.MatchFinder }; object[] properties = { (Int32)(dictionarySize), isBT4 ? bt4: bt2 }; UInt32 kBufferSize = dictionarySize + kAdditionalSize; UInt32 kCompressedBufferSize = (kBufferSize / 2) + kCompressedAdditionalSize; encoder.SetCoderProperties(propIDs, properties); System.IO.MemoryStream propStream = new System.IO.MemoryStream(); encoder.WriteCoderProperties(propStream); byte[] propArray = propStream.ToArray(); CBenchRandomGenerator rg = new CBenchRandomGenerator(); rg.Init(); rg.Set(kBufferSize); rg.Generate(); CRC crc = new CRC(); crc.Init(); crc.Update(rg.Buffer, 0, rg.BufferSize); CProgressInfo progressInfo = new CProgressInfo(); progressInfo.ApprovedStart = dictionarySize; UInt64 totalBenchSize = 0; UInt64 totalEncodeTime = 0; UInt64 totalDecodeTime = 0; UInt64 totalCompressedSize = 0; MemoryStream inStream = new MemoryStream(rg.Buffer, 0, (int)rg.BufferSize); MemoryStream compressedStream = new MemoryStream((int)kCompressedBufferSize); CrcOutStream crcOutStream = new CrcOutStream(); for (Int32 i = 0; i < numIterations; i++) { progressInfo.Init(); inStream.Seek(0, SeekOrigin.Begin); compressedStream.Seek(0, SeekOrigin.Begin); encoder.Code(inStream, compressedStream, -1, -1, progressInfo); TimeSpan sp2 = DateTime.UtcNow - progressInfo.Time; UInt64 encodeTime = (UInt64)sp2.Ticks; long compressedSize = compressedStream.Position; if (progressInfo.InSize == 0) throw (new Exception("Internal ERROR 1282")); UInt64 decodeTime = 0; for (int j = 0; j < 2; j++) { compressedStream.Seek(0, SeekOrigin.Begin); crcOutStream.Init(); decoder.SetDecoderProperties(propArray); UInt64 outSize = kBufferSize; System.DateTime startTime = DateTime.UtcNow; decoder.Code(compressedStream, crcOutStream, 0, (Int64)outSize, null); TimeSpan sp = (DateTime.UtcNow - startTime); decodeTime = (ulong)sp.Ticks; if (crcOutStream.GetDigest() != crc.GetDigest()) throw (new Exception("CRC Error")); } UInt64 benchSize = kBufferSize - (UInt64)progressInfo.InSize; PrintResults(dictionarySize, isBT4, encodeTime, benchSize, false, 0); System.Console.Write(" "); PrintResults(dictionarySize, isBT4, decodeTime, kBufferSize, true, (ulong)compressedSize); System.Console.WriteLine(); totalBenchSize += benchSize; totalEncodeTime += encodeTime; totalDecodeTime += decodeTime; totalCompressedSize += (ulong)compressedSize; } System.Console.WriteLine("---------------------------------------------------"); PrintResults(dictionarySize, isBT4, totalEncodeTime, totalBenchSize, false, 0); System.Console.Write(" "); PrintResults(dictionarySize, isBT4, totalDecodeTime, kBufferSize * (UInt64)numIterations, true, totalCompressedSize); System.Console.WriteLine(" Average"); return 0; } } }