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;
CRandomGenerator()21 			public CRandomGenerator() { Init(); }
Init()22 			public void Init() { A1 = 362436069; A2 = 521288629; }
GetRnd()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;
Init()36 			public void Init()
37 			{
38 				Value = 0;
39 				NumBits = 0;
40 			}
GetRnd(int numBits)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 
CBenchRandomGenerator()70 			public CBenchRandomGenerator() { }
71 
Set(UInt32 bufferSize)72 			public void Set(UInt32 bufferSize)
73 			{
74 				Buffer = new Byte[bufferSize];
75 				Pos = 0;
76 				BufferSize = bufferSize;
77 			}
GetRndBit()78 			UInt32 GetRndBit() { return RG.GetRnd(1); }
GetLogRandBits(int numBits)79 			UInt32 GetLogRandBits(int numBits)
80 			{
81 				UInt32 len = RG.GetRnd(numBits);
82 				return RG.GetRnd((int)len);
83 			}
GetOffset()84 			UInt32 GetOffset()
85 			{
86 				if (GetRndBit() == 0)
87 					return GetLogRandBits(4);
88 				return (GetLogRandBits(4) << 10) | RG.GetRnd(10);
89 			}
GetLen1()90 			UInt32 GetLen1() { return RG.GetRnd(1 + (int)RG.GetRnd(2)); }
GetLen2()91 			UInt32 GetLen2() { return RG.GetRnd(2 + (int)RG.GetRnd(2)); }
Generate()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();
Init()123 			public void Init() { CRC.Init(); }
GetDigest()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 { } }
Flush()131 			public override void Flush() { }
Seek(long offset, SeekOrigin origin)132 			public override long Seek(long offset, SeekOrigin origin) { return 0; }
SetLength(long value)133 			public override void SetLength(long value) { }
Read(byte[] buffer, int offset, int count)134 			public override int Read(byte[] buffer, int offset, int count) { return 0; }
135 
WriteByte(byte b)136 			public override void WriteByte(byte b)
137 			{
138 				CRC.UpdateByte(b);
139 			}
Write(byte[] buffer, int offset, int count)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;
Init()151 			public void Init() { InSize = 0; }
SetProgress(Int64 inSize, Int64 outSize)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 
GetLogSize(UInt32 size)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 
MyMultDiv64(UInt64 value, UInt64 elapsedTime)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 
GetCompressRating(UInt32 dictionarySize, UInt64 elapsedTime, UInt64 size)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 
GetDecompressRating(UInt64 elapsedTime, UInt64 outSize, UInt64 inSize)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 
GetTotalRating( UInt32 dictionarySize, UInt64 elapsedTimeEn, UInt64 sizeEn, UInt64 elapsedTimeDe, UInt64 inSizeDe, UInt64 outSizeDe)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 
PrintValue(UInt64 v)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 
PrintRating(UInt64 rating)218 		static void PrintRating(UInt64 rating)
219 		{
220 			PrintValue(rating / 1000000);
221 			System.Console.Write(" MIPS");
222 		}
223 
PrintResults( UInt32 dictionarySize, UInt64 elapsedTime, UInt64 size, bool decompressMode, UInt64 secondSize)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 
LzmaBenchmark(Int32 numIterations, UInt32 dictionarySize)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