// 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; | |
} |