Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / UI / Common / Bench.cpp
1 // Bench.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "Bench.h"\r
6 \r
7 #ifndef _WIN32\r
8 #define USE_POSIX_TIME\r
9 #define USE_POSIX_TIME2\r
10 #endif\r
11 \r
12 #ifdef USE_POSIX_TIME\r
13 #include <time.h>\r
14 #ifdef USE_POSIX_TIME2\r
15 #include <sys/time.h>\r
16 #endif\r
17 #endif\r
18 \r
19 #ifdef _WIN32\r
20 #define USE_ALLOCA\r
21 #endif\r
22 \r
23 #ifdef USE_ALLOCA\r
24 #ifdef _WIN32\r
25 #include <malloc.h>\r
26 #else\r
27 #include <stdlib.h>\r
28 #endif\r
29 #endif\r
30 \r
31 #include "../../../../C/7zCrc.h"\r
32 #include "../../../../C/Alloc.h"\r
33 \r
34 #ifndef _7ZIP_ST\r
35 #include "../../../Windows/Synchronization.h"\r
36 #include "../../../Windows/Thread.h"\r
37 #endif\r
38 \r
39 #include "../../../Windows/PropVariant.h"\r
40 \r
41 static const UInt32 kUncompressMinBlockSize =\r
42 #ifdef UNDER_CE\r
43 1 << 24;\r
44 #else\r
45 1 << 26;\r
46 #endif\r
47 \r
48 static const UInt32 kCrcBlockSize =\r
49 #ifdef UNDER_CE\r
50 1 << 25;\r
51 #else\r
52 1 << 30;\r
53 #endif\r
54 \r
55 static const UInt32 kAdditionalSize = (1 << 16);\r
56 static const UInt32 kCompressedAdditionalSize = (1 << 10);\r
57 static const UInt32 kMaxLzmaPropSize = 5;\r
58 \r
59 class CBaseRandomGenerator\r
60 {\r
61   UInt32 A1;\r
62   UInt32 A2;\r
63 public:\r
64   CBaseRandomGenerator() { Init(); }\r
65   void Init() { A1 = 362436069; A2 = 521288629;}\r
66   UInt32 GetRnd()\r
67   {\r
68     return\r
69       ((A1 = 36969 * (A1 & 0xffff) + (A1 >> 16)) << 16) +\r
70       ((A2 = 18000 * (A2 & 0xffff) + (A2 >> 16)) );\r
71   }\r
72 };\r
73 \r
74 class CBenchBuffer\r
75 {\r
76 public:\r
77   size_t BufferSize;\r
78   Byte *Buffer;\r
79   CBenchBuffer(): Buffer(0) {}\r
80   virtual ~CBenchBuffer() { Free(); }\r
81   void Free()\r
82   {\r
83     ::MidFree(Buffer);\r
84     Buffer = 0;\r
85   }\r
86   bool Alloc(size_t bufferSize)\r
87   {\r
88     if (Buffer != 0 && BufferSize == bufferSize)\r
89       return true;\r
90     Free();\r
91     Buffer = (Byte *)::MidAlloc(bufferSize);\r
92     BufferSize = bufferSize;\r
93     return (Buffer != 0);\r
94   }\r
95 };\r
96 \r
97 class CBenchRandomGenerator: public CBenchBuffer\r
98 {\r
99   CBaseRandomGenerator *RG;\r
100 public:\r
101   void Set(CBaseRandomGenerator *rg) { RG = rg; }\r
102   UInt32 GetVal(UInt32 &res, int numBits)\r
103   {\r
104     UInt32 val = res & (((UInt32)1 << numBits) - 1);\r
105     res >>= numBits;\r
106     return val;\r
107   }\r
108   UInt32 GetLen(UInt32 &res)\r
109   {\r
110     UInt32 len = GetVal(res, 2);\r
111     return GetVal(res, 1 + len);\r
112   }\r
113   void Generate()\r
114   {\r
115     UInt32 pos = 0;\r
116     UInt32 rep0 = 1;\r
117     while (pos < BufferSize)\r
118     {\r
119       UInt32 res = RG->GetRnd();\r
120       res >>= 1;\r
121       if (GetVal(res, 1) == 0 || pos < 1024)\r
122         Buffer[pos++] = (Byte)(res & 0xFF);\r
123       else\r
124       {\r
125         UInt32 len;\r
126         len = 1 + GetLen(res);\r
127         if (GetVal(res, 3) != 0)\r
128         {\r
129           len += GetLen(res);\r
130           do\r
131           {\r
132             UInt32 ppp = GetVal(res, 5) + 6;\r
133             res = RG->GetRnd();\r
134             if (ppp > 30)\r
135               continue;\r
136             rep0 = /* (1 << ppp) +*/  GetVal(res, ppp);\r
137             res = RG->GetRnd();\r
138           }\r
139           while (rep0 >= pos);\r
140           rep0++;\r
141         }\r
142 \r
143         for (UInt32 i = 0; i < len && pos < BufferSize; i++, pos++)\r
144           Buffer[pos] = Buffer[pos - rep0];\r
145       }\r
146     }\r
147   }\r
148 };\r
149 \r
150 \r
151 class CBenchmarkInStream:\r
152   public ISequentialInStream,\r
153   public CMyUnknownImp\r
154 {\r
155   const Byte *Data;\r
156   size_t Pos;\r
157   size_t Size;\r
158 public:\r
159   MY_UNKNOWN_IMP\r
160   void Init(const Byte *data, size_t size)\r
161   {\r
162     Data = data;\r
163     Size = size;\r
164     Pos = 0;\r
165   }\r
166   STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);\r
167 };\r
168 \r
169 STDMETHODIMP CBenchmarkInStream::Read(void *data, UInt32 size, UInt32 *processedSize)\r
170 {\r
171   size_t remain = Size - Pos;\r
172   UInt32 kMaxBlockSize = (1 << 20);\r
173   if (size > kMaxBlockSize)\r
174     size = kMaxBlockSize;\r
175   if (size > remain)\r
176     size = (UInt32)remain;\r
177   for (UInt32 i = 0; i < size; i++)\r
178     ((Byte *)data)[i] = Data[Pos + i];\r
179   Pos += size;\r
180   if(processedSize != NULL)\r
181     *processedSize = size;\r
182   return S_OK;\r
183 }\r
184   \r
185 class CBenchmarkOutStream:\r
186   public ISequentialOutStream,\r
187   public CBenchBuffer,\r
188   public CMyUnknownImp\r
189 {\r
190   // bool _overflow;\r
191 public:\r
192   UInt32 Pos;\r
193   // CBenchmarkOutStream(): _overflow(false) {}\r
194   void Init()\r
195   {\r
196     // _overflow = false;\r
197     Pos = 0;\r
198   }\r
199   MY_UNKNOWN_IMP\r
200   STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);\r
201 };\r
202 \r
203 STDMETHODIMP CBenchmarkOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)\r
204 {\r
205   size_t curSize = BufferSize - Pos;\r
206   if (curSize > size)\r
207     curSize = size;\r
208   memcpy(Buffer + Pos, data, curSize);\r
209   Pos += (UInt32)curSize;\r
210   if(processedSize != NULL)\r
211     *processedSize = (UInt32)curSize;\r
212   if (curSize != size)\r
213   {\r
214     // _overflow = true;\r
215     return E_FAIL;\r
216   }\r
217   return S_OK;\r
218 }\r
219   \r
220 class CCrcOutStream:\r
221   public ISequentialOutStream,\r
222   public CMyUnknownImp\r
223 {\r
224 public:\r
225   UInt32 Crc;\r
226   MY_UNKNOWN_IMP\r
227   void Init() { Crc = CRC_INIT_VAL; }\r
228   STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);\r
229 };\r
230 \r
231 STDMETHODIMP CCrcOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)\r
232 {\r
233   Crc = CrcUpdate(Crc, data, size);\r
234   if (processedSize != NULL)\r
235     *processedSize = size;\r
236   return S_OK;\r
237 }\r
238   \r
239 static UInt64 GetTimeCount()\r
240 {\r
241   #ifdef USE_POSIX_TIME\r
242   #ifdef USE_POSIX_TIME2\r
243   timeval v;\r
244   if (gettimeofday(&v, 0) == 0)\r
245     return (UInt64)(v.tv_sec) * 1000000 + v.tv_usec;\r
246   return (UInt64)time(NULL) * 1000000;\r
247   #else\r
248   return time(NULL);\r
249   #endif\r
250   #else\r
251   /*\r
252   LARGE_INTEGER value;\r
253   if (::QueryPerformanceCounter(&value))\r
254     return value.QuadPart;\r
255   */\r
256   return GetTickCount();\r
257   #endif\r
258 }\r
259 \r
260 static UInt64 GetFreq()\r
261 {\r
262   #ifdef USE_POSIX_TIME\r
263   #ifdef USE_POSIX_TIME2\r
264   return 1000000;\r
265   #else\r
266   return 1;\r
267   #endif\r
268   #else\r
269   /*\r
270   LARGE_INTEGER value;\r
271   if (::QueryPerformanceFrequency(&value))\r
272     return value.QuadPart;\r
273   */\r
274   return 1000;\r
275   #endif\r
276 }\r
277 \r
278 #ifndef USE_POSIX_TIME\r
279 static inline UInt64 GetTime64(const FILETIME &t) { return ((UInt64)t.dwHighDateTime << 32) | t.dwLowDateTime; }\r
280 #endif\r
281 \r
282 static UInt64 GetUserTime()\r
283 {\r
284   #ifdef USE_POSIX_TIME\r
285   return clock();\r
286   #else\r
287   FILETIME creationTime, exitTime, kernelTime, userTime;\r
288   if (\r
289   #ifdef UNDER_CE\r
290     ::GetThreadTimes(::GetCurrentThread()\r
291   #else\r
292     ::GetProcessTimes(::GetCurrentProcess()\r
293   #endif\r
294     , &creationTime, &exitTime, &kernelTime, &userTime) != 0)\r
295     return GetTime64(userTime) + GetTime64(kernelTime);\r
296   return (UInt64)GetTickCount() * 10000;\r
297   #endif\r
298 }\r
299 \r
300 static UInt64 GetUserFreq()\r
301 {\r
302   #ifdef USE_POSIX_TIME\r
303   return CLOCKS_PER_SEC;\r
304   #else\r
305   return 10000000;\r
306   #endif\r
307 }\r
308 \r
309 class CBenchProgressStatus\r
310 {\r
311   #ifndef _7ZIP_ST\r
312   NWindows::NSynchronization::CCriticalSection CS;\r
313   #endif\r
314 public:\r
315   HRESULT Res;\r
316   bool EncodeMode;\r
317   void SetResult(HRESULT res)\r
318   {\r
319     #ifndef _7ZIP_ST\r
320     NWindows::NSynchronization::CCriticalSectionLock lock(CS);\r
321     #endif\r
322     Res = res;\r
323   }\r
324   HRESULT GetResult()\r
325   {\r
326     #ifndef _7ZIP_ST\r
327     NWindows::NSynchronization::CCriticalSectionLock lock(CS);\r
328     #endif\r
329     return Res;\r
330   }\r
331 };\r
332 \r
333 class CBenchProgressInfo:\r
334   public ICompressProgressInfo,\r
335   public CMyUnknownImp\r
336 {\r
337 public:\r
338   CBenchProgressStatus *Status;\r
339   CBenchInfo BenchInfo;\r
340   HRESULT Res;\r
341   IBenchCallback *callback;\r
342   CBenchProgressInfo(): callback(0) {}\r
343   MY_UNKNOWN_IMP\r
344   STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);\r
345 };\r
346 \r
347 static void SetStartTime(CBenchInfo &bi)\r
348 {\r
349   bi.GlobalFreq = GetFreq();\r
350   bi.UserFreq = GetUserFreq();\r
351   bi.GlobalTime = ::GetTimeCount();\r
352   bi.UserTime = ::GetUserTime();\r
353 }\r
354 \r
355 static void SetFinishTime(const CBenchInfo &biStart, CBenchInfo &dest)\r
356 {\r
357   dest.GlobalFreq = GetFreq();\r
358   dest.UserFreq = GetUserFreq();\r
359   dest.GlobalTime = ::GetTimeCount() - biStart.GlobalTime;\r
360   dest.UserTime = ::GetUserTime() - biStart.UserTime;\r
361 }\r
362 \r
363 STDMETHODIMP CBenchProgressInfo::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)\r
364 {\r
365   HRESULT res = Status->GetResult();\r
366   if (res != S_OK)\r
367     return res;\r
368   if (!callback)\r
369     return res;\r
370   CBenchInfo info = BenchInfo;\r
371   SetFinishTime(BenchInfo, info);\r
372   if (Status->EncodeMode)\r
373   {\r
374     info.UnpackSize = *inSize;\r
375     info.PackSize = *outSize;\r
376     res = callback->SetEncodeResult(info, false);\r
377   }\r
378   else\r
379   {\r
380     info.PackSize = BenchInfo.PackSize + *inSize;\r
381     info.UnpackSize = BenchInfo.UnpackSize + *outSize;\r
382     res = callback->SetDecodeResult(info, false);\r
383   }\r
384   if (res != S_OK)\r
385     Status->SetResult(res);\r
386   return res;\r
387 }\r
388 \r
389 static const int kSubBits = 8;\r
390 \r
391 static UInt32 GetLogSize(UInt32 size)\r
392 {\r
393   for (int i = kSubBits; i < 32; i++)\r
394     for (UInt32 j = 0; j < (1 << kSubBits); j++)\r
395       if (size <= (((UInt32)1) << i) + (j << (i - kSubBits)))\r
396         return (i << kSubBits) + j;\r
397   return (32 << kSubBits);\r
398 }\r
399 \r
400 static void NormalizeVals(UInt64 &v1, UInt64 &v2)\r
401 {\r
402   while (v1 > 1000000)\r
403   {\r
404     v1 >>= 1;\r
405     v2 >>= 1;\r
406   }\r
407 }\r
408 \r
409 UInt64 GetUsage(const CBenchInfo &info)\r
410 {\r
411   UInt64 userTime = info.UserTime;\r
412   UInt64 userFreq = info.UserFreq;\r
413   UInt64 globalTime = info.GlobalTime;\r
414   UInt64 globalFreq = info.GlobalFreq;\r
415   NormalizeVals(userTime, userFreq);\r
416   NormalizeVals(globalFreq, globalTime);\r
417   if (userFreq == 0)\r
418     userFreq = 1;\r
419   if (globalTime == 0)\r
420     globalTime = 1;\r
421   return userTime * globalFreq * 1000000 / userFreq / globalTime;\r
422 }\r
423 \r
424 UInt64 GetRatingPerUsage(const CBenchInfo &info, UInt64 rating)\r
425 {\r
426   UInt64 userTime = info.UserTime;\r
427   UInt64 userFreq = info.UserFreq;\r
428   UInt64 globalTime = info.GlobalTime;\r
429   UInt64 globalFreq = info.GlobalFreq;\r
430   NormalizeVals(userFreq, userTime);\r
431   NormalizeVals(globalTime, globalFreq);\r
432   if (globalFreq == 0)\r
433     globalFreq = 1;\r
434   if (userTime == 0)\r
435     userTime = 1;\r
436   return userFreq * globalTime / globalFreq *  rating / userTime;\r
437 }\r
438 \r
439 static UInt64 MyMultDiv64(UInt64 value, UInt64 elapsedTime, UInt64 freq)\r
440 {\r
441   UInt64 elTime = elapsedTime;\r
442   NormalizeVals(freq, elTime);\r
443   if (elTime == 0)\r
444     elTime = 1;\r
445   return value * freq / elTime;\r
446 }\r
447 \r
448 UInt64 GetCompressRating(UInt32 dictionarySize, UInt64 elapsedTime, UInt64 freq, UInt64 size)\r
449 {\r
450   UInt64 t = GetLogSize(dictionarySize) - (kBenchMinDicLogSize << kSubBits);\r
451   UInt64 numCommandsForOne = 870 + ((t * t * 5) >> (2 * kSubBits));\r
452   UInt64 numCommands = (UInt64)(size) * numCommandsForOne;\r
453   return MyMultDiv64(numCommands, elapsedTime, freq);\r
454 }\r
455 \r
456 UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 freq, UInt64 outSize, UInt64 inSize, UInt32 numIterations)\r
457 {\r
458   UInt64 numCommands = (inSize * 200 + outSize * 4) * numIterations;\r
459   return MyMultDiv64(numCommands, elapsedTime, freq);\r
460 }\r
461 \r
462 struct CEncoderInfo;\r
463 \r
464 struct CEncoderInfo\r
465 {\r
466   #ifndef _7ZIP_ST\r
467   NWindows::CThread thread[2];\r
468   #endif\r
469   CMyComPtr<ICompressCoder> encoder;\r
470   CBenchProgressInfo *progressInfoSpec[2];\r
471   CMyComPtr<ICompressProgressInfo> progressInfo[2];\r
472   UInt32 NumIterations;\r
473   #ifdef USE_ALLOCA\r
474   size_t AllocaSize;\r
475   #endif\r
476 \r
477   struct CDecoderInfo\r
478   {\r
479     CEncoderInfo *Encoder;\r
480     UInt32 DecoderIndex;\r
481     #ifdef USE_ALLOCA\r
482     size_t AllocaSize;\r
483     #endif\r
484     bool CallbackMode;\r
485   };\r
486   CDecoderInfo decodersInfo[2];\r
487 \r
488   CMyComPtr<ICompressCoder> decoders[2];\r
489   HRESULT Results[2];\r
490   CBenchmarkOutStream *outStreamSpec;\r
491   CMyComPtr<ISequentialOutStream> outStream;\r
492   IBenchCallback *callback;\r
493   UInt32 crc;\r
494   UInt32 kBufferSize;\r
495   UInt32 compressedSize;\r
496   CBenchRandomGenerator rg;\r
497   CBenchmarkOutStream *propStreamSpec;\r
498   CMyComPtr<ISequentialOutStream> propStream;\r
499   HRESULT Init(UInt32 dictionarySize, UInt32 numThreads, CBaseRandomGenerator *rg);\r
500   HRESULT Encode();\r
501   HRESULT Decode(UInt32 decoderIndex);\r
502 \r
503   CEncoderInfo(): outStreamSpec(0), callback(0), propStreamSpec(0) {}\r
504 \r
505   #ifndef _7ZIP_ST\r
506   static THREAD_FUNC_DECL EncodeThreadFunction(void *param)\r
507   {\r
508     CEncoderInfo *encoder = (CEncoderInfo *)param;\r
509     #ifdef USE_ALLOCA\r
510     alloca(encoder->AllocaSize);\r
511     #endif\r
512     HRESULT res = encoder->Encode();\r
513     encoder->Results[0] = res;\r
514     if (res != S_OK)\r
515       encoder->progressInfoSpec[0]->Status->SetResult(res);\r
516 \r
517     return 0;\r
518   }\r
519   static THREAD_FUNC_DECL DecodeThreadFunction(void *param)\r
520   {\r
521     CDecoderInfo *decoder = (CDecoderInfo *)param;\r
522     #ifdef USE_ALLOCA\r
523     alloca(decoder->AllocaSize);\r
524     #endif\r
525     CEncoderInfo *encoder = decoder->Encoder;\r
526     encoder->Results[decoder->DecoderIndex] = encoder->Decode(decoder->DecoderIndex);\r
527     return 0;\r
528   }\r
529 \r
530   HRESULT CreateEncoderThread()\r
531   {\r
532     return thread[0].Create(EncodeThreadFunction, this);\r
533   }\r
534 \r
535   HRESULT CreateDecoderThread(int index, bool callbackMode\r
536       #ifdef USE_ALLOCA\r
537       , size_t allocaSize\r
538       #endif\r
539       )\r
540   {\r
541     CDecoderInfo &decoder = decodersInfo[index];\r
542     decoder.DecoderIndex = index;\r
543     decoder.Encoder = this;\r
544     #ifdef USE_ALLOCA\r
545     decoder.AllocaSize = allocaSize;\r
546     #endif\r
547     decoder.CallbackMode = callbackMode;\r
548     return thread[index].Create(DecodeThreadFunction, &decoder);\r
549   }\r
550   #endif\r
551 };\r
552 \r
553 HRESULT CEncoderInfo::Init(UInt32 dictionarySize, UInt32 numThreads, CBaseRandomGenerator *rgLoc)\r
554 {\r
555   rg.Set(rgLoc);\r
556   kBufferSize = dictionarySize + kAdditionalSize;\r
557   UInt32 kCompressedBufferSize = (kBufferSize / 2) + kCompressedAdditionalSize;\r
558   if (!rg.Alloc(kBufferSize))\r
559     return E_OUTOFMEMORY;\r
560   rg.Generate();\r
561   crc = CrcCalc(rg.Buffer, rg.BufferSize);\r
562 \r
563   outStreamSpec = new CBenchmarkOutStream;\r
564   if (!outStreamSpec->Alloc(kCompressedBufferSize))\r
565     return E_OUTOFMEMORY;\r
566 \r
567   outStream = outStreamSpec;\r
568 \r
569   propStreamSpec = 0;\r
570   if (!propStream)\r
571   {\r
572     propStreamSpec = new CBenchmarkOutStream;\r
573     propStream = propStreamSpec;\r
574   }\r
575   if (!propStreamSpec->Alloc(kMaxLzmaPropSize))\r
576     return E_OUTOFMEMORY;\r
577   propStreamSpec->Init();\r
578   \r
579   PROPID propIDs[] =\r
580   {\r
581     NCoderPropID::kDictionarySize,\r
582     NCoderPropID::kNumThreads\r
583   };\r
584   const int kNumProps = sizeof(propIDs) / sizeof(propIDs[0]);\r
585   PROPVARIANT props[kNumProps];\r
586   props[0].vt = VT_UI4;\r
587   props[0].ulVal = dictionarySize;\r
588 \r
589   props[1].vt = VT_UI4;\r
590   props[1].ulVal = numThreads;\r
591 \r
592   {\r
593     CMyComPtr<ICompressSetCoderProperties> setCoderProperties;\r
594     RINOK(encoder.QueryInterface(IID_ICompressSetCoderProperties, &setCoderProperties));\r
595     if (!setCoderProperties)\r
596       return E_FAIL;\r
597     RINOK(setCoderProperties->SetCoderProperties(propIDs, props, kNumProps));\r
598 \r
599     CMyComPtr<ICompressWriteCoderProperties> writeCoderProperties;\r
600     encoder.QueryInterface(IID_ICompressWriteCoderProperties, &writeCoderProperties);\r
601     if (writeCoderProperties)\r
602     {\r
603       RINOK(writeCoderProperties->WriteCoderProperties(propStream));\r
604     }\r
605   }\r
606   return S_OK;\r
607 }\r
608 \r
609 HRESULT CEncoderInfo::Encode()\r
610 {\r
611   CBenchmarkInStream *inStreamSpec = new CBenchmarkInStream;\r
612   CMyComPtr<ISequentialInStream> inStream = inStreamSpec;\r
613   inStreamSpec->Init(rg.Buffer, rg.BufferSize);\r
614   outStreamSpec->Init();\r
615 \r
616   RINOK(encoder->Code(inStream, outStream, 0, 0, progressInfo[0]));\r
617   compressedSize = outStreamSpec->Pos;\r
618   encoder.Release();\r
619   return S_OK;\r
620 }\r
621 \r
622 HRESULT CEncoderInfo::Decode(UInt32 decoderIndex)\r
623 {\r
624   CBenchmarkInStream *inStreamSpec = new CBenchmarkInStream;\r
625   CMyComPtr<ISequentialInStream> inStream = inStreamSpec;\r
626   CMyComPtr<ICompressCoder> &decoder = decoders[decoderIndex];\r
627 \r
628   CMyComPtr<ICompressSetDecoderProperties2> compressSetDecoderProperties;\r
629   decoder.QueryInterface(IID_ICompressSetDecoderProperties2, &compressSetDecoderProperties);\r
630   if (!compressSetDecoderProperties)\r
631     return E_FAIL;\r
632 \r
633   CCrcOutStream *crcOutStreamSpec = new CCrcOutStream;\r
634   CMyComPtr<ISequentialOutStream> crcOutStream = crcOutStreamSpec;\r
635     \r
636   CBenchProgressInfo *pi = progressInfoSpec[decoderIndex];\r
637   pi->BenchInfo.UnpackSize = 0;\r
638   pi->BenchInfo.PackSize = 0;\r
639 \r
640   for (UInt32 j = 0; j < NumIterations; j++)\r
641   {\r
642     inStreamSpec->Init(outStreamSpec->Buffer, compressedSize);\r
643     crcOutStreamSpec->Init();\r
644     \r
645     RINOK(compressSetDecoderProperties->SetDecoderProperties2(propStreamSpec->Buffer, propStreamSpec->Pos));\r
646     UInt64 outSize = kBufferSize;\r
647     RINOK(decoder->Code(inStream, crcOutStream, 0, &outSize, progressInfo[decoderIndex]));\r
648     if (CRC_GET_DIGEST(crcOutStreamSpec->Crc) != crc)\r
649       return S_FALSE;\r
650     pi->BenchInfo.UnpackSize += kBufferSize;\r
651     pi->BenchInfo.PackSize += compressedSize;\r
652   }\r
653   decoder.Release();\r
654   return S_OK;\r
655 }\r
656 \r
657 static const UInt32 kNumThreadsMax = (1 << 16);\r
658 \r
659 struct CBenchEncoders\r
660 {\r
661   CEncoderInfo *encoders;\r
662   CBenchEncoders(UInt32 num): encoders(0) { encoders = new CEncoderInfo[num]; }\r
663   ~CBenchEncoders() { delete []encoders; }\r
664 };\r
665 \r
666 HRESULT LzmaBench(\r
667   DECL_EXTERNAL_CODECS_LOC_VARS\r
668   UInt32 numThreads, UInt32 dictionarySize, IBenchCallback *callback)\r
669 {\r
670   UInt32 numEncoderThreads =\r
671     #ifndef _7ZIP_ST\r
672     (numThreads > 1 ? numThreads / 2 : 1);\r
673     #else\r
674     1;\r
675     #endif\r
676   UInt32 numSubDecoderThreads =\r
677     #ifndef _7ZIP_ST\r
678     (numThreads > 1 ? 2 : 1);\r
679     #else\r
680     1;\r
681     #endif\r
682   if (dictionarySize < (1 << kBenchMinDicLogSize) || numThreads < 1 || numEncoderThreads > kNumThreadsMax)\r
683   {\r
684     return E_INVALIDARG;\r
685   }\r
686 \r
687   CBenchEncoders encodersSpec(numEncoderThreads);\r
688   CEncoderInfo *encoders = encodersSpec.encoders;\r
689 \r
690 \r
691   UInt32 i;\r
692   for (i = 0; i < numEncoderThreads; i++)\r
693   {\r
694     CEncoderInfo &encoder = encoders[i];\r
695     encoder.callback = (i == 0) ? callback : 0;\r
696 \r
697     const UInt32 kLzmaId = 0x030101;\r
698     RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS kLzmaId, encoder.encoder, true));\r
699     if (!encoder.encoder)\r
700       return E_NOTIMPL;\r
701     for (UInt32 j = 0; j < numSubDecoderThreads; j++)\r
702     {\r
703       RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS kLzmaId, encoder.decoders[j], false));\r
704       if (!encoder.decoders[j])\r
705         return E_NOTIMPL;\r
706     }\r
707   }\r
708 \r
709   CBaseRandomGenerator rg;\r
710   rg.Init();\r
711   for (i = 0; i < numEncoderThreads; i++)\r
712   {\r
713     RINOK(encoders[i].Init(dictionarySize, numThreads, &rg));\r
714   }\r
715 \r
716   CBenchProgressStatus status;\r
717   status.Res = S_OK;\r
718   status.EncodeMode = true;\r
719 \r
720   for (i = 0; i < numEncoderThreads; i++)\r
721   {\r
722     CEncoderInfo &encoder = encoders[i];\r
723     for (int j = 0; j < 2; j++)\r
724     {\r
725       encoder.progressInfo[j] = encoder.progressInfoSpec[j] = new CBenchProgressInfo;\r
726       encoder.progressInfoSpec[j]->Status = &status;\r
727     }\r
728     if (i == 0)\r
729     {\r
730       encoder.progressInfoSpec[0]->callback = callback;\r
731       encoder.progressInfoSpec[0]->BenchInfo.NumIterations = numEncoderThreads;\r
732       SetStartTime(encoder.progressInfoSpec[0]->BenchInfo);\r
733     }\r
734 \r
735     #ifndef _7ZIP_ST\r
736     if (numEncoderThreads > 1)\r
737     {\r
738       #ifdef USE_ALLOCA\r
739       encoder.AllocaSize = (i * 16 * 21) & 0x7FF;\r
740       #endif\r
741       RINOK(encoder.CreateEncoderThread())\r
742     }\r
743     else\r
744     #endif\r
745     {\r
746       RINOK(encoder.Encode());\r
747     }\r
748   }\r
749   #ifndef _7ZIP_ST\r
750   if (numEncoderThreads > 1)\r
751     for (i = 0; i < numEncoderThreads; i++)\r
752       encoders[i].thread[0].Wait();\r
753   #endif\r
754 \r
755   RINOK(status.Res);\r
756 \r
757   CBenchInfo info;\r
758 \r
759   SetFinishTime(encoders[0].progressInfoSpec[0]->BenchInfo, info);\r
760   info.UnpackSize = 0;\r
761   info.PackSize = 0;\r
762   info.NumIterations = 1; // progressInfoSpec->NumIterations;\r
763   for (i = 0; i < numEncoderThreads; i++)\r
764   {\r
765     CEncoderInfo &encoder = encoders[i];\r
766     info.UnpackSize += encoder.kBufferSize;\r
767     info.PackSize += encoder.compressedSize;\r
768   }\r
769   RINOK(callback->SetEncodeResult(info, true));\r
770 \r
771 \r
772   status.Res = S_OK;\r
773   status.EncodeMode = false;\r
774 \r
775   UInt32 numDecoderThreads = numEncoderThreads * numSubDecoderThreads;\r
776   for (i = 0; i < numEncoderThreads; i++)\r
777   {\r
778     CEncoderInfo &encoder = encoders[i];\r
779     encoder.NumIterations = 2 + kUncompressMinBlockSize / encoder.kBufferSize;\r
780 \r
781     if (i == 0)\r
782     {\r
783       encoder.progressInfoSpec[0]->callback = callback;\r
784       encoder.progressInfoSpec[0]->BenchInfo.NumIterations = numDecoderThreads;\r
785       SetStartTime(encoder.progressInfoSpec[0]->BenchInfo);\r
786     }\r
787 \r
788     #ifndef _7ZIP_ST\r
789     if (numDecoderThreads > 1)\r
790     {\r
791       for (UInt32 j = 0; j < numSubDecoderThreads; j++)\r
792       {\r
793         HRESULT res = encoder.CreateDecoderThread(j, (i == 0 && j == 0)\r
794             #ifdef USE_ALLOCA\r
795             , ((i * numSubDecoderThreads + j) * 16 * 21) & 0x7FF\r
796             #endif\r
797             );\r
798         RINOK(res);\r
799       }\r
800     }\r
801     else\r
802     #endif\r
803     {\r
804       RINOK(encoder.Decode(0));\r
805     }\r
806   }\r
807   #ifndef _7ZIP_ST\r
808   HRESULT res = S_OK;\r
809   if (numDecoderThreads > 1)\r
810     for (i = 0; i < numEncoderThreads; i++)\r
811       for (UInt32 j = 0; j < numSubDecoderThreads; j++)\r
812       {\r
813         CEncoderInfo &encoder = encoders[i];\r
814         encoder.thread[j].Wait();\r
815         if (encoder.Results[j] != S_OK)\r
816           res = encoder.Results[j];\r
817       }\r
818   RINOK(res);\r
819   #endif\r
820   RINOK(status.Res);\r
821   SetFinishTime(encoders[0].progressInfoSpec[0]->BenchInfo, info);\r
822   #ifndef _7ZIP_ST\r
823   #ifdef UNDER_CE\r
824   if (numDecoderThreads > 1)\r
825     for (i = 0; i < numEncoderThreads; i++)\r
826       for (UInt32 j = 0; j < numSubDecoderThreads; j++)\r
827       {\r
828         FILETIME creationTime, exitTime, kernelTime, userTime;\r
829         if (::GetThreadTimes(encoders[i].thread[j], &creationTime, &exitTime, &kernelTime, &userTime) != 0)\r
830           info.UserTime += GetTime64(userTime) + GetTime64(kernelTime);\r
831       }\r
832   #endif\r
833   #endif\r
834   info.UnpackSize = 0;\r
835   info.PackSize = 0;\r
836   info.NumIterations = numSubDecoderThreads * encoders[0].NumIterations;\r
837   for (i = 0; i < numEncoderThreads; i++)\r
838   {\r
839     CEncoderInfo &encoder = encoders[i];\r
840     info.UnpackSize += encoder.kBufferSize;\r
841     info.PackSize += encoder.compressedSize;\r
842   }\r
843   RINOK(callback->SetDecodeResult(info, false));\r
844   RINOK(callback->SetDecodeResult(info, true));\r
845   return S_OK;\r
846 }\r
847 \r
848 \r
849 inline UInt64 GetLZMAUsage(bool multiThread, UInt32 dictionary)\r
850 {\r
851   UInt32 hs = dictionary - 1;\r
852   hs |= (hs >> 1);\r
853   hs |= (hs >> 2);\r
854   hs |= (hs >> 4);\r
855   hs |= (hs >> 8);\r
856   hs >>= 1;\r
857   hs |= 0xFFFF;\r
858   if (hs > (1 << 24))\r
859     hs >>= 1;\r
860   hs++;\r
861   return ((hs + (1 << 16)) + (UInt64)dictionary * 2) * 4 + (UInt64)dictionary * 3 / 2 +\r
862       (1 << 20) + (multiThread ? (6 << 20) : 0);\r
863 }\r
864 \r
865 UInt64 GetBenchMemoryUsage(UInt32 numThreads, UInt32 dictionary)\r
866 {\r
867   const UInt32 kBufferSize = dictionary;\r
868   const UInt32 kCompressedBufferSize = (kBufferSize / 2);\r
869   UInt32 numSubThreads = (numThreads > 1) ? 2 : 1;\r
870   UInt32 numBigThreads = numThreads / numSubThreads;\r
871   return (kBufferSize + kCompressedBufferSize +\r
872     GetLZMAUsage((numThreads > 1), dictionary) + (2 << 20)) * numBigThreads;\r
873 }\r
874 \r
875 static bool CrcBig(const void *data, UInt32 size, UInt32 numCycles, UInt32 crcBase)\r
876 {\r
877   for (UInt32 i = 0; i < numCycles; i++)\r
878     if (CrcCalc(data, size) != crcBase)\r
879       return false;\r
880   return true;\r
881 }\r
882 \r
883 #ifndef _7ZIP_ST\r
884 struct CCrcInfo\r
885 {\r
886   NWindows::CThread Thread;\r
887   const Byte *Data;\r
888   UInt32 Size;\r
889   UInt32 NumCycles;\r
890   UInt32 Crc;\r
891   bool Res;\r
892   void Wait()\r
893   {\r
894     Thread.Wait();\r
895     Thread.Close();\r
896   }\r
897 };\r
898 \r
899 static THREAD_FUNC_DECL CrcThreadFunction(void *param)\r
900 {\r
901   CCrcInfo *p = (CCrcInfo *)param;\r
902   p->Res = CrcBig(p->Data, p->Size, p->NumCycles, p->Crc);\r
903   return 0;\r
904 }\r
905 \r
906 struct CCrcThreads\r
907 {\r
908   UInt32 NumThreads;\r
909   CCrcInfo *Items;\r
910   CCrcThreads(): Items(0), NumThreads(0) {}\r
911   void WaitAll()\r
912   {\r
913     for (UInt32 i = 0; i < NumThreads; i++)\r
914       Items[i].Wait();\r
915     NumThreads = 0;\r
916   }\r
917   ~CCrcThreads()\r
918   {\r
919     WaitAll();\r
920     delete []Items;\r
921   }\r
922 };\r
923 #endif\r
924 \r
925 static UInt32 CrcCalc1(const Byte *buf, UInt32 size)\r
926 {\r
927   UInt32 crc = CRC_INIT_VAL;;\r
928   for (UInt32 i = 0; i < size; i++)\r
929     crc = CRC_UPDATE_BYTE(crc, buf[i]);\r
930   return CRC_GET_DIGEST(crc);\r
931 }\r
932 \r
933 static void RandGen(Byte *buf, UInt32 size, CBaseRandomGenerator &RG)\r
934 {\r
935   for (UInt32 i = 0; i < size; i++)\r
936     buf[i] = (Byte)RG.GetRnd();\r
937 }\r
938 \r
939 static UInt32 RandGenCrc(Byte *buf, UInt32 size, CBaseRandomGenerator &RG)\r
940 {\r
941   RandGen(buf, size, RG);\r
942   return CrcCalc1(buf, size);\r
943 }\r
944 \r
945 bool CrcInternalTest()\r
946 {\r
947   CBenchBuffer buffer;\r
948   const UInt32 kBufferSize0 = (1 << 8);\r
949   const UInt32 kBufferSize1 = (1 << 10);\r
950   const UInt32 kCheckSize = (1 << 5);\r
951   if (!buffer.Alloc(kBufferSize0 + kBufferSize1))\r
952     return false;\r
953   Byte *buf = buffer.Buffer;\r
954   UInt32 i;\r
955   for (i = 0; i < kBufferSize0; i++)\r
956     buf[i] = (Byte)i;\r
957   UInt32 crc1 = CrcCalc1(buf, kBufferSize0);\r
958   if (crc1 != 0x29058C73)\r
959     return false;\r
960   CBaseRandomGenerator RG;\r
961   RandGen(buf + kBufferSize0, kBufferSize1, RG);\r
962   for (i = 0; i < kBufferSize0 + kBufferSize1 - kCheckSize; i++)\r
963     for (UInt32 j = 0; j < kCheckSize; j++)\r
964       if (CrcCalc1(buf + i, j) != CrcCalc(buf + i, j))\r
965         return false;\r
966   return true;\r
967 }\r
968 \r
969 HRESULT CrcBench(UInt32 numThreads, UInt32 bufferSize, UInt64 &speed)\r
970 {\r
971   if (numThreads == 0)\r
972     numThreads = 1;\r
973 \r
974   CBenchBuffer buffer;\r
975   size_t totalSize = (size_t)bufferSize * numThreads;\r
976   if (totalSize / numThreads != bufferSize)\r
977     return E_OUTOFMEMORY;\r
978   if (!buffer.Alloc(totalSize))\r
979     return E_OUTOFMEMORY;\r
980 \r
981   Byte *buf = buffer.Buffer;\r
982   CBaseRandomGenerator RG;\r
983   UInt32 numCycles = (kCrcBlockSize) / ((bufferSize >> 2) + 1) + 1;\r
984 \r
985   UInt64 timeVal;\r
986   #ifndef _7ZIP_ST\r
987   CCrcThreads threads;\r
988   if (numThreads > 1)\r
989   {\r
990     threads.Items = new CCrcInfo[numThreads];\r
991     UInt32 i;\r
992     for (i = 0; i < numThreads; i++)\r
993     {\r
994       CCrcInfo &info = threads.Items[i];\r
995       Byte *data = buf + (size_t)bufferSize * i;\r
996       info.Data = data;\r
997       info.NumCycles = numCycles;\r
998       info.Size = bufferSize;\r
999       info.Crc = RandGenCrc(data, bufferSize, RG);\r
1000     }\r
1001     timeVal = GetTimeCount();\r
1002     for (i = 0; i < numThreads; i++)\r
1003     {\r
1004       CCrcInfo &info = threads.Items[i];\r
1005       RINOK(info.Thread.Create(CrcThreadFunction, &info));\r
1006       threads.NumThreads++;\r
1007     }\r
1008     threads.WaitAll();\r
1009     for (i = 0; i < numThreads; i++)\r
1010       if (!threads.Items[i].Res)\r
1011         return S_FALSE;\r
1012   }\r
1013   else\r
1014   #endif\r
1015   {\r
1016     UInt32 crc = RandGenCrc(buf, bufferSize, RG);\r
1017     timeVal = GetTimeCount();\r
1018     if (!CrcBig(buf, bufferSize, numCycles, crc))\r
1019       return S_FALSE;\r
1020   }\r
1021   timeVal = GetTimeCount() - timeVal;\r
1022   if (timeVal == 0)\r
1023     timeVal = 1;\r
1024 \r
1025   UInt64 size = (UInt64)numCycles * totalSize;\r
1026   speed = MyMultDiv64(size, timeVal, GetFreq());\r
1027   return S_OK;\r
1028 }\r