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