1 // LzmaBench.cpp
2
3 #include "StdAfx.h"
4
5 #include "LzmaBench.h"
6
7 #ifndef _WIN32
8 #include <time.h>
9 #endif
10
11 #include "../../../Common/CRC.h"
12 #include "../LZMA/LZMADecoder.h"
13 #include "../LZMA/LZMAEncoder.h"
14
15 static const UInt32 kAdditionalSize =
16 #ifdef _WIN32_WCE
17 (1 << 20);
18 #else
19 (6 << 20);
20 #endif
21
22 static const UInt32 kCompressedAdditionalSize = (1 << 10);
23 static const UInt32 kMaxLzmaPropSize = 10;
24
25 class CRandomGenerator
26 {
27 UInt32 A1;
28 UInt32 A2;
29 public:
CRandomGenerator()30 CRandomGenerator() { Init(); }
Init()31 void Init() { A1 = 362436069; A2 = 521288629;}
GetRnd()32 UInt32 GetRnd()
33 {
34 return
35 ((A1 = 36969 * (A1 & 0xffff) + (A1 >> 16)) << 16) ^
36 ((A2 = 18000 * (A2 & 0xffff) + (A2 >> 16)) );
37 }
38 };
39
40 class CBitRandomGenerator
41 {
42 CRandomGenerator RG;
43 UInt32 Value;
44 int NumBits;
45 public:
Init()46 void Init()
47 {
48 Value = 0;
49 NumBits = 0;
50 }
GetRnd(int numBits)51 UInt32 GetRnd(int numBits)
52 {
53 if (NumBits > numBits)
54 {
55 UInt32 result = Value & ((1 << numBits) - 1);
56 Value >>= numBits;
57 NumBits -= numBits;
58 return result;
59 }
60 numBits -= NumBits;
61 UInt32 result = (Value << numBits);
62 Value = RG.GetRnd();
63 result |= Value & ((1 << numBits) - 1);
64 Value >>= numBits;
65 NumBits = 32 - numBits;
66 return result;
67 }
68 };
69
70 class CBenchRandomGenerator
71 {
72 CBitRandomGenerator RG;
73 UInt32 Pos;
74 public:
75 UInt32 BufferSize;
76 Byte *Buffer;
CBenchRandomGenerator()77 CBenchRandomGenerator(): Buffer(0) {}
~CBenchRandomGenerator()78 ~CBenchRandomGenerator() { delete []Buffer; }
Init()79 void Init() { RG.Init(); }
Set(UInt32 bufferSize)80 void Set(UInt32 bufferSize)
81 {
82 delete []Buffer;
83 Buffer = 0;
84 Buffer = new Byte[bufferSize];
85 Pos = 0;
86 BufferSize = bufferSize;
87 }
GetRndBit()88 UInt32 GetRndBit() { return RG.GetRnd(1); }
89 /*
90 UInt32 GetLogRand(int maxLen)
91 {
92 UInt32 len = GetRnd() % (maxLen + 1);
93 return GetRnd() & ((1 << len) - 1);
94 }
95 */
GetLogRandBits(int numBits)96 UInt32 GetLogRandBits(int numBits)
97 {
98 UInt32 len = RG.GetRnd(numBits);
99 return RG.GetRnd(len);
100 }
GetOffset()101 UInt32 GetOffset()
102 {
103 if (GetRndBit() == 0)
104 return GetLogRandBits(4);
105 return (GetLogRandBits(4) << 10) | RG.GetRnd(10);
106 }
GetLen()107 UInt32 GetLen()
108 {
109 if (GetRndBit() == 0)
110 return RG.GetRnd(2);
111 if (GetRndBit() == 0)
112 return 4 + RG.GetRnd(3);
113 return 12 + RG.GetRnd(4);
114 }
Generate()115 void Generate()
116 {
117 while(Pos < BufferSize)
118 {
119 if (GetRndBit() == 0 || Pos < 1)
120 Buffer[Pos++] = Byte(RG.GetRnd(8));
121 else
122 {
123 UInt32 offset = GetOffset();
124 while (offset >= Pos)
125 offset >>= 1;
126 offset += 1;
127 UInt32 len = 2 + GetLen();
128 for (UInt32 i = 0; i < len && Pos < BufferSize; i++, Pos++)
129 Buffer[Pos] = Buffer[Pos - offset];
130 }
131 }
132 }
133 };
134
135 class CBenchmarkInStream:
136 public ISequentialInStream,
137 public CMyUnknownImp
138 {
139 const Byte *Data;
140 UInt32 Pos;
141 UInt32 Size;
142 public:
143 MY_UNKNOWN_IMP
Init(const Byte * data,UInt32 size)144 void Init(const Byte *data, UInt32 size)
145 {
146 Data = data;
147 Size = size;
148 Pos = 0;
149 }
150 STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
151 };
152
Read(void * data,UInt32 size,UInt32 * processedSize)153 STDMETHODIMP CBenchmarkInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
154 {
155 UInt32 remain = Size - Pos;
156 if (size > remain)
157 size = remain;
158 for (UInt32 i = 0; i < size; i++)
159 ((Byte *)data)[i] = Data[Pos + i];
160 Pos += size;
161 if(processedSize != NULL)
162 *processedSize = size;
163 return S_OK;
164 }
165
166 class CBenchmarkOutStream:
167 public ISequentialOutStream,
168 public CMyUnknownImp
169 {
170 UInt32 BufferSize;
171 FILE *_f;
172 public:
173 UInt32 Pos;
174 Byte *Buffer;
CBenchmarkOutStream()175 CBenchmarkOutStream(): _f(0), Buffer(0) {}
~CBenchmarkOutStream()176 virtual ~CBenchmarkOutStream() { delete []Buffer; }
Init(FILE * f,UInt32 bufferSize)177 void Init(FILE *f, UInt32 bufferSize)
178 {
179 delete []Buffer;
180 Buffer = 0;
181 Buffer = new Byte[bufferSize];
182 Pos = 0;
183 BufferSize = bufferSize;
184 _f = f;
185 }
186 MY_UNKNOWN_IMP
187 STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
188 };
189
Write(const void * data,UInt32 size,UInt32 * processedSize)190 STDMETHODIMP CBenchmarkOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
191 {
192 UInt32 i;
193 for (i = 0; i < size && Pos < BufferSize; i++)
194 Buffer[Pos++] = ((const Byte *)data)[i];
195 if(processedSize != NULL)
196 *processedSize = i;
197 if (i != size)
198 {
199 fprintf(_f, "\nERROR: Buffer is full\n");
200 return E_FAIL;
201 }
202 return S_OK;
203 }
204
205 class CCrcOutStream:
206 public ISequentialOutStream,
207 public CMyUnknownImp
208 {
209 public:
210 CCRC CRC;
211 MY_UNKNOWN_IMP
Init()212 void Init() { CRC.Init(); }
213 STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
214 };
215
Write(const void * data,UInt32 size,UInt32 * processedSize)216 STDMETHODIMP CCrcOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
217 {
218 CRC.Update(data, size);
219 if(processedSize != NULL)
220 *processedSize = size;
221 return S_OK;
222 }
223
GetTimeCount()224 static UInt64 GetTimeCount()
225 {
226 #ifdef _WIN32
227 LARGE_INTEGER value;
228 if (::QueryPerformanceCounter(&value))
229 return value.QuadPart;
230 return GetTickCount();
231 #else
232 return clock();
233 #endif
234 }
235
GetFreq()236 static UInt64 GetFreq()
237 {
238 #ifdef _WIN32
239 LARGE_INTEGER value;
240 if (::QueryPerformanceFrequency(&value))
241 return value.QuadPart;
242 return 1000;
243 #else
244 return CLOCKS_PER_SEC;
245 #endif
246 }
247
248 struct CProgressInfo:
249 public ICompressProgressInfo,
250 public CMyUnknownImp
251 {
252 UInt64 ApprovedStart;
253 UInt64 InSize;
254 UInt64 Time;
InitCProgressInfo255 void Init()
256 {
257 InSize = 0;
258 Time = 0;
259 }
260 MY_UNKNOWN_IMP
261 STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
262 };
263
SetRatioInfo(const UInt64 * inSize,const UInt64 * outSize)264 STDMETHODIMP CProgressInfo::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
265 {
266 if (*inSize >= ApprovedStart && InSize == 0)
267 {
268 Time = ::GetTimeCount();
269 InSize = *inSize;
270 }
271 return S_OK;
272 }
273
274 static const int kSubBits = 8;
275
GetLogSize(UInt32 size)276 static UInt32 GetLogSize(UInt32 size)
277 {
278 for (int i = kSubBits; i < 32; i++)
279 for (UInt32 j = 0; j < (1 << kSubBits); j++)
280 if (size <= (((UInt32)1) << i) + (j << (i - kSubBits)))
281 return (i << kSubBits) + j;
282 return (32 << kSubBits);
283 }
284
MyMultDiv64(UInt64 value,UInt64 elapsedTime)285 static UInt64 MyMultDiv64(UInt64 value, UInt64 elapsedTime)
286 {
287 UInt64 freq = GetFreq();
288 UInt64 elTime = elapsedTime;
289 while(freq > 1000000)
290 {
291 freq >>= 1;
292 elTime >>= 1;
293 }
294 if (elTime == 0)
295 elTime = 1;
296 return value * freq / elTime;
297 }
298
GetCompressRating(UInt32 dictionarySize,bool isBT4,UInt64 elapsedTime,UInt64 size)299 static UInt64 GetCompressRating(UInt32 dictionarySize, bool isBT4,
300 UInt64 elapsedTime, UInt64 size)
301 {
302 UInt64 numCommandsForOne;
303 if (isBT4)
304 {
305 UInt64 t = GetLogSize(dictionarySize) - (19 << kSubBits);
306 numCommandsForOne = 2000 + ((t * t * 68) >> (2 * kSubBits));
307 }
308 else
309 {
310 UInt64 t = GetLogSize(dictionarySize) - (15 << kSubBits);
311 numCommandsForOne = 1500 + ((t * t * 41) >> (2 * kSubBits));
312 }
313 UInt64 numCommands = (UInt64)(size) * numCommandsForOne;
314 return MyMultDiv64(numCommands, elapsedTime);
315 }
316
GetDecompressRating(UInt64 elapsedTime,UInt64 outSize,UInt64 inSize)317 static UInt64 GetDecompressRating(UInt64 elapsedTime,
318 UInt64 outSize, UInt64 inSize)
319 {
320 UInt64 numCommands = inSize * 250 + outSize * 21;
321 return MyMultDiv64(numCommands, elapsedTime);
322 }
323
324 /*
325 static UInt64 GetTotalRating(
326 UInt32 dictionarySize,
327 bool isBT4,
328 UInt64 elapsedTimeEn, UInt64 sizeEn,
329 UInt64 elapsedTimeDe,
330 UInt64 inSizeDe, UInt64 outSizeDe)
331 {
332 return (GetCompressRating(dictionarySize, isBT4, elapsedTimeEn, sizeEn) +
333 GetDecompressRating(elapsedTimeDe, inSizeDe, outSizeDe)) / 2;
334 }
335 */
336
PrintRating(FILE * f,UInt64 rating)337 static void PrintRating(FILE *f, UInt64 rating)
338 {
339 fprintf(f, "%5d MIPS", (unsigned int)(rating / 1000000));
340 }
341
PrintResults(FILE * f,UInt32 dictionarySize,bool isBT4,UInt64 elapsedTime,UInt64 size,bool decompressMode,UInt64 secondSize)342 static void PrintResults(
343 FILE *f,
344 UInt32 dictionarySize,
345 bool isBT4,
346 UInt64 elapsedTime,
347 UInt64 size,
348 bool decompressMode, UInt64 secondSize)
349 {
350 UInt64 speed = MyMultDiv64(size, elapsedTime);
351 fprintf(f, "%6d KB/s ", (unsigned int)(speed / 1024));
352 UInt64 rating;
353 if (decompressMode)
354 rating = GetDecompressRating(elapsedTime, size, secondSize);
355 else
356 rating = GetCompressRating(dictionarySize, isBT4, elapsedTime, size);
357 PrintRating(f, rating);
358 }
359
ThrowError(FILE * f,HRESULT result,const char * s)360 static void ThrowError(FILE *f, HRESULT result, const char *s)
361 {
362 fprintf(f, "\nError: ");
363 if (result == E_ABORT)
364 fprintf(f, "User break");
365 if (result == E_OUTOFMEMORY)
366 fprintf(f, "Can not allocate memory");
367 else
368 fprintf(f, s);
369 fprintf(f, "\n");
370 }
371
372 const wchar_t *bt2 = L"BT2";
373 const wchar_t *bt4 = L"BT4";
374
LzmaBenchmark(FILE * f,UInt32 numIterations,UInt32 dictionarySize,bool isBT4)375 int LzmaBenchmark(FILE *f, UInt32 numIterations, UInt32 dictionarySize, bool isBT4)
376 {
377 if (numIterations == 0)
378 return 0;
379 if (dictionarySize < (1 << 19) && isBT4 || dictionarySize < (1 << 15))
380 {
381 fprintf(f, "\nError: dictionary size for benchmark must be >= 19 (512 KB)\n");
382 return 1;
383 }
384 fprintf(f, "\n Compressing Decompressing\n\n");
385 NCompress::NLZMA::CEncoder *encoderSpec = new NCompress::NLZMA::CEncoder;
386 CMyComPtr<ICompressCoder> encoder = encoderSpec;
387
388 NCompress::NLZMA::CDecoder *decoderSpec = new NCompress::NLZMA::CDecoder;
389 CMyComPtr<ICompressCoder> decoder = decoderSpec;
390
391 CBenchmarkOutStream *propStreamSpec = new CBenchmarkOutStream;
392 CMyComPtr<ISequentialOutStream> propStream = propStreamSpec;
393 propStreamSpec->Init(f, kMaxLzmaPropSize);
394
395 PROPID propIDs[] =
396 {
397 NCoderPropID::kDictionarySize,
398 NCoderPropID::kMatchFinder
399 };
400 const int kNumProps = sizeof(propIDs) / sizeof(propIDs[0]);
401 PROPVARIANT properties[kNumProps];
402 properties[0].vt = VT_UI4;
403 properties[0].ulVal = UInt32(dictionarySize);
404
405 properties[1].vt = VT_BSTR;
406 properties[1].bstrVal = isBT4 ? (BSTR)bt4: (BSTR)bt2;
407
408 const UInt32 kBufferSize = dictionarySize + kAdditionalSize;
409 const UInt32 kCompressedBufferSize = (kBufferSize / 2) + kCompressedAdditionalSize;
410
411 if (encoderSpec->SetCoderProperties(propIDs, properties, kNumProps) != S_OK)
412 {
413 fprintf(f, "\nError: Incorrect command\n");
414 return 1;
415 }
416 encoderSpec->WriteCoderProperties(propStream);
417
418 CBenchRandomGenerator rg;
419 rg.Init();
420 rg.Set(kBufferSize);
421 rg.Generate();
422 CCRC crc;
423 crc.Update(rg.Buffer, rg.BufferSize);
424
425 CProgressInfo *progressInfoSpec = new CProgressInfo;
426 CMyComPtr<ICompressProgressInfo> progressInfo = progressInfoSpec;
427
428 progressInfoSpec->ApprovedStart = dictionarySize;
429
430 UInt64 totalBenchSize = 0;
431 UInt64 totalEncodeTime = 0;
432 UInt64 totalDecodeTime = 0;
433 UInt64 totalCompressedSize = 0;
434
435 for (UInt32 i = 0; i < numIterations; i++)
436 {
437 progressInfoSpec->Init();
438 CBenchmarkInStream *inStreamSpec = new CBenchmarkInStream;
439 inStreamSpec->Init(rg.Buffer, rg.BufferSize);
440 CMyComPtr<ISequentialInStream> inStream = inStreamSpec;
441 CBenchmarkOutStream *outStreamSpec = new CBenchmarkOutStream;
442 outStreamSpec->Init(f, kCompressedBufferSize);
443 CMyComPtr<ISequentialOutStream> outStream = outStreamSpec;
444 HRESULT result = encoder->Code(inStream, outStream, 0, 0, progressInfo);
445 UInt64 encodeTime = ::GetTimeCount() - progressInfoSpec->Time;
446 UInt32 compressedSize = outStreamSpec->Pos;
447 if(result != S_OK)
448 {
449 ThrowError(f, result, "Encoder Error");
450 return 1;
451 }
452 if (progressInfoSpec->InSize == 0)
453 {
454 fprintf(f, "\nError: Internal ERROR 1282\n");
455 return 1;
456 }
457
458 ///////////////////////
459 // Decompressing
460
461 CCrcOutStream *crcOutStreamSpec = new CCrcOutStream;
462 CMyComPtr<ISequentialOutStream> crcOutStream = crcOutStreamSpec;
463
464 UInt64 decodeTime;
465 for (int j = 0; j < 2; j++)
466 {
467 inStreamSpec->Init(outStreamSpec->Buffer, compressedSize);
468 crcOutStreamSpec->Init();
469
470 if (decoderSpec->SetDecoderProperties2(propStreamSpec->Buffer, propStreamSpec->Pos) != S_OK)
471 {
472 fprintf(f, "\nError: Set Decoder Properties Error\n");
473 return 1;
474 }
475 UInt64 outSize = kBufferSize;
476 UInt64 startTime = ::GetTimeCount();
477 result = decoder->Code(inStream, crcOutStream, 0, &outSize, 0);
478 decodeTime = ::GetTimeCount() - startTime;
479 if(result != S_OK)
480 {
481 ThrowError(f, result, "Decode Error");
482 return 1;
483 }
484 if (crcOutStreamSpec->CRC.GetDigest() != crc.GetDigest())
485 {
486 fprintf(f, "\nError: CRC Error\n");
487 return 1;
488 }
489 }
490 UInt64 benchSize = kBufferSize - progressInfoSpec->InSize;
491 PrintResults(f, dictionarySize, isBT4, encodeTime, benchSize, false, 0);
492 fprintf(f, " ");
493 PrintResults(f, dictionarySize, isBT4, decodeTime, kBufferSize, true, compressedSize);
494 fprintf(f, "\n");
495
496 totalBenchSize += benchSize;
497 totalEncodeTime += encodeTime;
498 totalDecodeTime += decodeTime;
499 totalCompressedSize += compressedSize;
500 }
501 fprintf(f, "---------------------------------------------------\n");
502 PrintResults(f, dictionarySize, isBT4, totalEncodeTime, totalBenchSize, false, 0);
503 fprintf(f, " ");
504 PrintResults(f, dictionarySize, isBT4, totalDecodeTime,
505 kBufferSize * numIterations, true, totalCompressedSize);
506 fprintf(f, " Average\n");
507 return 0;
508 }
509