mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-10 08:07:09 -06:00
529 lines
13 KiB
C++
Executable File
529 lines
13 KiB
C++
Executable File
// LzmaBench.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "LzmaBench.h"
|
|
|
|
#ifndef _WIN32
|
|
#include <time.h>
|
|
#endif
|
|
|
|
#include "../../../Common/CRC.h"
|
|
#include "../LZMA/LZMADecoder.h"
|
|
#include "../LZMA/LZMAEncoder.h"
|
|
|
|
static const UInt32 kAdditionalSize =
|
|
#ifdef _WIN32_WCE
|
|
(1 << 20);
|
|
#else
|
|
(6 << 20);
|
|
#endif
|
|
|
|
static const UInt32 kCompressedAdditionalSize = (1 << 10);
|
|
static const UInt32 kMaxLzmaPropSize = 10;
|
|
|
|
class CRandomGenerator
|
|
{
|
|
UInt32 A1;
|
|
UInt32 A2;
|
|
public:
|
|
CRandomGenerator() { Init(); }
|
|
void Init() { A1 = 362436069; A2 = 521288629;}
|
|
UInt32 GetRnd()
|
|
{
|
|
return
|
|
((A1 = 36969 * (A1 & 0xffff) + (A1 >> 16)) << 16) ^
|
|
((A2 = 18000 * (A2 & 0xffff) + (A2 >> 16)) );
|
|
}
|
|
};
|
|
|
|
class CBitRandomGenerator
|
|
{
|
|
CRandomGenerator RG;
|
|
UInt32 Value;
|
|
int NumBits;
|
|
public:
|
|
void Init()
|
|
{
|
|
Value = 0;
|
|
NumBits = 0;
|
|
}
|
|
UInt32 GetRnd(int numBits)
|
|
{
|
|
if (NumBits > numBits)
|
|
{
|
|
UInt32 result = Value & ((1 << numBits) - 1);
|
|
Value >>= numBits;
|
|
NumBits -= numBits;
|
|
return result;
|
|
}
|
|
numBits -= NumBits;
|
|
UInt32 result = (Value << numBits);
|
|
Value = RG.GetRnd();
|
|
result |= Value & ((1 << numBits) - 1);
|
|
Value >>= numBits;
|
|
NumBits = 32 - numBits;
|
|
return result;
|
|
}
|
|
};
|
|
|
|
class CBenchRandomGenerator
|
|
{
|
|
CBitRandomGenerator RG;
|
|
UInt32 Pos;
|
|
public:
|
|
UInt32 BufferSize;
|
|
Byte *Buffer;
|
|
CBenchRandomGenerator(): Buffer(0) {}
|
|
~CBenchRandomGenerator() { delete []Buffer; }
|
|
void Init() { RG.Init(); }
|
|
void Set(UInt32 bufferSize)
|
|
{
|
|
delete []Buffer;
|
|
Buffer = 0;
|
|
Buffer = new Byte[bufferSize];
|
|
Pos = 0;
|
|
BufferSize = bufferSize;
|
|
}
|
|
UInt32 GetRndBit() { return RG.GetRnd(1); }
|
|
/*
|
|
UInt32 GetLogRand(int maxLen)
|
|
{
|
|
UInt32 len = GetRnd() % (maxLen + 1);
|
|
return GetRnd() & ((1 << len) - 1);
|
|
}
|
|
*/
|
|
UInt32 GetLogRandBits(int numBits)
|
|
{
|
|
UInt32 len = RG.GetRnd(numBits);
|
|
return RG.GetRnd(len);
|
|
}
|
|
UInt32 GetOffset()
|
|
{
|
|
if (GetRndBit() == 0)
|
|
return GetLogRandBits(4);
|
|
return (GetLogRandBits(4) << 10) | RG.GetRnd(10);
|
|
}
|
|
UInt32 GetLen()
|
|
{
|
|
if (GetRndBit() == 0)
|
|
return RG.GetRnd(2);
|
|
if (GetRndBit() == 0)
|
|
return 4 + RG.GetRnd(3);
|
|
return 12 + RG.GetRnd(4);
|
|
}
|
|
void Generate()
|
|
{
|
|
while(Pos < BufferSize)
|
|
{
|
|
if (GetRndBit() == 0 || Pos < 1)
|
|
Buffer[Pos++] = Byte(RG.GetRnd(8));
|
|
else
|
|
{
|
|
UInt32 offset = GetOffset();
|
|
while (offset >= Pos)
|
|
offset >>= 1;
|
|
offset += 1;
|
|
UInt32 len = 2 + GetLen();
|
|
for (UInt32 i = 0; i < len && Pos < BufferSize; i++, Pos++)
|
|
Buffer[Pos] = Buffer[Pos - offset];
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class CBenchmarkInStream:
|
|
public ISequentialInStream,
|
|
public CMyUnknownImp
|
|
{
|
|
const Byte *Data;
|
|
UInt32 Pos;
|
|
UInt32 Size;
|
|
public:
|
|
MY_UNKNOWN_IMP
|
|
void Init(const Byte *data, UInt32 size)
|
|
{
|
|
Data = data;
|
|
Size = size;
|
|
Pos = 0;
|
|
}
|
|
STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
|
|
STDMETHOD(ReadPart)(void *data, UInt32 size, UInt32 *processedSize);
|
|
};
|
|
|
|
STDMETHODIMP CBenchmarkInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
|
|
{
|
|
UInt32 remain = Size - Pos;
|
|
if (size > remain)
|
|
size = remain;
|
|
for (UInt32 i = 0; i < size; i++)
|
|
{
|
|
((Byte *)data)[i] = Data[Pos + i];
|
|
}
|
|
Pos += size;
|
|
if(processedSize != NULL)
|
|
*processedSize = size;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CBenchmarkInStream::ReadPart(void *data, UInt32 size, UInt32 *processedSize)
|
|
{
|
|
return Read(data, size, processedSize);
|
|
}
|
|
|
|
class CBenchmarkOutStream:
|
|
public ISequentialOutStream,
|
|
public CMyUnknownImp
|
|
{
|
|
UInt32 BufferSize;
|
|
FILE *_f;
|
|
public:
|
|
UInt32 Pos;
|
|
Byte *Buffer;
|
|
CBenchmarkOutStream(): _f(0), Buffer(0) {}
|
|
virtual ~CBenchmarkOutStream() { delete []Buffer; }
|
|
void Init(FILE *f, UInt32 bufferSize)
|
|
{
|
|
delete []Buffer;
|
|
Buffer = 0;
|
|
Buffer = new Byte[bufferSize];
|
|
Pos = 0;
|
|
BufferSize = bufferSize;
|
|
_f = f;
|
|
}
|
|
MY_UNKNOWN_IMP
|
|
STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
|
|
STDMETHOD(WritePart)(const void *data, UInt32 size, UInt32 *processedSize);
|
|
};
|
|
|
|
STDMETHODIMP CBenchmarkOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
|
|
{
|
|
UInt32 i;
|
|
for (i = 0; i < size && Pos < BufferSize; i++)
|
|
Buffer[Pos++] = ((const Byte *)data)[i];
|
|
if(processedSize != NULL)
|
|
*processedSize = i;
|
|
if (i != size)
|
|
{
|
|
fprintf(_f, "\nERROR: Buffer is full\n");
|
|
return E_FAIL;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CBenchmarkOutStream::WritePart(const void *data, UInt32 size, UInt32 *processedSize)
|
|
{
|
|
return Write(data, size, processedSize);
|
|
}
|
|
|
|
class CCrcOutStream:
|
|
public ISequentialOutStream,
|
|
public CMyUnknownImp
|
|
{
|
|
public:
|
|
CCRC CRC;
|
|
MY_UNKNOWN_IMP
|
|
void Init() { CRC.Init(); }
|
|
STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
|
|
STDMETHOD(WritePart)(const void *data, UInt32 size, UInt32 *processedSize);
|
|
};
|
|
|
|
STDMETHODIMP CCrcOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
|
|
{
|
|
CRC.Update(data, size);
|
|
if(processedSize != NULL)
|
|
*processedSize = size;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CCrcOutStream::WritePart(const void *data, UInt32 size, UInt32 *processedSize)
|
|
{
|
|
return Write(data, size, processedSize);
|
|
}
|
|
|
|
static UInt64 GetTimeCount()
|
|
{
|
|
#ifdef _WIN32
|
|
LARGE_INTEGER value;
|
|
if (::QueryPerformanceCounter(&value))
|
|
return value.QuadPart;
|
|
return GetTickCount();
|
|
#else
|
|
return clock();
|
|
#endif
|
|
}
|
|
|
|
static UInt64 GetFreq()
|
|
{
|
|
#ifdef _WIN32
|
|
LARGE_INTEGER value;
|
|
if (::QueryPerformanceFrequency(&value))
|
|
return value.QuadPart;
|
|
return 1000;
|
|
#else
|
|
return CLOCKS_PER_SEC;
|
|
#endif
|
|
}
|
|
|
|
struct CProgressInfo:
|
|
public ICompressProgressInfo,
|
|
public CMyUnknownImp
|
|
{
|
|
UInt64 ApprovedStart;
|
|
UInt64 InSize;
|
|
UInt64 Time;
|
|
void Init()
|
|
{
|
|
InSize = 0;
|
|
Time = 0;
|
|
}
|
|
MY_UNKNOWN_IMP
|
|
STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
|
|
};
|
|
|
|
STDMETHODIMP CProgressInfo::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
|
|
{
|
|
if (*inSize >= ApprovedStart && InSize == 0)
|
|
{
|
|
Time = ::GetTimeCount();
|
|
InSize = *inSize;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
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 UInt64 MyMultDiv64(UInt64 value, UInt64 elapsedTime)
|
|
{
|
|
UInt64 freq = GetFreq();
|
|
UInt64 elTime = elapsedTime;
|
|
while(freq > 1000000)
|
|
{
|
|
freq >>= 1;
|
|
elTime >>= 1;
|
|
}
|
|
if (elTime == 0)
|
|
elTime = 1;
|
|
return value * freq / elTime;
|
|
}
|
|
|
|
static UInt64 GetCompressRating(UInt32 dictionarySize, bool isBT4,
|
|
UInt64 elapsedTime, UInt64 size)
|
|
{
|
|
UInt64 numCommandsForOne;
|
|
if (isBT4)
|
|
{
|
|
UInt64 t = GetLogSize(dictionarySize) - (19 << kSubBits);
|
|
numCommandsForOne = 2000 + ((t * t * 68) >> (2 * kSubBits));
|
|
}
|
|
else
|
|
{
|
|
UInt64 t = GetLogSize(dictionarySize) - (15 << kSubBits);
|
|
numCommandsForOne = 1500 + ((t * t * 41) >> (2 * kSubBits));
|
|
}
|
|
UInt64 numCommands = (UInt64)(size) * numCommandsForOne;
|
|
return MyMultDiv64(numCommands, elapsedTime);
|
|
}
|
|
|
|
static UInt64 GetDecompressRating(UInt64 elapsedTime,
|
|
UInt64 outSize, UInt64 inSize)
|
|
{
|
|
UInt64 numCommands = inSize * 250 + outSize * 21;
|
|
return MyMultDiv64(numCommands, elapsedTime);
|
|
}
|
|
|
|
/*
|
|
static UInt64 GetTotalRating(
|
|
UInt32 dictionarySize,
|
|
bool isBT4,
|
|
UInt64 elapsedTimeEn, UInt64 sizeEn,
|
|
UInt64 elapsedTimeDe,
|
|
UInt64 inSizeDe, UInt64 outSizeDe)
|
|
{
|
|
return (GetCompressRating(dictionarySize, isBT4, elapsedTimeEn, sizeEn) +
|
|
GetDecompressRating(elapsedTimeDe, inSizeDe, outSizeDe)) / 2;
|
|
}
|
|
*/
|
|
|
|
static void PrintRating(FILE *f, UInt64 rating)
|
|
{
|
|
fprintf(f, "%5d MIPS", (unsigned int)(rating / 1000000));
|
|
}
|
|
|
|
static void PrintResults(
|
|
FILE *f,
|
|
UInt32 dictionarySize,
|
|
bool isBT4,
|
|
UInt64 elapsedTime,
|
|
UInt64 size,
|
|
bool decompressMode, UInt64 secondSize)
|
|
{
|
|
UInt64 speed = MyMultDiv64(size, elapsedTime);
|
|
fprintf(f, "%6d KB/s ", (unsigned int)(speed / 1024));
|
|
UInt64 rating;
|
|
if (decompressMode)
|
|
rating = GetDecompressRating(elapsedTime, size, secondSize);
|
|
else
|
|
rating = GetCompressRating(dictionarySize, isBT4, elapsedTime, size);
|
|
PrintRating(f, rating);
|
|
}
|
|
|
|
static void ThrowError(FILE *f, HRESULT result, const char *s)
|
|
{
|
|
fprintf(f, "\nError: ");
|
|
if (result == E_ABORT)
|
|
fprintf(f, "User break");
|
|
if (result == E_OUTOFMEMORY)
|
|
fprintf(f, "Can not allocate memory");
|
|
else
|
|
fprintf(f, s);
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
const wchar_t *bt2 = L"BT2";
|
|
const wchar_t *bt4 = L"BT4";
|
|
|
|
int LzmaBenchmark(FILE *f, UInt32 numIterations, UInt32 dictionarySize, bool isBT4)
|
|
{
|
|
if (numIterations == 0)
|
|
return 0;
|
|
if (dictionarySize < (1 << 19) && isBT4 || dictionarySize < (1 << 15))
|
|
{
|
|
fprintf(f, "\nError: dictionary size for benchmark must be >= 19 (512 KB)\n");
|
|
return 1;
|
|
}
|
|
fprintf(f, "\n Compressing Decompressing\n\n");
|
|
NCompress::NLZMA::CEncoder *encoderSpec = new NCompress::NLZMA::CEncoder;
|
|
CMyComPtr<ICompressCoder> encoder = encoderSpec;
|
|
|
|
NCompress::NLZMA::CDecoder *decoderSpec = new NCompress::NLZMA::CDecoder;
|
|
CMyComPtr<ICompressCoder> decoder = decoderSpec;
|
|
|
|
CBenchmarkOutStream *propStreamSpec = new CBenchmarkOutStream;
|
|
CMyComPtr<ISequentialOutStream> propStream = propStreamSpec;
|
|
propStreamSpec->Init(f, kMaxLzmaPropSize);
|
|
|
|
PROPID propIDs[] =
|
|
{
|
|
NCoderPropID::kDictionarySize,
|
|
NCoderPropID::kMatchFinder
|
|
};
|
|
const int kNumProps = sizeof(propIDs) / sizeof(propIDs[0]);
|
|
PROPVARIANT properties[kNumProps];
|
|
properties[0].vt = VT_UI4;
|
|
properties[0].ulVal = UInt32(dictionarySize);
|
|
|
|
properties[1].vt = VT_BSTR;
|
|
properties[1].bstrVal = isBT4 ? (BSTR)bt4: (BSTR)bt2;
|
|
|
|
const UInt32 kBufferSize = dictionarySize + kAdditionalSize;
|
|
const UInt32 kCompressedBufferSize = (kBufferSize / 2) + kCompressedAdditionalSize;
|
|
|
|
if (encoderSpec->SetCoderProperties(propIDs, properties, kNumProps) != S_OK)
|
|
{
|
|
fprintf(f, "\nError: Incorrect command\n");
|
|
return 1;
|
|
}
|
|
encoderSpec->WriteCoderProperties(propStream);
|
|
|
|
CBenchRandomGenerator rg;
|
|
rg.Init();
|
|
rg.Set(kBufferSize);
|
|
rg.Generate();
|
|
CCRC crc;
|
|
crc.Update(rg.Buffer, rg.BufferSize);
|
|
|
|
CProgressInfo *progressInfoSpec = new CProgressInfo;
|
|
CMyComPtr<ICompressProgressInfo> progressInfo = progressInfoSpec;
|
|
|
|
progressInfoSpec->ApprovedStart = dictionarySize;
|
|
|
|
UInt64 totalBenchSize = 0;
|
|
UInt64 totalEncodeTime = 0;
|
|
UInt64 totalDecodeTime = 0;
|
|
UInt64 totalCompressedSize = 0;
|
|
|
|
for (UInt32 i = 0; i < numIterations; i++)
|
|
{
|
|
progressInfoSpec->Init();
|
|
CBenchmarkInStream *inStreamSpec = new CBenchmarkInStream;
|
|
inStreamSpec->Init(rg.Buffer, rg.BufferSize);
|
|
CMyComPtr<ISequentialInStream> inStream = inStreamSpec;
|
|
CBenchmarkOutStream *outStreamSpec = new CBenchmarkOutStream;
|
|
outStreamSpec->Init(f, kCompressedBufferSize);
|
|
CMyComPtr<ISequentialOutStream> outStream = outStreamSpec;
|
|
HRESULT result = encoder->Code(inStream, outStream, 0, 0, progressInfo);
|
|
UInt64 encodeTime = ::GetTimeCount() - progressInfoSpec->Time;
|
|
UInt32 compressedSize = outStreamSpec->Pos;
|
|
if(result != S_OK)
|
|
{
|
|
ThrowError(f, result, "Encoder Error");
|
|
return 1;
|
|
}
|
|
if (progressInfoSpec->InSize == 0)
|
|
{
|
|
fprintf(f, "\nError: Internal ERROR 1282\n");
|
|
return 1;
|
|
}
|
|
|
|
///////////////////////
|
|
// Decompressing
|
|
|
|
CCrcOutStream *crcOutStreamSpec = new CCrcOutStream;
|
|
CMyComPtr<ISequentialOutStream> crcOutStream = crcOutStreamSpec;
|
|
|
|
UInt64 decodeTime;
|
|
for (int j = 0; j < 2; j++)
|
|
{
|
|
inStreamSpec->Init(outStreamSpec->Buffer, compressedSize);
|
|
crcOutStreamSpec->Init();
|
|
|
|
if (decoderSpec->SetDecoderProperties2(propStreamSpec->Buffer, propStreamSpec->Pos) != S_OK)
|
|
{
|
|
fprintf(f, "\nError: Set Decoder Properties Error\n");
|
|
return 1;
|
|
}
|
|
UInt64 outSize = kBufferSize;
|
|
UInt64 startTime = ::GetTimeCount();
|
|
result = decoder->Code(inStream, crcOutStream, 0, &outSize, 0);
|
|
decodeTime = ::GetTimeCount() - startTime;
|
|
if(result != S_OK)
|
|
{
|
|
ThrowError(f, result, "Decode Error");
|
|
return 1;
|
|
}
|
|
if (crcOutStreamSpec->CRC.GetDigest() != crc.GetDigest())
|
|
{
|
|
fprintf(f, "\nError: CRC Error\n");
|
|
return 1;
|
|
}
|
|
}
|
|
UInt64 benchSize = kBufferSize - progressInfoSpec->InSize;
|
|
PrintResults(f, dictionarySize, isBT4, encodeTime, benchSize, false, 0);
|
|
fprintf(f, " ");
|
|
PrintResults(f, dictionarySize, isBT4, decodeTime, kBufferSize, true, compressedSize);
|
|
fprintf(f, "\n");
|
|
|
|
totalBenchSize += benchSize;
|
|
totalEncodeTime += encodeTime;
|
|
totalDecodeTime += decodeTime;
|
|
totalCompressedSize += compressedSize;
|
|
}
|
|
fprintf(f, "---------------------------------------------------\n");
|
|
PrintResults(f, dictionarySize, isBT4, totalEncodeTime, totalBenchSize, false, 0);
|
|
fprintf(f, " ");
|
|
PrintResults(f, dictionarySize, isBT4, totalDecodeTime,
|
|
kBufferSize * numIterations, true, totalCompressedSize);
|
|
fprintf(f, " Average\n");
|
|
return 0;
|
|
}
|