Home | History | Annotate | Download | only in LzmaAlone
      1 // LzmaBench.cs
      2 
      3 using System;
      4 using System.IO;
      5 
      6 namespace SevenZip
      7 {
      8 	/// <summary>
      9 	/// LZMA Benchmark
     10 	/// </summary>
     11 	internal abstract class LzmaBench
     12 	{
     13 		const UInt32 kAdditionalSize = (6 << 20);
     14 		const UInt32 kCompressedAdditionalSize = (1 << 10);
     15 		const UInt32 kMaxLzmaPropSize = 10;
     16 
     17 		class CRandomGenerator
     18 		{
     19 			UInt32 A1;
     20 			UInt32 A2;
     21 			public CRandomGenerator() { Init(); }
     22 			public void Init() { A1 = 362436069; A2 = 521288629; }
     23 			public UInt32 GetRnd()
     24 			{
     25 				return
     26 					((A1 = 36969 * (A1 & 0xffff) + (A1 >> 16)) << 16) ^
     27 					((A2 = 18000 * (A2 & 0xffff) + (A2 >> 16)));
     28 			}
     29 		};
     30 
     31 		class CBitRandomGenerator
     32 		{
     33 			CRandomGenerator RG = new CRandomGenerator();
     34 			UInt32 Value;
     35 			int NumBits;
     36 			public void Init()
     37 			{
     38 				Value = 0;
     39 				NumBits = 0;
     40 			}
     41 			public UInt32 GetRnd(int numBits)
     42 			{
     43 				UInt32 result;
     44 				if (NumBits > numBits)
     45 				{
     46 					result = Value & (((UInt32)1 << numBits) - 1);
     47 					Value >>= numBits;
     48 					NumBits -= numBits;
     49 					return result;
     50 				}
     51 				numBits -= NumBits;
     52 				result = (Value << numBits);
     53 				Value = RG.GetRnd();
     54 				result |= Value & (((UInt32)1 << numBits) - 1);
     55 				Value >>= numBits;
     56 				NumBits = 32 - numBits;
     57 				return result;
     58 			}
     59 		};
     60 
     61 		class CBenchRandomGenerator
     62 		{
     63 			CBitRandomGenerator RG = new CBitRandomGenerator();
     64 			UInt32 Pos;
     65 			UInt32 Rep0;
     66 			
     67 			public UInt32 BufferSize;
     68 			public Byte[] Buffer = null;
     69 
     70 			public CBenchRandomGenerator() { }
     71 
     72 			public void Set(UInt32 bufferSize)
     73 			{
     74 				Buffer = new Byte[bufferSize];
     75 				Pos = 0;
     76 				BufferSize = bufferSize;
     77 			}
     78 			UInt32 GetRndBit() { return RG.GetRnd(1); }
     79 			UInt32 GetLogRandBits(int numBits)
     80 			{
     81 				UInt32 len = RG.GetRnd(numBits);
     82 				return RG.GetRnd((int)len);
     83 			}
     84 			UInt32 GetOffset()
     85 			{
     86 				if (GetRndBit() == 0)
     87 					return GetLogRandBits(4);
     88 				return (GetLogRandBits(4) << 10) | RG.GetRnd(10);
     89 			}
     90 			UInt32 GetLen1() { return RG.GetRnd(1 + (int)RG.GetRnd(2)); }
     91 			UInt32 GetLen2() { return RG.GetRnd(2 + (int)RG.GetRnd(2)); }
     92 			public void Generate()
     93 			{
     94 				RG.Init();
     95 				Rep0 = 1;
     96 				while (Pos < BufferSize)
     97 				{
     98 					if (GetRndBit() == 0 || Pos < 1)
     99 						Buffer[Pos++] = (Byte)RG.GetRnd(8);
    100 					else
    101 					{
    102 						UInt32 len;
    103 						if (RG.GetRnd(3) == 0)
    104 							len = 1 + GetLen1();
    105 						else
    106 						{
    107 							do
    108 								Rep0 = GetOffset();
    109 							while (Rep0 >= Pos);
    110 							Rep0++;
    111 							len = 2 + GetLen2();
    112 						}
    113 						for (UInt32 i = 0; i < len && Pos < BufferSize; i++, Pos++)
    114 							Buffer[Pos] = Buffer[Pos - Rep0];
    115 					}
    116 				}
    117 			}
    118 		};
    119 
    120 		class CrcOutStream : System.IO.Stream
    121 		{
    122 			public CRC CRC = new CRC();
    123 			public void Init() { CRC.Init(); }
    124 			public UInt32 GetDigest() { return CRC.GetDigest(); }
    125 
    126 			public override bool CanRead { get { return false; } }
    127 			public override bool CanSeek { get { return false; } }
    128 			public override bool CanWrite { get { return true; } }
    129 			public override Int64 Length { get { return 0; } }
    130 			public override Int64 Position { get { return 0; } set { } }
    131 			public override void Flush() { }
    132 			public override long Seek(long offset, SeekOrigin origin) { return 0; }
    133 			public override void SetLength(long value) { }
    134 			public override int Read(byte[] buffer, int offset, int count) { return 0; }
    135 
    136 			public override void WriteByte(byte b)
    137 			{
    138 				CRC.UpdateByte(b);
    139 			}
    140 			public override void Write(byte[] buffer, int offset, int count)
    141 			{
    142 				CRC.Update(buffer, (uint)offset, (uint)count);
    143 			}
    144 		};
    145 
    146 		class CProgressInfo : ICodeProgress
    147 		{
    148 			public Int64 ApprovedStart;
    149 			public Int64 InSize;
    150 			public System.DateTime Time;
    151 			public void Init() { InSize = 0; }
    152 			public void SetProgress(Int64 inSize, Int64 outSize)
    153 			{
    154 				if (inSize >= ApprovedStart && InSize == 0)
    155 				{
    156 					Time = DateTime.UtcNow;
    157 					InSize = inSize;
    158 				}
    159 			}
    160 		}
    161 		const int kSubBits = 8;
    162 
    163 		static UInt32 GetLogSize(UInt32 size)
    164 		{
    165 			for (int i = kSubBits; i < 32; i++)
    166 				for (UInt32 j = 0; j < (1 << kSubBits); j++)
    167 					if (size <= (((UInt32)1) << i) + (j << (i - kSubBits)))
    168 						return (UInt32)(i << kSubBits) + j;
    169 			return (32 << kSubBits);
    170 		}
    171 
    172 		static UInt64 MyMultDiv64(UInt64 value, UInt64 elapsedTime)
    173 		{
    174 			UInt64 freq = TimeSpan.TicksPerSecond;
    175 			UInt64 elTime = elapsedTime;
    176 			while (freq > 1000000)
    177 			{
    178 				freq >>= 1;
    179 				elTime >>= 1;
    180 			}
    181 			if (elTime == 0)
    182 				elTime = 1;
    183 			return value * freq / elTime;
    184 		}
    185 
    186 		static UInt64 GetCompressRating(UInt32 dictionarySize, UInt64 elapsedTime, UInt64 size)
    187 		{
    188 			UInt64 t = GetLogSize(dictionarySize) - (18 << kSubBits);
    189 			UInt64 numCommandsForOne = 1060 + ((t * t * 10) >> (2 * kSubBits));
    190 			UInt64 numCommands = (UInt64)(size) * numCommandsForOne;
    191 			return MyMultDiv64(numCommands, elapsedTime);
    192 		}
    193 
    194 		static UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 outSize, UInt64 inSize)
    195 		{
    196 			UInt64 numCommands = inSize * 220 + outSize * 20;
    197 			return MyMultDiv64(numCommands, elapsedTime);
    198 		}
    199 
    200 		static UInt64 GetTotalRating(
    201 			UInt32 dictionarySize,
    202 			UInt64 elapsedTimeEn, UInt64 sizeEn,
    203 			UInt64 elapsedTimeDe,
    204 			UInt64 inSizeDe, UInt64 outSizeDe)
    205 		{
    206 			return (GetCompressRating(dictionarySize, elapsedTimeEn, sizeEn) +
    207 				GetDecompressRating(elapsedTimeDe, inSizeDe, outSizeDe)) / 2;
    208 		}
    209 
    210 		static void PrintValue(UInt64 v)
    211 		{
    212 			string s = v.ToString();
    213 			for (int i = 0; i + s.Length < 6; i++)
    214 				System.Console.Write(" ");
    215 			System.Console.Write(s);
    216 		}
    217 
    218 		static void PrintRating(UInt64 rating)
    219 		{
    220 			PrintValue(rating / 1000000);
    221 			System.Console.Write(" MIPS");
    222 		}
    223 
    224 		static void PrintResults(
    225 			UInt32 dictionarySize,
    226 			UInt64 elapsedTime,
    227 			UInt64 size,
    228 			bool decompressMode, UInt64 secondSize)
    229 		{
    230 			UInt64 speed = MyMultDiv64(size, elapsedTime);
    231 			PrintValue(speed / 1024);
    232 			System.Console.Write(" KB/s  ");
    233 			UInt64 rating;
    234 			if (decompressMode)
    235 				rating = GetDecompressRating(elapsedTime, size, secondSize);
    236 			else
    237 				rating = GetCompressRating(dictionarySize, elapsedTime, size);
    238 			PrintRating(rating);
    239 		}
    240 
    241 		static public int LzmaBenchmark(Int32 numIterations, UInt32 dictionarySize)
    242 		{
    243 			if (numIterations <= 0)
    244 				return 0;
    245 			if (dictionarySize < (1 << 18))
    246 			{
    247 				System.Console.WriteLine("\nError: dictionary size for benchmark must be >= 19 (512 KB)");
    248 				return 1;
    249 			}
    250 			System.Console.Write("\n       Compressing                Decompressing\n\n");
    251 
    252 			Compression.LZMA.Encoder encoder = new Compression.LZMA.Encoder();
    253 			Compression.LZMA.Decoder decoder = new Compression.LZMA.Decoder();
    254 
    255 
    256 			CoderPropID[] propIDs = 
    257 			{ 
    258 				CoderPropID.DictionarySize,
    259 			};
    260 			object[] properties = 
    261 			{
    262 				(Int32)(dictionarySize),
    263 			};
    264 
    265 			UInt32 kBufferSize = dictionarySize + kAdditionalSize;
    266 			UInt32 kCompressedBufferSize = (kBufferSize / 2) + kCompressedAdditionalSize;
    267 
    268 			encoder.SetCoderProperties(propIDs, properties);
    269 			System.IO.MemoryStream propStream = new System.IO.MemoryStream();
    270 			encoder.WriteCoderProperties(propStream);
    271 			byte[] propArray = propStream.ToArray();
    272 
    273 			CBenchRandomGenerator rg = new CBenchRandomGenerator();
    274 
    275 			rg.Set(kBufferSize);
    276 			rg.Generate();
    277 			CRC crc = new CRC();
    278 			crc.Init();
    279 			crc.Update(rg.Buffer, 0, rg.BufferSize);
    280 
    281 			CProgressInfo progressInfo = new CProgressInfo();
    282 			progressInfo.ApprovedStart = dictionarySize;
    283 
    284 			UInt64 totalBenchSize = 0;
    285 			UInt64 totalEncodeTime = 0;
    286 			UInt64 totalDecodeTime = 0;
    287 			UInt64 totalCompressedSize = 0;
    288 
    289 			MemoryStream inStream = new MemoryStream(rg.Buffer, 0, (int)rg.BufferSize);
    290 			MemoryStream compressedStream = new MemoryStream((int)kCompressedBufferSize);
    291 			CrcOutStream crcOutStream = new CrcOutStream();
    292 			for (Int32 i = 0; i < numIterations; i++)
    293 			{
    294 				progressInfo.Init();
    295 				inStream.Seek(0, SeekOrigin.Begin);
    296 				compressedStream.Seek(0, SeekOrigin.Begin);
    297 				encoder.Code(inStream, compressedStream, -1, -1, progressInfo);
    298 				TimeSpan sp2 = DateTime.UtcNow - progressInfo.Time;
    299 				UInt64 encodeTime = (UInt64)sp2.Ticks;
    300 
    301 				long compressedSize = compressedStream.Position;
    302 				if (progressInfo.InSize == 0)
    303 					throw (new Exception("Internal ERROR 1282"));
    304 
    305 				UInt64 decodeTime = 0;
    306 				for (int j = 0; j < 2; j++)
    307 				{
    308 					compressedStream.Seek(0, SeekOrigin.Begin);
    309 					crcOutStream.Init();
    310 
    311 					decoder.SetDecoderProperties(propArray);
    312 					UInt64 outSize = kBufferSize;
    313 					System.DateTime startTime = DateTime.UtcNow;
    314 					decoder.Code(compressedStream, crcOutStream, 0, (Int64)outSize, null);
    315 					TimeSpan sp = (DateTime.UtcNow - startTime);
    316 					decodeTime = (ulong)sp.Ticks;
    317 					if (crcOutStream.GetDigest() != crc.GetDigest())
    318 						throw (new Exception("CRC Error"));
    319 				}
    320 				UInt64 benchSize = kBufferSize - (UInt64)progressInfo.InSize;
    321 				PrintResults(dictionarySize, encodeTime, benchSize, false, 0);
    322 				System.Console.Write("     ");
    323 				PrintResults(dictionarySize, decodeTime, kBufferSize, true, (ulong)compressedSize);
    324 				System.Console.WriteLine();
    325 
    326 				totalBenchSize += benchSize;
    327 				totalEncodeTime += encodeTime;
    328 				totalDecodeTime += decodeTime;
    329 				totalCompressedSize += (ulong)compressedSize;
    330 			}
    331 			System.Console.WriteLine("---------------------------------------------------");
    332 			PrintResults(dictionarySize, totalEncodeTime, totalBenchSize, false, 0);
    333 			System.Console.Write("     ");
    334 			PrintResults(dictionarySize, totalDecodeTime,
    335 					kBufferSize * (UInt64)numIterations, true, totalCompressedSize);
    336 			System.Console.WriteLine("    Average");
    337 			return 0;
    338 		}
    339 	}
    340 }
    341