blob: 282f405f1e57d551782b4d90ea18c63170cdde1e [file] [log] [blame]
// Bench.cpp
#include "StdAfx.h"
#include "Bench.h"
#ifndef _WIN32
#define USE_POSIX_TIME
#define USE_POSIX_TIME2
#endif
#ifdef USE_POSIX_TIME
#include <time.h>
#ifdef USE_POSIX_TIME2
#include <sys/time.h>
#endif
#endif
#ifdef _WIN32
#define USE_ALLOCA
#endif
#ifdef USE_ALLOCA
#ifdef _WIN32
#include <malloc.h>
#else
#include <stdlib.h>
#endif
#endif
#include "../../../../C/7zCrc.h"
#include "../../../../C/Alloc.h"
#ifndef _7ZIP_ST
#include "../../../Windows/Synchronization.h"
#include "../../../Windows/Thread.h"
#endif
#include "../../../Windows/PropVariant.h"
static const UInt32 kUncompressMinBlockSize =
#ifdef UNDER_CE
1 << 24;
#else
1 << 26;
#endif
static const UInt32 kCrcBlockSize =
#ifdef UNDER_CE
1 << 25;
#else
1 << 30;
#endif
static const UInt32 kAdditionalSize = (1 << 16);
static const UInt32 kCompressedAdditionalSize = (1 << 10);
static const UInt32 kMaxLzmaPropSize = 5;
class CBaseRandomGenerator
{
UInt32 A1;
UInt32 A2;
public:
CBaseRandomGenerator() { Init(); }
void Init() { A1 = 362436069; A2 = 521288629;}
UInt32 GetRnd()
{
return
((A1 = 36969 * (A1 & 0xffff) + (A1 >> 16)) << 16) +
((A2 = 18000 * (A2 & 0xffff) + (A2 >> 16)) );
}
};
class CBenchBuffer
{
public:
size_t BufferSize;
Byte *Buffer;
CBenchBuffer(): Buffer(0) {}
virtual ~CBenchBuffer() { Free(); }
void Free()
{
::MidFree(Buffer);
Buffer = 0;
}
bool Alloc(size_t bufferSize)
{
if (Buffer != 0 && BufferSize == bufferSize)
return true;
Free();
Buffer = (Byte *)::MidAlloc(bufferSize);
BufferSize = bufferSize;
return (Buffer != 0);
}
};
class CBenchRandomGenerator: public CBenchBuffer
{
CBaseRandomGenerator *RG;
public:
void Set(CBaseRandomGenerator *rg) { RG = rg; }
UInt32 GetVal(UInt32 &res, int numBits)
{
UInt32 val = res & (((UInt32)1 << numBits) - 1);
res >>= numBits;
return val;
}
UInt32 GetLen(UInt32 &res)
{
UInt32 len = GetVal(res, 2);
return GetVal(res, 1 + len);
}
void Generate()
{
UInt32 pos = 0;
UInt32 rep0 = 1;
while (pos < BufferSize)
{
UInt32 res = RG->GetRnd();
res >>= 1;
if (GetVal(res, 1) == 0 || pos < 1024)
Buffer[pos++] = (Byte)(res & 0xFF);
else
{
UInt32 len;
len = 1 + GetLen(res);
if (GetVal(res, 3) != 0)
{
len += GetLen(res);
do
{
UInt32 ppp = GetVal(res, 5) + 6;
res = RG->GetRnd();
if (ppp > 30)
continue;
rep0 = /* (1 << ppp) +*/ GetVal(res, ppp);
res = RG->GetRnd();
}
while (rep0 >= pos);
rep0++;
}
for (UInt32 i = 0; i < len && pos < BufferSize; i++, pos++)
Buffer[pos] = Buffer[pos - rep0];
}
}
}
};
class CBenchmarkInStream:
public ISequentialInStream,
public CMyUnknownImp
{
const Byte *Data;
size_t Pos;
size_t Size;
public:
MY_UNKNOWN_IMP
void Init(const Byte *data, size_t size)
{
Data = data;
Size = size;
Pos = 0;
}
STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
};
STDMETHODIMP CBenchmarkInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
{
size_t remain = Size - Pos;
UInt32 kMaxBlockSize = (1 << 20);
if (size > kMaxBlockSize)
size = kMaxBlockSize;
if (size > remain)
size = (UInt32)remain;
for (UInt32 i = 0; i < size; i++)
((Byte *)data)[i] = Data[Pos + i];
Pos += size;
if(processedSize != NULL)
*processedSize = size;
return S_OK;
}
class CBenchmarkOutStream:
public ISequentialOutStream,
public CBenchBuffer,
public CMyUnknownImp
{
// bool _overflow;
public:
UInt32 Pos;
// CBenchmarkOutStream(): _overflow(false) {}
void Init()
{
// _overflow = false;
Pos = 0;
}
MY_UNKNOWN_IMP
STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
};
STDMETHODIMP CBenchmarkOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
{
size_t curSize = BufferSize - Pos;
if (curSize > size)
curSize = size;
memcpy(Buffer + Pos, data, curSize);
Pos += (UInt32)curSize;
if(processedSize != NULL)
*processedSize = (UInt32)curSize;
if (curSize != size)
{
// _overflow = true;
return E_FAIL;
}
return S_OK;
}
class CCrcOutStream:
public ISequentialOutStream,
public CMyUnknownImp
{
public:
UInt32 Crc;
MY_UNKNOWN_IMP
void Init() { Crc = CRC_INIT_VAL; }
STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
};
STDMETHODIMP CCrcOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
{
Crc = CrcUpdate(Crc, data, size);
if (processedSize != NULL)
*processedSize = size;
return S_OK;
}
static UInt64 GetTimeCount()
{
#ifdef USE_POSIX_TIME
#ifdef USE_POSIX_TIME2
timeval v;
if (gettimeofday(&v, 0) == 0)
return (UInt64)(v.tv_sec) * 1000000 + v.tv_usec;
return (UInt64)time(NULL) * 1000000;
#else
return time(NULL);
#endif
#else
/*
LARGE_INTEGER value;
if (::QueryPerformanceCounter(&value))
return value.QuadPart;
*/
return GetTickCount();
#endif
}
static UInt64 GetFreq()
{
#ifdef USE_POSIX_TIME
#ifdef USE_POSIX_TIME2
return 1000000;
#else
return 1;
#endif
#else
/*
LARGE_INTEGER value;
if (::QueryPerformanceFrequency(&value))
return value.QuadPart;
*/
return 1000;
#endif
}
#ifndef USE_POSIX_TIME
static inline UInt64 GetTime64(const FILETIME &t) { return ((UInt64)t.dwHighDateTime << 32) | t.dwLowDateTime; }
#endif
static UInt64 GetUserTime()
{
#ifdef USE_POSIX_TIME
return clock();
#else
FILETIME creationTime, exitTime, kernelTime, userTime;
if (
#ifdef UNDER_CE
::GetThreadTimes(::GetCurrentThread()
#else
::GetProcessTimes(::GetCurrentProcess()
#endif
, &creationTime, &exitTime, &kernelTime, &userTime) != 0)
return GetTime64(userTime) + GetTime64(kernelTime);
return (UInt64)GetTickCount() * 10000;
#endif
}
static UInt64 GetUserFreq()
{
#ifdef USE_POSIX_TIME
return CLOCKS_PER_SEC;
#else
return 10000000;
#endif
}
class CBenchProgressStatus
{
#ifndef _7ZIP_ST
NWindows::NSynchronization::CCriticalSection CS;
#endif
public:
HRESULT Res;
bool EncodeMode;
void SetResult(HRESULT res)
{
#ifndef _7ZIP_ST
NWindows::NSynchronization::CCriticalSectionLock lock(CS);
#endif
Res = res;
}
HRESULT GetResult()
{
#ifndef _7ZIP_ST
NWindows::NSynchronization::CCriticalSectionLock lock(CS);
#endif
return Res;
}
};
class CBenchProgressInfo:
public ICompressProgressInfo,
public CMyUnknownImp
{
public:
CBenchProgressStatus *Status;
CBenchInfo BenchInfo;
HRESULT Res;
IBenchCallback *callback;
CBenchProgressInfo(): callback(0) {}
MY_UNKNOWN_IMP
STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
};
static void SetStartTime(CBenchInfo &bi)
{
bi.GlobalFreq = GetFreq();
bi.UserFreq = GetUserFreq();
bi.GlobalTime = ::GetTimeCount();
bi.UserTime = ::GetUserTime();
}
static void SetFinishTime(const CBenchInfo &biStart, CBenchInfo &dest)
{
dest.GlobalFreq = GetFreq();
dest.UserFreq = GetUserFreq();
dest.GlobalTime = ::GetTimeCount() - biStart.GlobalTime;
dest.UserTime = ::GetUserTime() - biStart.UserTime;
}
STDMETHODIMP CBenchProgressInfo::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
{
HRESULT res = Status->GetResult();
if (res != S_OK)
return res;
if (!callback)
return res;
CBenchInfo info = BenchInfo;
SetFinishTime(BenchInfo, info);
if (Status->EncodeMode)
{
info.UnpackSize = *inSize;
info.PackSize = *outSize;
res = callback->SetEncodeResult(info, false);
}
else
{
info.PackSize = BenchInfo.PackSize + *inSize;
info.UnpackSize = BenchInfo.UnpackSize + *outSize;
res = callback->SetDecodeResult(info, false);
}
if (res != S_OK)
Status->SetResult(res);
return res;
}
static const int kSubBits = 8;
static UInt32 GetLogSize(UInt32 size)
{
for (int i = kSubBits; i < 32; i++)
for (UInt32 j = 0; j < (1 << kSubBits); j++)
if (size <= (((UInt32)1) << i) + (j << (i - kSubBits)))
return (i << kSubBits) + j;
return (32 << kSubBits);
}
static void NormalizeVals(UInt64 &v1, UInt64 &v2)
{
while (v1 > 1000000)
{
v1 >>= 1;
v2 >>= 1;
}
}
UInt64 GetUsage(const CBenchInfo &info)
{
UInt64 userTime = info.UserTime;
UInt64 userFreq = info.UserFreq;
UInt64 globalTime = info.GlobalTime;
UInt64 globalFreq = info.GlobalFreq;
NormalizeVals(userTime, userFreq);
NormalizeVals(globalFreq, globalTime);
if (userFreq == 0)
userFreq = 1;
if (globalTime == 0)
globalTime = 1;
return userTime * globalFreq * 1000000 / userFreq / globalTime;
}
UInt64 GetRatingPerUsage(const CBenchInfo &info, UInt64 rating)
{
UInt64 userTime = info.UserTime;
UInt64 userFreq = info.UserFreq;
UInt64 globalTime = info.GlobalTime;
UInt64 globalFreq = info.GlobalFreq;
NormalizeVals(userFreq, userTime);
NormalizeVals(globalTime, globalFreq);
if (globalFreq == 0)
globalFreq = 1;
if (userTime == 0)
userTime = 1;
return userFreq * globalTime / globalFreq * rating / userTime;
}
static UInt64 MyMultDiv64(UInt64 value, UInt64 elapsedTime, UInt64 freq)
{
UInt64 elTime = elapsedTime;
NormalizeVals(freq, elTime);
if (elTime == 0)
elTime = 1;
return value * freq / elTime;
}
UInt64 GetCompressRating(UInt32 dictionarySize, UInt64 elapsedTime, UInt64 freq, UInt64 size)
{
UInt64 t = GetLogSize(dictionarySize) - (kBenchMinDicLogSize << kSubBits);
UInt64 numCommandsForOne = 870 + ((t * t * 5) >> (2 * kSubBits));
UInt64 numCommands = (UInt64)(size) * numCommandsForOne;
return MyMultDiv64(numCommands, elapsedTime, freq);
}
UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 freq, UInt64 outSize, UInt64 inSize, UInt32 numIterations)
{
UInt64 numCommands = (inSize * 200 + outSize * 4) * numIterations;
return MyMultDiv64(numCommands, elapsedTime, freq);
}
struct CEncoderInfo;
struct CEncoderInfo
{
#ifndef _7ZIP_ST
NWindows::CThread thread[2];
#endif
CMyComPtr<ICompressCoder> encoder;
CBenchProgressInfo *progressInfoSpec[2];
CMyComPtr<ICompressProgressInfo> progressInfo[2];
UInt32 NumIterations;
#ifdef USE_ALLOCA
size_t AllocaSize;
#endif
struct CDecoderInfo
{
CEncoderInfo *Encoder;
UInt32 DecoderIndex;
#ifdef USE_ALLOCA
size_t AllocaSize;
#endif
bool CallbackMode;
};
CDecoderInfo decodersInfo[2];
CMyComPtr<ICompressCoder> decoders[2];
HRESULT Results[2];
CBenchmarkOutStream *outStreamSpec;
CMyComPtr<ISequentialOutStream> outStream;
IBenchCallback *callback;
UInt32 crc;
UInt32 kBufferSize;
UInt32 compressedSize;
CBenchRandomGenerator rg;
CBenchmarkOutStream *propStreamSpec;
CMyComPtr<ISequentialOutStream> propStream;
HRESULT Init(UInt32 dictionarySize, UInt32 numThreads, CBaseRandomGenerator *rg);
HRESULT Encode();
HRESULT Decode(UInt32 decoderIndex);
CEncoderInfo(): outStreamSpec(0), callback(0), propStreamSpec(0) {}
#ifndef _7ZIP_ST
static THREAD_FUNC_DECL EncodeThreadFunction(void *param)
{
CEncoderInfo *encoder = (CEncoderInfo *)param;
#ifdef USE_ALLOCA
alloca(encoder->AllocaSize);
#endif
HRESULT res = encoder->Encode();
encoder->Results[0] = res;
if (res != S_OK)
encoder->progressInfoSpec[0]->Status->SetResult(res);
return 0;
}
static THREAD_FUNC_DECL DecodeThreadFunction(void *param)
{
CDecoderInfo *decoder = (CDecoderInfo *)param;
#ifdef USE_ALLOCA
alloca(decoder->AllocaSize);
#endif
CEncoderInfo *encoder = decoder->Encoder;
encoder->Results[decoder->DecoderIndex] = encoder->Decode(decoder->DecoderIndex);
return 0;
}
HRESULT CreateEncoderThread()
{
return thread[0].Create(EncodeThreadFunction, this);
}
HRESULT CreateDecoderThread(int index, bool callbackMode
#ifdef USE_ALLOCA
, size_t allocaSize
#endif
)
{
CDecoderInfo &decoder = decodersInfo[index];
decoder.DecoderIndex = index;
decoder.Encoder = this;
#ifdef USE_ALLOCA
decoder.AllocaSize = allocaSize;
#endif
decoder.CallbackMode = callbackMode;
return thread[index].Create(DecodeThreadFunction, &decoder);
}
#endif
};
HRESULT CEncoderInfo::Init(UInt32 dictionarySize, UInt32 numThreads, CBaseRandomGenerator *rgLoc)
{
rg.Set(rgLoc);
kBufferSize = dictionarySize + kAdditionalSize;
UInt32 kCompressedBufferSize = (kBufferSize / 2) + kCompressedAdditionalSize;
if (!rg.Alloc(kBufferSize))
return E_OUTOFMEMORY;
rg.Generate();
crc = CrcCalc(rg.Buffer, rg.BufferSize);
outStreamSpec = new CBenchmarkOutStream;
if (!outStreamSpec->Alloc(kCompressedBufferSize))
return E_OUTOFMEMORY;
outStream = outStreamSpec;
propStreamSpec = 0;
if (!propStream)
{
propStreamSpec = new CBenchmarkOutStream;
propStream = propStreamSpec;
}
if (!propStreamSpec->Alloc(kMaxLzmaPropSize))
return E_OUTOFMEMORY;
propStreamSpec->Init();
PROPID propIDs[] =
{
NCoderPropID::kDictionarySize,
NCoderPropID::kNumThreads
};
const int kNumProps = sizeof(propIDs) / sizeof(propIDs[0]);
PROPVARIANT props[kNumProps];
props[0].vt = VT_UI4;
props[0].ulVal = dictionarySize;
props[1].vt = VT_UI4;
props[1].ulVal = numThreads;
{
CMyComPtr<ICompressSetCoderProperties> setCoderProperties;
RINOK(encoder.QueryInterface(IID_ICompressSetCoderProperties, &setCoderProperties));
if (!setCoderProperties)
return E_FAIL;
RINOK(setCoderProperties->SetCoderProperties(propIDs, props, kNumProps));
CMyComPtr<ICompressWriteCoderProperties> writeCoderProperties;
encoder.QueryInterface(IID_ICompressWriteCoderProperties, &writeCoderProperties);
if (writeCoderProperties)
{
RINOK(writeCoderProperties->WriteCoderProperties(propStream));
}
}
return S_OK;
}
HRESULT CEncoderInfo::Encode()
{
CBenchmarkInStream *inStreamSpec = new CBenchmarkInStream;
CMyComPtr<ISequentialInStream> inStream = inStreamSpec;
inStreamSpec->Init(rg.Buffer, rg.BufferSize);
outStreamSpec->Init();
RINOK(encoder->Code(inStream, outStream, 0, 0, progressInfo[0]));
compressedSize = outStreamSpec->Pos;
encoder.Release();
return S_OK;
}
HRESULT CEncoderInfo::Decode(UInt32 decoderIndex)
{
CBenchmarkInStream *inStreamSpec = new CBenchmarkInStream;
CMyComPtr<ISequentialInStream> inStream = inStreamSpec;
CMyComPtr<ICompressCoder> &decoder = decoders[decoderIndex];
CMyComPtr<ICompressSetDecoderProperties2> compressSetDecoderProperties;
decoder.QueryInterface(IID_ICompressSetDecoderProperties2, &compressSetDecoderProperties);
if (!compressSetDecoderProperties)
return E_FAIL;
CCrcOutStream *crcOutStreamSpec = new CCrcOutStream;
CMyComPtr<ISequentialOutStream> crcOutStream = crcOutStreamSpec;
CBenchProgressInfo *pi = progressInfoSpec[decoderIndex];
pi->BenchInfo.UnpackSize = 0;
pi->BenchInfo.PackSize = 0;
for (UInt32 j = 0; j < NumIterations; j++)
{
inStreamSpec->Init(outStreamSpec->Buffer, compressedSize);
crcOutStreamSpec->Init();
RINOK(compressSetDecoderProperties->SetDecoderProperties2(propStreamSpec->Buffer, propStreamSpec->Pos));
UInt64 outSize = kBufferSize;
RINOK(decoder->Code(inStream, crcOutStream, 0, &outSize, progressInfo[decoderIndex]));
if (CRC_GET_DIGEST(crcOutStreamSpec->Crc) != crc)
return S_FALSE;
pi->BenchInfo.UnpackSize += kBufferSize;
pi->BenchInfo.PackSize += compressedSize;
}
decoder.Release();
return S_OK;
}
static const UInt32 kNumThreadsMax = (1 << 16);
struct CBenchEncoders
{
CEncoderInfo *encoders;
CBenchEncoders(UInt32 num): encoders(0) { encoders = new CEncoderInfo[num]; }
~CBenchEncoders() { delete []encoders; }
};
HRESULT LzmaBench(
DECL_EXTERNAL_CODECS_LOC_VARS
UInt32 numThreads, UInt32 dictionarySize, IBenchCallback *callback)
{
UInt32 numEncoderThreads =
#ifndef _7ZIP_ST
(numThreads > 1 ? numThreads / 2 : 1);
#else
1;
#endif
UInt32 numSubDecoderThreads =
#ifndef _7ZIP_ST
(numThreads > 1 ? 2 : 1);
#else
1;
#endif
if (dictionarySize < (1 << kBenchMinDicLogSize) || numThreads < 1 || numEncoderThreads > kNumThreadsMax)
{
return E_INVALIDARG;
}
CBenchEncoders encodersSpec(numEncoderThreads);
CEncoderInfo *encoders = encodersSpec.encoders;
UInt32 i;
for (i = 0; i < numEncoderThreads; i++)
{
CEncoderInfo &encoder = encoders[i];
encoder.callback = (i == 0) ? callback : 0;
const UInt32 kLzmaId = 0x030101;
RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS kLzmaId, encoder.encoder, true));
if (!encoder.encoder)
return E_NOTIMPL;
for (UInt32 j = 0; j < numSubDecoderThreads; j++)
{
RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS kLzmaId, encoder.decoders[j], false));
if (!encoder.decoders[j])
return E_NOTIMPL;
}
}
CBaseRandomGenerator rg;
rg.Init();
for (i = 0; i < numEncoderThreads; i++)
{
RINOK(encoders[i].Init(dictionarySize, numThreads, &rg));
}
CBenchProgressStatus status;
status.Res = S_OK;
status.EncodeMode = true;
for (i = 0; i < numEncoderThreads; i++)
{
CEncoderInfo &encoder = encoders[i];
for (int j = 0; j < 2; j++)
{
encoder.progressInfo[j] = encoder.progressInfoSpec[j] = new CBenchProgressInfo;
encoder.progressInfoSpec[j]->Status = &status;
}
if (i == 0)
{
encoder.progressInfoSpec[0]->callback = callback;
encoder.progressInfoSpec[0]->BenchInfo.NumIterations = numEncoderThreads;
SetStartTime(encoder.progressInfoSpec[0]->BenchInfo);
}
#ifndef _7ZIP_ST
if (numEncoderThreads > 1)
{
#ifdef USE_ALLOCA
encoder.AllocaSize = (i * 16 * 21) & 0x7FF;
#endif
RINOK(encoder.CreateEncoderThread())
}
else
#endif
{
RINOK(encoder.Encode());
}
}
#ifndef _7ZIP_ST
if (numEncoderThreads > 1)
for (i = 0; i < numEncoderThreads; i++)
encoders[i].thread[0].Wait();
#endif
RINOK(status.Res);
CBenchInfo info;
SetFinishTime(encoders[0].progressInfoSpec[0]->BenchInfo, info);
info.UnpackSize = 0;
info.PackSize = 0;
info.NumIterations = 1; // progressInfoSpec->NumIterations;
for (i = 0; i < numEncoderThreads; i++)
{
CEncoderInfo &encoder = encoders[i];
info.UnpackSize += encoder.kBufferSize;
info.PackSize += encoder.compressedSize;
}
RINOK(callback->SetEncodeResult(info, true));
status.Res = S_OK;
status.EncodeMode = false;
UInt32 numDecoderThreads = numEncoderThreads * numSubDecoderThreads;
for (i = 0; i < numEncoderThreads; i++)
{
CEncoderInfo &encoder = encoders[i];
encoder.NumIterations = 2 + kUncompressMinBlockSize / encoder.kBufferSize;
if (i == 0)
{
encoder.progressInfoSpec[0]->callback = callback;
encoder.progressInfoSpec[0]->BenchInfo.NumIterations = numDecoderThreads;
SetStartTime(encoder.progressInfoSpec[0]->BenchInfo);
}
#ifndef _7ZIP_ST
if (numDecoderThreads > 1)
{
for (UInt32 j = 0; j < numSubDecoderThreads; j++)
{
HRESULT res = encoder.CreateDecoderThread(j, (i == 0 && j == 0)
#ifdef USE_ALLOCA
, ((i * numSubDecoderThreads + j) * 16 * 21) & 0x7FF
#endif
);
RINOK(res);
}
}
else
#endif
{
RINOK(encoder.Decode(0));
}
}
#ifndef _7ZIP_ST
HRESULT res = S_OK;
if (numDecoderThreads > 1)
for (i = 0; i < numEncoderThreads; i++)
for (UInt32 j = 0; j < numSubDecoderThreads; j++)
{
CEncoderInfo &encoder = encoders[i];
encoder.thread[j].Wait();
if (encoder.Results[j] != S_OK)
res = encoder.Results[j];
}
RINOK(res);
#endif
RINOK(status.Res);
SetFinishTime(encoders[0].progressInfoSpec[0]->BenchInfo, info);
#ifndef _7ZIP_ST
#ifdef UNDER_CE
if (numDecoderThreads > 1)
for (i = 0; i < numEncoderThreads; i++)
for (UInt32 j = 0; j < numSubDecoderThreads; j++)
{
FILETIME creationTime, exitTime, kernelTime, userTime;
if (::GetThreadTimes(encoders[i].thread[j], &creationTime, &exitTime, &kernelTime, &userTime) != 0)
info.UserTime += GetTime64(userTime) + GetTime64(kernelTime);
}
#endif
#endif
info.UnpackSize = 0;
info.PackSize = 0;
info.NumIterations = numSubDecoderThreads * encoders[0].NumIterations;
for (i = 0; i < numEncoderThreads; i++)
{
CEncoderInfo &encoder = encoders[i];
info.UnpackSize += encoder.kBufferSize;
info.PackSize += encoder.compressedSize;
}
RINOK(callback->SetDecodeResult(info, false));
RINOK(callback->SetDecodeResult(info, true));
return S_OK;
}
inline UInt64 GetLZMAUsage(bool multiThread, UInt32 dictionary)
{
UInt32 hs = dictionary - 1;
hs |= (hs >> 1);
hs |= (hs >> 2);
hs |= (hs >> 4);
hs |= (hs >> 8);
hs >>= 1;
hs |= 0xFFFF;
if (hs > (1 << 24))
hs >>= 1;
hs++;
return ((hs + (1 << 16)) + (UInt64)dictionary * 2) * 4 + (UInt64)dictionary * 3 / 2 +
(1 << 20) + (multiThread ? (6 << 20) : 0);
}
UInt64 GetBenchMemoryUsage(UInt32 numThreads, UInt32 dictionary)
{
const UInt32 kBufferSize = dictionary;
const UInt32 kCompressedBufferSize = (kBufferSize / 2);
UInt32 numSubThreads = (numThreads > 1) ? 2 : 1;
UInt32 numBigThreads = numThreads / numSubThreads;
return (kBufferSize + kCompressedBufferSize +
GetLZMAUsage((numThreads > 1), dictionary) + (2 << 20)) * numBigThreads;
}
static bool CrcBig(const void *data, UInt32 size, UInt32 numCycles, UInt32 crcBase)
{
for (UInt32 i = 0; i < numCycles; i++)
if (CrcCalc(data, size) != crcBase)
return false;
return true;
}
#ifndef _7ZIP_ST
struct CCrcInfo
{
NWindows::CThread Thread;
const Byte *Data;
UInt32 Size;
UInt32 NumCycles;
UInt32 Crc;
bool Res;
void Wait()
{
Thread.Wait();
Thread.Close();
}
};
static THREAD_FUNC_DECL CrcThreadFunction(void *param)
{
CCrcInfo *p = (CCrcInfo *)param;
p->Res = CrcBig(p->Data, p->Size, p->NumCycles, p->Crc);
return 0;
}
struct CCrcThreads
{
UInt32 NumThreads;
CCrcInfo *Items;
CCrcThreads(): Items(0), NumThreads(0) {}
void WaitAll()
{
for (UInt32 i = 0; i < NumThreads; i++)
Items[i].Wait();
NumThreads = 0;
}
~CCrcThreads()
{
WaitAll();
delete []Items;
}
};
#endif
static UInt32 CrcCalc1(const Byte *buf, UInt32 size)
{
UInt32 crc = CRC_INIT_VAL;;
for (UInt32 i = 0; i < size; i++)
crc = CRC_UPDATE_BYTE(crc, buf[i]);
return CRC_GET_DIGEST(crc);
}
static void RandGen(Byte *buf, UInt32 size, CBaseRandomGenerator &RG)
{
for (UInt32 i = 0; i < size; i++)
buf[i] = (Byte)RG.GetRnd();
}
static UInt32 RandGenCrc(Byte *buf, UInt32 size, CBaseRandomGenerator &RG)
{
RandGen(buf, size, RG);
return CrcCalc1(buf, size);
}
bool CrcInternalTest()
{
CBenchBuffer buffer;
const UInt32 kBufferSize0 = (1 << 8);
const UInt32 kBufferSize1 = (1 << 10);
const UInt32 kCheckSize = (1 << 5);
if (!buffer.Alloc(kBufferSize0 + kBufferSize1))
return false;
Byte *buf = buffer.Buffer;
UInt32 i;
for (i = 0; i < kBufferSize0; i++)
buf[i] = (Byte)i;
UInt32 crc1 = CrcCalc1(buf, kBufferSize0);
if (crc1 != 0x29058C73)
return false;
CBaseRandomGenerator RG;
RandGen(buf + kBufferSize0, kBufferSize1, RG);
for (i = 0; i < kBufferSize0 + kBufferSize1 - kCheckSize; i++)
for (UInt32 j = 0; j < kCheckSize; j++)
if (CrcCalc1(buf + i, j) != CrcCalc(buf + i, j))
return false;
return true;
}
HRESULT CrcBench(UInt32 numThreads, UInt32 bufferSize, UInt64 &speed)
{
if (numThreads == 0)
numThreads = 1;
CBenchBuffer buffer;
size_t totalSize = (size_t)bufferSize * numThreads;
if (totalSize / numThreads != bufferSize)
return E_OUTOFMEMORY;
if (!buffer.Alloc(totalSize))
return E_OUTOFMEMORY;
Byte *buf = buffer.Buffer;
CBaseRandomGenerator RG;
UInt32 numCycles = (kCrcBlockSize) / ((bufferSize >> 2) + 1) + 1;
UInt64 timeVal;
#ifndef _7ZIP_ST
CCrcThreads threads;
if (numThreads > 1)
{
threads.Items = new CCrcInfo[numThreads];
UInt32 i;
for (i = 0; i < numThreads; i++)
{
CCrcInfo &info = threads.Items[i];
Byte *data = buf + (size_t)bufferSize * i;
info.Data = data;
info.NumCycles = numCycles;
info.Size = bufferSize;
info.Crc = RandGenCrc(data, bufferSize, RG);
}
timeVal = GetTimeCount();
for (i = 0; i < numThreads; i++)
{
CCrcInfo &info = threads.Items[i];
RINOK(info.Thread.Create(CrcThreadFunction, &info));
threads.NumThreads++;
}
threads.WaitAll();
for (i = 0; i < numThreads; i++)
if (!threads.Items[i].Res)
return S_FALSE;
}
else
#endif
{
UInt32 crc = RandGenCrc(buf, bufferSize, RG);
timeVal = GetTimeCount();
if (!CrcBig(buf, bufferSize, numCycles, crc))
return S_FALSE;
}
timeVal = GetTimeCount() - timeVal;
if (timeVal == 0)
timeVal = 1;
UInt64 size = (UInt64)numCycles * totalSize;
speed = MyMultDiv64(size, timeVal, GetFreq());
return S_OK;
}