Добавлена папка source в CristalDiskMark
This commit is contained in:
@@ -0,0 +1,685 @@
|
||||
/*
|
||||
|
||||
DISKSPD
|
||||
|
||||
Copyright(c) Microsoft Corporation
|
||||
All rights reserved.
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
UINT64 PerfTimer::GetTime()
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
QueryPerformanceCounter(&li);
|
||||
return li.QuadPart;
|
||||
}
|
||||
|
||||
UINT64 PerfTimer::_GetPerfTimerFreq()
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
QueryPerformanceFrequency(&li);
|
||||
return li.QuadPart;
|
||||
}
|
||||
|
||||
const UINT64 PerfTimer::TIMER_FREQ = _GetPerfTimerFreq();
|
||||
|
||||
double PerfTimer::PerfTimeToMicroseconds(const double perfTime)
|
||||
{
|
||||
return perfTime / (TIMER_FREQ / 1000000.0);
|
||||
}
|
||||
|
||||
double PerfTimer::PerfTimeToMilliseconds(const double perfTime)
|
||||
{
|
||||
return PerfTimeToMicroseconds(perfTime) / 1000;
|
||||
}
|
||||
|
||||
double PerfTimer::PerfTimeToSeconds(const double perfTime)
|
||||
{
|
||||
return PerfTimeToMilliseconds(perfTime) / 1000;
|
||||
}
|
||||
|
||||
double PerfTimer::PerfTimeToMicroseconds(const UINT64 perfTime)
|
||||
{
|
||||
return PerfTimeToMicroseconds(static_cast<double>(perfTime));
|
||||
}
|
||||
|
||||
double PerfTimer::PerfTimeToMilliseconds(const UINT64 perfTime)
|
||||
{
|
||||
return PerfTimeToMilliseconds(static_cast<double>(perfTime));
|
||||
}
|
||||
|
||||
double PerfTimer::PerfTimeToSeconds(const UINT64 perfTime)
|
||||
{
|
||||
return PerfTimeToSeconds(static_cast<double>(perfTime));
|
||||
}
|
||||
|
||||
UINT64 PerfTimer::MicrosecondsToPerfTime(const double microseconds)
|
||||
{
|
||||
return static_cast<UINT64>(TIMER_FREQ * (microseconds / 1000000.0));
|
||||
}
|
||||
|
||||
UINT64 PerfTimer::MillisecondsToPerfTime(const double milliseconds)
|
||||
{
|
||||
return static_cast<UINT64>(TIMER_FREQ * (milliseconds / 1000.0));
|
||||
}
|
||||
|
||||
UINT64 PerfTimer::SecondsToPerfTime(const double seconds)
|
||||
{
|
||||
return static_cast<UINT64>(TIMER_FREQ * seconds);
|
||||
}
|
||||
|
||||
string Util::DoubleToStringHelper(const double d)
|
||||
{
|
||||
char szFloatBuffer[100];
|
||||
sprintf_s(szFloatBuffer, _countof(szFloatBuffer), "%10.3lf", d);
|
||||
|
||||
return string(szFloatBuffer);
|
||||
}
|
||||
|
||||
string Target::GetXml() const
|
||||
{
|
||||
char buffer[4096];
|
||||
string sXml("<Target>\n");
|
||||
sXml += "<Path>" + _sPath + "</Path>\n";
|
||||
|
||||
sprintf_s(buffer, _countof(buffer), "<BlockSize>%u</BlockSize>\n", _dwBlockSize);
|
||||
sXml += buffer;
|
||||
|
||||
sprintf_s(buffer, _countof(buffer), "<BaseFileOffset>%I64u</BaseFileOffset>\n", _ullBaseFileOffset);
|
||||
sXml += buffer;
|
||||
|
||||
sXml += _fSequentialScanHint ? "<SequentialScan>true</SequentialScan>\n" : "<SequentialScan>false</SequentialScan>\n";
|
||||
sXml += _fRandomAccessHint ? "<RandomAccess>true</RandomAccess>\n" : "<RandomAccess>false</RandomAccess>\n";
|
||||
sXml += _fUseLargePages ? "<UseLargePages>true</UseLargePages>\n" : "<UseLargePages>false</UseLargePages>\n";
|
||||
sXml += _fDisableAllCache ? "<DisableAllCache>true</DisableAllCache>\n" : "<DisableAllCache>false</DisableAllCache>\n";
|
||||
// normalize cache controls - disabling the OS cache is included if all caches are disabled,
|
||||
// so specifying it twice is not neccesary (this generates a warning on the cmdline)
|
||||
if (!_fDisableAllCache)
|
||||
{
|
||||
sXml += _fDisableOSCache ? "<DisableOSCache>true</DisableOSCache>\n" : "<DisableOSCache>false</DisableOSCache>\n";
|
||||
}
|
||||
|
||||
sXml += "<WriteBufferContent>\n";
|
||||
if (_fZeroWriteBuffers)
|
||||
{
|
||||
sXml += "<Pattern>zero</Pattern>\n";
|
||||
}
|
||||
else if (_cbRandomDataWriteBuffer == 0)
|
||||
{
|
||||
sXml += "<Pattern>sequential</Pattern>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
sXml += "<Pattern>random</Pattern>\n";
|
||||
sXml += "<RandomDataSource>\n";
|
||||
sprintf_s(buffer, _countof(buffer), "<SizeInBytes>%I64u</SizeInBytes>\n", _cbRandomDataWriteBuffer);
|
||||
sXml += buffer;
|
||||
if (_sRandomDataWriteBufferSourcePath != "")
|
||||
{
|
||||
sXml += "<FilePath>" + _sRandomDataWriteBufferSourcePath + "</FilePath>\n";
|
||||
}
|
||||
sXml += "</RandomDataSource>\n";
|
||||
}
|
||||
sXml += "</WriteBufferContent>\n";
|
||||
|
||||
sXml += _fParallelAsyncIO ? "<ParallelAsyncIO>true</ParallelAsyncIO>\n" : "<ParallelAsyncIO>false</ParallelAsyncIO>\n";
|
||||
|
||||
if (_fUseBurstSize)
|
||||
{
|
||||
sprintf_s(buffer, _countof(buffer), "<BurstSize>%u</BurstSize>\n", _dwBurstSize);
|
||||
sXml += buffer;
|
||||
}
|
||||
|
||||
if (_fThinkTime)
|
||||
{
|
||||
sprintf_s(buffer, _countof(buffer), "<ThinkTime>%u</ThinkTime>\n", _dwThinkTime);
|
||||
sXml += buffer;
|
||||
}
|
||||
|
||||
if (_fCreateFile)
|
||||
{
|
||||
sprintf_s(buffer, _countof(buffer), "<FileSize>%I64u</FileSize>\n", _ullFileSize);
|
||||
sXml += buffer;
|
||||
}
|
||||
|
||||
// If XML contains <Random>, <StrideSize> is ignored
|
||||
if (_fUseRandomAccessPattern)
|
||||
{
|
||||
sprintf_s(buffer, _countof(buffer), "<Random>%I64u</Random>\n", GetBlockAlignmentInBytes());
|
||||
sXml += buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf_s(buffer, _countof(buffer), "<StrideSize>%I64u</StrideSize>\n", GetBlockAlignmentInBytes());
|
||||
sXml += buffer;
|
||||
|
||||
sXml += _fInterlockedSequential ?
|
||||
"<InterlockedSequential>true</InterlockedSequential>\n" :
|
||||
"<InterlockedSequential>false</InterlockedSequential>\n";
|
||||
}
|
||||
|
||||
sprintf_s(buffer, _countof(buffer), "<ThreadStride>%I64u</ThreadStride>\n", _ullThreadStride);
|
||||
sXml += buffer;
|
||||
|
||||
sprintf_s(buffer, _countof(buffer), "<MaxFileSize>%I64u</MaxFileSize>\n", _ullMaxFileSize);
|
||||
sXml += buffer;
|
||||
|
||||
sprintf_s(buffer, _countof(buffer), "<RequestCount>%u</RequestCount>\n", _dwRequestCount);
|
||||
sXml += buffer;
|
||||
|
||||
sprintf_s(buffer, _countof(buffer), "<WriteRatio>%u</WriteRatio>\n", _ulWriteRatio);
|
||||
sXml += buffer;
|
||||
|
||||
sprintf_s(buffer, _countof(buffer), "<Throughput>%u</Throughput>\n", _dwThroughputBytesPerMillisecond);
|
||||
sXml += buffer;
|
||||
|
||||
sprintf_s(buffer, _countof(buffer), "<ThreadsPerFile>%u</ThreadsPerFile>\n", _dwThreadsPerFile);
|
||||
sXml += buffer;
|
||||
|
||||
if (_ioPriorityHint == IoPriorityHintVeryLow)
|
||||
{
|
||||
sXml += "<IOPriority>1</IOPriority>\n";
|
||||
}
|
||||
else if (_ioPriorityHint == IoPriorityHintLow)
|
||||
{
|
||||
sXml += "<IOPriority>2</IOPriority>\n";
|
||||
}
|
||||
else if (_ioPriorityHint == IoPriorityHintNormal)
|
||||
{
|
||||
sXml += "<IOPriority>3</IOPriority>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
sXml += "<IOPriority>* UNSUPPORTED *</IOPriority>\n";
|
||||
}
|
||||
|
||||
sXml += "</Target>\n";
|
||||
|
||||
|
||||
return sXml;
|
||||
}
|
||||
|
||||
bool Target::_FillRandomDataWriteBuffer()
|
||||
{
|
||||
assert(_pRandomDataWriteBuffer != nullptr);
|
||||
bool fOk = true;
|
||||
size_t cb = static_cast<size_t>(GetRandomDataWriteBufferSize());
|
||||
if (GetRandomDataWriteBufferSourcePath() == "")
|
||||
{
|
||||
// fill buffer with random data
|
||||
for (size_t i = 0; i < cb; i++)
|
||||
{
|
||||
_pRandomDataWriteBuffer[i] = (rand() % 256);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// fill buffer from file
|
||||
HANDLE hFile = CreateFile(GetRandomDataWriteBufferSourcePath().c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
if (hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
UINT64 cbLeftToRead = GetRandomDataWriteBufferSize();
|
||||
BYTE *pBuffer = _pRandomDataWriteBuffer;
|
||||
bool fReadSuccess = true;
|
||||
while (fReadSuccess && cbLeftToRead > 0)
|
||||
{
|
||||
DWORD cbToRead = static_cast<DWORD>(min(64 * 1024, cbLeftToRead));
|
||||
DWORD cbRead;
|
||||
fReadSuccess = ((ReadFile(hFile, pBuffer, cbToRead, &cbRead, nullptr) == TRUE) && (cbRead > 0));
|
||||
pBuffer += cbRead;
|
||||
}
|
||||
|
||||
// if the file is smaller than the buffer, repeat its content
|
||||
BYTE *pSource = _pRandomDataWriteBuffer;
|
||||
const BYTE *pPastEnd = pSource + GetRandomDataWriteBufferSize();
|
||||
while (pBuffer < pPastEnd)
|
||||
{
|
||||
*pBuffer++ = *pSource++;
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
fOk = false;
|
||||
// TODO: print error message?
|
||||
}
|
||||
}
|
||||
return fOk;
|
||||
}
|
||||
|
||||
bool Target::AllocateAndFillRandomDataWriteBuffer()
|
||||
{
|
||||
assert(_pRandomDataWriteBuffer == nullptr);
|
||||
bool fOk = true;
|
||||
size_t cb = static_cast<size_t>(GetRandomDataWriteBufferSize());
|
||||
assert(cb > 0);
|
||||
|
||||
// TODO: make sure the size if <= max value for size_t
|
||||
/// CrystalDiskMark 4 does not support Large Page
|
||||
if (false && GetUseLargePages())
|
||||
{
|
||||
/// size_t cbMinLargePage = GetLargePageMinimum();
|
||||
/// size_t cbRoundedSize = (cb + cbMinLargePage - 1) & ~(cbMinLargePage - 1);
|
||||
/// _pRandomDataWriteBuffer = (BYTE *)VirtualAlloc(nullptr, cbRoundedSize, MEM_COMMIT | MEM_RESERVE | MEM_LARGE_PAGES, PAGE_EXECUTE_READWRITE);
|
||||
}
|
||||
else
|
||||
{
|
||||
_pRandomDataWriteBuffer = (BYTE *)VirtualAlloc(nullptr, cb, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
|
||||
fOk = (_pRandomDataWriteBuffer != nullptr);
|
||||
if (fOk)
|
||||
{
|
||||
fOk = _FillRandomDataWriteBuffer();
|
||||
}
|
||||
return fOk;
|
||||
}
|
||||
|
||||
void Target::FreeRandomDataWriteBuffer()
|
||||
{
|
||||
if (nullptr != _pRandomDataWriteBuffer)
|
||||
{
|
||||
VirtualFree(_pRandomDataWriteBuffer, 0, MEM_RELEASE);
|
||||
_pRandomDataWriteBuffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
BYTE* Target::GetRandomDataWriteBuffer()
|
||||
{
|
||||
size_t cbBuffer = static_cast<size_t>(GetRandomDataWriteBufferSize());
|
||||
size_t cbBlock = GetBlockSizeInBytes();
|
||||
|
||||
// leave enough bytes in the buffer for one block
|
||||
size_t randomOffset = rand() % (cbBuffer - (cbBlock - 1));
|
||||
|
||||
bool fUnbufferedIO = (GetDisableOSCache() || GetDisableAllCache());
|
||||
if (fUnbufferedIO)
|
||||
{
|
||||
// for unbuffered IO, offset in the buffer needs to be DWORD-aligned
|
||||
const size_t cbAlignment = 4;
|
||||
randomOffset -= (randomOffset % cbAlignment);
|
||||
}
|
||||
|
||||
BYTE *pBuffer = reinterpret_cast<BYTE*>(reinterpret_cast<ULONG_PTR>(_pRandomDataWriteBuffer)+randomOffset);
|
||||
|
||||
// unbuffered IO needs aligned addresses
|
||||
assert(!fUnbufferedIO || (reinterpret_cast<ULONG_PTR>(pBuffer) % 4 == 0));
|
||||
|
||||
assert(pBuffer >= _pRandomDataWriteBuffer);
|
||||
assert(pBuffer <= _pRandomDataWriteBuffer + GetRandomDataWriteBufferSize() - GetBlockSizeInBytes());
|
||||
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
string TimeSpan::GetXml() const
|
||||
{
|
||||
string sXml("<TimeSpan>\n");
|
||||
char buffer[4096];
|
||||
|
||||
sXml += _fCompletionRoutines ? "<CompletionRoutines>true</CompletionRoutines>\n" : "<CompletionRoutines>false</CompletionRoutines>\n";
|
||||
sXml += _fMeasureLatency ? "<MeasureLatency>true</MeasureLatency>\n" : "<MeasureLatency>false</MeasureLatency>\n";
|
||||
sXml += _fCalculateIopsStdDev ? "<CalculateIopsStdDev>true</CalculateIopsStdDev>\n" : "<CalculateIopsStdDev>false</CalculateIopsStdDev>\n";
|
||||
sXml += _fDisableAffinity ? "<DisableAffinity>true</DisableAffinity>\n" : "<DisableAffinity>false</DisableAffinity>\n";
|
||||
sXml += _fGroupAffinity ? "<GroupAffinity>true</GroupAffinity>\n" : "<GroupAffinity>false</GroupAffinity>\n";
|
||||
|
||||
sprintf_s(buffer, _countof(buffer), "<Duration>%u</Duration>\n", _ulDuration);
|
||||
sXml += buffer;
|
||||
|
||||
sprintf_s(buffer, _countof(buffer), "<Warmup>%u</Warmup>\n", _ulWarmUp);
|
||||
sXml += buffer;
|
||||
|
||||
sprintf_s(buffer, _countof(buffer), "<Cooldown>%u</Cooldown>\n", _ulCoolDown);
|
||||
sXml += buffer;
|
||||
|
||||
sprintf_s(buffer, _countof(buffer), "<ThreadCount>%u</ThreadCount>\n", _dwThreadCount);
|
||||
sXml += buffer;
|
||||
|
||||
sprintf_s(buffer, _countof(buffer), "<IoBucketDuration>%u</IoBucketDuration>\n", _ulIoBucketDurationInMilliseconds);
|
||||
sXml += buffer;
|
||||
|
||||
sprintf_s(buffer, _countof(buffer), "<RandSeed>%u</RandSeed>\n", _ulRandSeed);
|
||||
sXml += buffer;
|
||||
|
||||
if (_vAffinity.size() > 0)
|
||||
{
|
||||
sXml += "<Affinity>\n";
|
||||
for (auto a : _vAffinity)
|
||||
{
|
||||
sprintf_s(buffer, _countof(buffer), "<AffinityAssignment>%u</AffinityAssignment>\n", a);
|
||||
sXml += buffer;
|
||||
}
|
||||
sXml += "</Affinity>\n";
|
||||
}
|
||||
|
||||
sXml += "<Targets>\n";
|
||||
for (const auto& target : _vTargets)
|
||||
{
|
||||
sXml += target.GetXml();
|
||||
}
|
||||
sXml += "</Targets>\n";
|
||||
sXml += "</TimeSpan>\n";
|
||||
return sXml;
|
||||
}
|
||||
|
||||
void TimeSpan::MarkFilesAsPrecreated(const vector<string> vFiles)
|
||||
{
|
||||
for (auto sFile : vFiles)
|
||||
{
|
||||
for (auto pTarget = _vTargets.begin(); pTarget != _vTargets.end(); pTarget++)
|
||||
{
|
||||
if (sFile == pTarget->GetPath())
|
||||
{
|
||||
pTarget->SetPrecreated(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string Profile::GetXml() const
|
||||
{
|
||||
string sXml("<Profile>\n");
|
||||
char buffer[4096];
|
||||
|
||||
sprintf_s(buffer, _countof(buffer), "<Progress>%u</Progress>\n", _dwProgress);
|
||||
sXml += buffer;
|
||||
|
||||
if (_resultsFormat == ResultsFormat::Text)
|
||||
{
|
||||
sXml += "<ResultFormat>text</ResultFormat>\n";
|
||||
}
|
||||
else if (_resultsFormat == ResultsFormat::Xml)
|
||||
{
|
||||
sXml += "<ResultFormat>xml</ResultFormat>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
sXml += "<ResultFormat>* UNSUPPORTED *</ResultFormat>\n";
|
||||
}
|
||||
|
||||
sXml += _fVerbose ? "<Verbose>true</Verbose>\n" : "<Verbose>false</Verbose>\n";
|
||||
if (_precreateFiles == PrecreateFiles::UseMaxSize)
|
||||
{
|
||||
sXml += "<PrecreateFiles>UseMaxSize</PrecreateFiles>\n";
|
||||
}
|
||||
else if (_precreateFiles == PrecreateFiles::OnlyFilesWithConstantSizes)
|
||||
{
|
||||
sXml += "<PrecreateFiles>CreateOnlyFilesWithConstantSizes</PrecreateFiles>\n";
|
||||
}
|
||||
else if (_precreateFiles == PrecreateFiles::OnlyFilesWithConstantOrZeroSizes)
|
||||
{
|
||||
sXml += "<PrecreateFiles>CreateOnlyFilesWithConstantOrZeroSizes</PrecreateFiles>\n";
|
||||
}
|
||||
|
||||
if (_fEtwEnabled)
|
||||
{
|
||||
sXml += _fEtwProcess ? "<Process>true</Process>\n" : "<Process>false</Process>\n";
|
||||
sXml += _fEtwThread ? "<Thread>true</Thread>\n" : "<Thread>false</Thread>\n";
|
||||
sXml += _fEtwImageLoad ? "<ImageLoad>true</ImageLoad>\n" : "<ImageLoad>false</ImageLoad>\n";
|
||||
sXml += _fEtwDiskIO ? "<DiskIO>true</DiskIO>\n" : "<DiskIO>false</DiskIO>\n";
|
||||
sXml += _fEtwMemoryPageFaults ? "<MemoryPageFaults>true</MemoryPageFaults>\n" : "<MemoryPageFaults>false</MemoryPageFaults>\n";
|
||||
sXml += _fEtwMemoryHardFaults ? "<MemoryHardFaults>true</MemoryHardFaults>\n" : "<MemoryHardFaults>false</MemoryHardFaults>\n";
|
||||
sXml += _fEtwNetwork ? "<Network>true</Network>\n" : "<Network>false</Network>\n";
|
||||
sXml += _fEtwRegistry ? "<Registry>true</Registry>\n" : "<Registry>false</Registry>\n";
|
||||
sXml += _fEtwUsePagedMemory ? "<UsePagedMemory>true</UsePagedMemory>\n" : "<UsePagedMemory>false</UsePagedMemory>\n";
|
||||
sXml += _fEtwUsePerfTimer ? "<UsePerfTimer>true</UsePerfTimer>\n" : "<UsePerfTimer>false</UsePerfTimer>\n";
|
||||
sXml += _fEtwUseSystemTimer ? "<UseSystemTimer>true</UseSystemTimer>\n" : "<UseSystemTimer>false</UseSystemTimer>\n";
|
||||
sXml += _fEtwUseCyclesCounter ? "<UseCyclesCounter>true</UseCyclesCounter>\n" : "<UseCyclesCounter>false</UseCyclesCounter>\n";
|
||||
}
|
||||
|
||||
sXml += "<TimeSpans>\n";
|
||||
for (const auto& timespan : _vTimeSpans)
|
||||
{
|
||||
sXml += timespan.GetXml();
|
||||
}
|
||||
sXml += "</TimeSpans>\n";
|
||||
sXml += "</Profile>\n";
|
||||
return sXml;
|
||||
}
|
||||
|
||||
void Profile::MarkFilesAsPrecreated(const vector<string> vFiles)
|
||||
{
|
||||
for (auto pTimeSpan = _vTimeSpans.begin(); pTimeSpan != _vTimeSpans.end(); pTimeSpan++)
|
||||
{
|
||||
pTimeSpan->MarkFilesAsPrecreated(vFiles);
|
||||
}
|
||||
}
|
||||
|
||||
bool Profile::Validate(bool fSingleSpec) const
|
||||
{
|
||||
bool fOk = true;
|
||||
for (const auto& timeSpan : GetTimeSpans())
|
||||
{
|
||||
if (timeSpan.GetDisableAffinity() && timeSpan.GetAffinityAssignments().size() > 0)
|
||||
{
|
||||
fprintf(stderr, "ERROR: -n and -a parameters cannot be used together\n");
|
||||
fOk = false;
|
||||
}
|
||||
|
||||
for (const auto& target : timeSpan.GetTargets())
|
||||
{
|
||||
const bool targetHasMultipleThreads = (timeSpan.GetThreadCount() > 1) || (target.GetThreadsPerFile() > 1);
|
||||
|
||||
if (timeSpan.GetThreadCount() > 0 && target.GetThreadsPerFile() > 1)
|
||||
{
|
||||
fprintf(stderr, "ERROR: -F and -t parameters cannot be used together\n");
|
||||
fOk = false;
|
||||
}
|
||||
|
||||
if (target.GetDisableAllCache() && target.GetDisableOSCache())
|
||||
{
|
||||
fprintf(stderr, "WARNING: -S is included in the effect of -h, specifying both is not required\n");
|
||||
}
|
||||
|
||||
if (target.GetThroughputInBytesPerMillisecond() > 0 && timeSpan.GetCompletionRoutines())
|
||||
{
|
||||
fprintf(stderr, "ERROR: -g throughput control cannot be used with -x completion routines\n");
|
||||
fOk = false;
|
||||
}
|
||||
|
||||
// If burst size is specified think time must be specified and If think time is specified burst size should be non zero
|
||||
if ((target.GetThinkTime() == 0 && target.GetBurstSize() > 0) || (target.GetThinkTime() > 0 && target.GetBurstSize() == 0))
|
||||
{
|
||||
fprintf(stderr, "ERROR: need to specify -j<think time> with -i<burst size>\n");
|
||||
fOk = false;
|
||||
}
|
||||
|
||||
// FIXME: we can no longer do this check, because the target no longer
|
||||
// contains a property that uniquely identifies the case where "-s" or <StrideSize>
|
||||
// was passed.
|
||||
#if 0
|
||||
if (target.GetUseRandomAccessPattern() && target.GetEnableCustomStrideSize())
|
||||
{
|
||||
fprintf(stderr, "WARNING: -s is ignored if -r is provided\n");
|
||||
}
|
||||
#endif
|
||||
if (target.GetUseRandomAccessPattern())
|
||||
{
|
||||
if (target.GetThreadStrideInBytes() > 0)
|
||||
{
|
||||
fprintf(stderr, "ERROR: -T conflicts with -r\n");
|
||||
fOk = false;
|
||||
// although ullThreadStride==0 is a valid value, it's interpreted as "not provided" for this warning
|
||||
}
|
||||
|
||||
if (target.GetUseInterlockedSequential())
|
||||
{
|
||||
fprintf(stderr, "ERROR: -si conflicts with -r\n");
|
||||
fOk = false;
|
||||
}
|
||||
|
||||
if (target.GetUseParallelAsyncIO())
|
||||
{
|
||||
fprintf(stderr, "ERROR: -p conflicts with -r\n");
|
||||
fOk = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target.GetUseParallelAsyncIO() && target.GetRequestCount() == 1)
|
||||
{
|
||||
fprintf(stderr, "WARNING: -p does not have effect unless outstanding I/O count (-o) is > 1\n");
|
||||
}
|
||||
|
||||
if (timeSpan.GetRandSeed() > 0)
|
||||
{
|
||||
fprintf(stderr, "WARNING: -z is ignored if -r is not provided\n");
|
||||
// although ulRandSeed==0 is a valid value, it's interpreted as "not provided" for this warning
|
||||
}
|
||||
|
||||
if (target.GetUseInterlockedSequential())
|
||||
{
|
||||
if (target.GetThreadStrideInBytes() > 0)
|
||||
{
|
||||
fprintf(stderr, "ERROR: -si conflicts with -T\n");
|
||||
fOk = false;
|
||||
}
|
||||
|
||||
if (target.GetUseParallelAsyncIO())
|
||||
{
|
||||
fprintf(stderr, "ERROR: -si conflicts with -p\n");
|
||||
fOk = false;
|
||||
}
|
||||
|
||||
if (!targetHasMultipleThreads)
|
||||
{
|
||||
fprintf(stderr, "WARNING: single-threaded test, -si ignored\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (targetHasMultipleThreads && !target.GetThreadStrideInBytes())
|
||||
{
|
||||
fprintf(stderr, "WARNING: target access pattern will not be sequential, consider -si\n");
|
||||
}
|
||||
|
||||
if (!targetHasMultipleThreads && target.GetThreadStrideInBytes())
|
||||
{
|
||||
fprintf(stderr, "ERROR: -T has no effect unless multiple threads per target are used\n");
|
||||
fOk = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (target.GetRandomDataWriteBufferSize() > 0)
|
||||
{
|
||||
if (target.GetRandomDataWriteBufferSize() < target.GetBlockSizeInBytes())
|
||||
{
|
||||
fprintf(stderr, "ERROR: custom write buffer (-Z) is smaller than the block size. Write buffer size: %I64u block size: %u\n",
|
||||
target.GetRandomDataWriteBufferSize(),
|
||||
target.GetBlockSizeInBytes());
|
||||
fOk = false;
|
||||
}
|
||||
}
|
||||
|
||||
// in the cases where there is only a single configuration specified for each target (e.g., cmdline),
|
||||
// currently there are no validations specific to individual targets (e.g., pre-existing files)
|
||||
// so we can stop validation now. this allows us to only warn/error once, as opposed to repeating
|
||||
// it for each target.
|
||||
if (fSingleSpec)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fOk;
|
||||
}
|
||||
|
||||
bool ThreadParameters::AllocateAndFillBufferForTarget(const Target& target)
|
||||
{
|
||||
bool fOk = true;
|
||||
BYTE *pDataBuffer = nullptr;
|
||||
size_t cbDataBuffer = target.GetBlockSizeInBytes() * target.GetRequestCount();
|
||||
|
||||
/// CrystalDiskMark 4 does not support Large Page
|
||||
if (false && target.GetUseLargePages())
|
||||
{
|
||||
/// size_t cbMinLargePage = GetLargePageMinimum();
|
||||
/// size_t cbRoundedSize = (cbDataBuffer + cbMinLargePage - 1) & ~(cbMinLargePage - 1);
|
||||
/// pDataBuffer = (BYTE *)VirtualAlloc(nullptr, cbRoundedSize, MEM_COMMIT | MEM_RESERVE | MEM_LARGE_PAGES, PAGE_EXECUTE_READWRITE);
|
||||
}
|
||||
else
|
||||
{
|
||||
pDataBuffer = (BYTE *)VirtualAlloc(nullptr, cbDataBuffer, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
|
||||
fOk = (pDataBuffer != nullptr);
|
||||
|
||||
//fill buffer (useful only for write tests)
|
||||
if (fOk && target.GetWriteRatio() > 0)
|
||||
{
|
||||
if (target.GetZeroWriteBuffers())
|
||||
{
|
||||
memset(pDataBuffer, 0, cbDataBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < cbDataBuffer; i++)
|
||||
{
|
||||
pDataBuffer[i] = (BYTE)(i % 256);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fOk)
|
||||
{
|
||||
vpDataBuffers.push_back(pDataBuffer);
|
||||
}
|
||||
|
||||
return fOk;
|
||||
}
|
||||
|
||||
BYTE* ThreadParameters::GetReadBuffer(size_t iTarget, size_t iRequest)
|
||||
{
|
||||
return vpDataBuffers[iTarget] + (iRequest * vTargets[iTarget].GetBlockSizeInBytes());
|
||||
}
|
||||
|
||||
BYTE* ThreadParameters::GetWriteBuffer(size_t iTarget, size_t iRequest)
|
||||
{
|
||||
BYTE *pBuffer = nullptr;
|
||||
|
||||
Target& target(vTargets[iTarget]);
|
||||
size_t cb = static_cast<size_t>(target.GetRandomDataWriteBufferSize());
|
||||
if (cb == 0)
|
||||
{
|
||||
pBuffer = vpDataBuffers[iTarget] + (iRequest * vTargets[iTarget].GetBlockSizeInBytes());
|
||||
}
|
||||
else
|
||||
{
|
||||
pBuffer = target.GetRandomDataWriteBuffer();
|
||||
}
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
DWORD ThreadParameters::GetTotalRequestCount() const
|
||||
{
|
||||
DWORD cRequests = 0;
|
||||
|
||||
for (const auto& t : vTargets)
|
||||
{
|
||||
cRequests += t.GetRequestCount();
|
||||
}
|
||||
|
||||
return cRequests;
|
||||
}
|
||||
@@ -0,0 +1,814 @@
|
||||
/*
|
||||
|
||||
DISKSPD
|
||||
|
||||
Copyright(c) Microsoft Corporation
|
||||
All rights reserved.
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <vector>
|
||||
#include <Winternl.h> //ntdll.dll
|
||||
#include <assert.h>
|
||||
#include "Histogram.h"
|
||||
#include "IoBucketizer.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/// structures used for passing the input parameters
|
||||
|
||||
// versioning material. for simplicity in consumption, please ensure that the date string
|
||||
// parses via the System.Datetime constructor as follows (in Powershell):
|
||||
//
|
||||
// [datetime] "string"
|
||||
//
|
||||
// this should result in a valid System.Datetime object, rendered like:
|
||||
//
|
||||
// Monday, June 16, 2014 12:00:00 AM
|
||||
|
||||
#define DISKSPD_RELEASE_TAG ""
|
||||
#define DISKSPD_NUMERIC_VERSION_STRING "2.0.15" DISKSPD_RELEASE_TAG
|
||||
#define DISKSPD_DATE_VERSION_STRING "2015/01/09"
|
||||
|
||||
typedef void (WINAPI *PRINTF)(const char*, va_list); //function used for displaying formatted data (printf style)
|
||||
|
||||
struct ETWEventCounters
|
||||
{
|
||||
UINT64 ullIORead; // Read
|
||||
UINT64 ullIOWrite; // Write
|
||||
UINT64 ullMMTransitionFault; // Transition fault
|
||||
UINT64 ullMMDemandZeroFault; // Demand Zero fault
|
||||
UINT64 ullMMCopyOnWrite; // Copy on Write
|
||||
UINT64 ullMMGuardPageFault; // Guard Page fault
|
||||
UINT64 ullMMHardPageFault; // Hard page fault
|
||||
UINT64 ullNetTcpSend; // Send
|
||||
UINT64 ullNetTcpReceive; // Receive
|
||||
UINT64 ullNetUdpSend; // Send
|
||||
UINT64 ullNetUdpReceive; // Receive
|
||||
UINT64 ullNetConnect; // Connect
|
||||
UINT64 ullNetDisconnect; // Disconnect
|
||||
UINT64 ullNetRetransmit; // ReTransmit
|
||||
UINT64 ullNetAccept; // Accept
|
||||
UINT64 ullNetReconnect; // ReConnect
|
||||
UINT64 ullRegCreate; // NtCreateKey
|
||||
UINT64 ullRegOpen; // NtOpenKey
|
||||
UINT64 ullRegDelete; // NtDeleteKey
|
||||
UINT64 ullRegQuery; // NtQueryKey
|
||||
UINT64 ullRegSetValue; // NtSetValueKey
|
||||
UINT64 ullRegDeleteValue; // NtDeleteValueKey
|
||||
UINT64 ullRegQueryValue; // NtQueryValueKey
|
||||
UINT64 ullRegEnumerateKey; // NtEnumerateKey
|
||||
UINT64 ullRegEnumerateValueKey; // NtEnumerateValueKey
|
||||
UINT64 ullRegQueryMultipleValue; // NtQueryMultipleValueKey
|
||||
UINT64 ullRegSetInformation; // NtSetInformationKey
|
||||
UINT64 ullRegFlush; // NtFlushKey
|
||||
UINT64 ullRegKcbDmp; // KcbDump/create
|
||||
UINT64 ullThreadStart;
|
||||
UINT64 ullThreadEnd;
|
||||
UINT64 ullProcessStart;
|
||||
UINT64 ullProcessEnd;
|
||||
UINT64 ullImageLoad;
|
||||
};
|
||||
|
||||
|
||||
// structure containing informations about ETW session
|
||||
struct ETWSessionInfo
|
||||
{
|
||||
ULONG ulBufferSize;
|
||||
ULONG ulMinimumBuffers;
|
||||
ULONG ulMaximumBuffers;
|
||||
ULONG ulFreeBuffers;
|
||||
ULONG ulBuffersWritten;
|
||||
ULONG ulFlushTimer;
|
||||
LONG lAgeLimit;
|
||||
ULONG ulNumberOfBuffers;
|
||||
ULONG ulEventsLost;
|
||||
ULONG ulLogBuffersLost;
|
||||
ULONG ulRealTimeBuffersLost;
|
||||
};
|
||||
|
||||
// structure containing parameters concerning ETW session provided by user
|
||||
struct ETWMask
|
||||
{
|
||||
BOOL bProcess;
|
||||
BOOL bThread;
|
||||
BOOL bImageLoad;
|
||||
BOOL bDiskIO;
|
||||
BOOL bMemoryPageFaults;
|
||||
BOOL bMemoryHardFaults;
|
||||
BOOL bNetwork;
|
||||
BOOL bRegistry;
|
||||
BOOL bUsePagedMemory;
|
||||
BOOL bUsePerfTimer;
|
||||
BOOL bUseSystemTimer;
|
||||
BOOL bUseCyclesCounter;
|
||||
};
|
||||
|
||||
namespace UnitTests
|
||||
{
|
||||
class PerfTimerUnitTests;
|
||||
class ProfileUnitTests;
|
||||
class TargetUnitTests;
|
||||
}
|
||||
|
||||
class PerfTimer
|
||||
{
|
||||
public:
|
||||
|
||||
static UINT64 GetTime();
|
||||
|
||||
static double PerfTimeToMicroseconds(const double);
|
||||
static double PerfTimeToMilliseconds(const double);
|
||||
static double PerfTimeToSeconds(const double);
|
||||
static double PerfTimeToMicroseconds(const UINT64);
|
||||
static double PerfTimeToMilliseconds(const UINT64);
|
||||
static double PerfTimeToSeconds(const UINT64);
|
||||
|
||||
static UINT64 MicrosecondsToPerfTime(const double);
|
||||
static UINT64 MillisecondsToPerfTime(const double);
|
||||
static UINT64 SecondsToPerfTime(const double);
|
||||
|
||||
private:
|
||||
|
||||
static const UINT64 TIMER_FREQ;
|
||||
static UINT64 _GetPerfTimerFreq();
|
||||
|
||||
friend class UnitTests::PerfTimerUnitTests;
|
||||
};
|
||||
|
||||
struct PercentileDescriptor
|
||||
{
|
||||
double Percentile;
|
||||
string Name;
|
||||
};
|
||||
|
||||
class Util
|
||||
{
|
||||
public:
|
||||
static string DoubleToStringHelper(const double);
|
||||
template<typename T> static T QuotientCeiling(T dividend, T divisor)
|
||||
{
|
||||
return (dividend + divisor - 1) / divisor;
|
||||
}
|
||||
};
|
||||
|
||||
// To keep track of which type of IO was issued
|
||||
enum class IOOperation
|
||||
{
|
||||
ReadIO = 1,
|
||||
WriteIO
|
||||
};
|
||||
|
||||
class TargetResults
|
||||
{
|
||||
public:
|
||||
TargetResults() :
|
||||
ullFileSize(0),
|
||||
ullBytesCount(0),
|
||||
ullIOCount(0),
|
||||
ullReadBytesCount(0),
|
||||
ullReadIOCount(0),
|
||||
ullWriteBytesCount(0),
|
||||
ullWriteIOCount(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Add(DWORD dwBytesTransferred,
|
||||
IOOperation type,
|
||||
PUINT64 pullIoStartTime,
|
||||
PUINT64 pullSpanStartTime,
|
||||
bool fMeasureLatency,
|
||||
bool fCalculateIopsStdDev
|
||||
)
|
||||
{
|
||||
float fDurationMsec = 0;
|
||||
UINT64 ullEndTime = 0;
|
||||
// assume it is worthwhile to stay off of the time query path unless needed (micro-overhead)
|
||||
if (fMeasureLatency || fCalculateIopsStdDev)
|
||||
{
|
||||
ullEndTime = PerfTimer::GetTime();
|
||||
}
|
||||
|
||||
if (fMeasureLatency)
|
||||
{
|
||||
UINT64 ullDuration = ullEndTime - *pullIoStartTime;
|
||||
fDurationMsec = static_cast<float>(PerfTimer::PerfTimeToMicroseconds(ullDuration));
|
||||
|
||||
if (type == IOOperation::ReadIO)
|
||||
{
|
||||
readLatencyHistogram.Add(fDurationMsec);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeLatencyHistogram.Add(fDurationMsec);
|
||||
}
|
||||
}
|
||||
|
||||
UINT64 ullRelativeCompletionTime = 0;
|
||||
if (fCalculateIopsStdDev)
|
||||
{
|
||||
ullRelativeCompletionTime = ullEndTime - *pullSpanStartTime;
|
||||
|
||||
if (type == IOOperation::ReadIO)
|
||||
{
|
||||
readBucketizer.Add(ullRelativeCompletionTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeBucketizer.Add(ullRelativeCompletionTime);
|
||||
}
|
||||
}
|
||||
|
||||
if (type == IOOperation::ReadIO)
|
||||
{
|
||||
ullReadBytesCount += dwBytesTransferred; // update read bytes counter
|
||||
ullReadIOCount++; // update completed read I/O operations counter
|
||||
}
|
||||
else
|
||||
{
|
||||
ullWriteBytesCount += dwBytesTransferred; // update write bytes counter
|
||||
ullWriteIOCount++; // update completed write I/O operations counter
|
||||
}
|
||||
|
||||
ullBytesCount += dwBytesTransferred; // update bytes counter
|
||||
ullIOCount++; // update completed I/O operations counter
|
||||
}
|
||||
|
||||
string sPath;
|
||||
UINT64 ullFileSize; //size of the file
|
||||
UINT64 ullBytesCount; //number of accessed bytes
|
||||
UINT64 ullIOCount; //number of performed I/O operations
|
||||
UINT64 ullReadBytesCount; //number of bytes read
|
||||
UINT64 ullReadIOCount; //number of performed Read I/O operations
|
||||
UINT64 ullWriteBytesCount; //number of bytes written
|
||||
UINT64 ullWriteIOCount; //number of performed Write I/O operations
|
||||
|
||||
Histogram<float> readLatencyHistogram;
|
||||
Histogram<float> writeLatencyHistogram;
|
||||
|
||||
IoBucketizer readBucketizer;
|
||||
IoBucketizer writeBucketizer;
|
||||
};
|
||||
|
||||
class ThreadResults
|
||||
{
|
||||
public:
|
||||
vector<TargetResults> vTargetResults;
|
||||
};
|
||||
|
||||
class Results
|
||||
{
|
||||
public:
|
||||
bool fUseETW;
|
||||
struct ETWEventCounters EtwEventCounters;
|
||||
struct ETWMask EtwMask;
|
||||
struct ETWSessionInfo EtwSessionInfo;
|
||||
vector<ThreadResults> vThreadResults;
|
||||
UINT64 ullTimeCount;
|
||||
vector<SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION> vSystemProcessorPerfInfo;
|
||||
};
|
||||
|
||||
typedef void (*CALLBACK_TEST_STARTED)(); //callback function to notify that the measured test is about to start
|
||||
typedef void (*CALLBACK_TEST_FINISHED)(); //callback function to notify that the measured test has just finished
|
||||
|
||||
class SystemInformation
|
||||
{
|
||||
public:
|
||||
string sComputerName;
|
||||
|
||||
SystemInformation()
|
||||
{
|
||||
char buffer[64];
|
||||
DWORD cb = _countof(buffer);
|
||||
BOOL fResult;
|
||||
|
||||
#pragma prefast(suppress:38020, "Yes, we're aware this is an ANSI API in a UNICODE project")
|
||||
fResult = GetComputerNameExA(ComputerNamePhysicalDnsHostname, buffer, &cb);
|
||||
if (fResult)
|
||||
{
|
||||
sComputerName = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
string SystemInformation::GetXml() const
|
||||
{
|
||||
string sXml("<System>\n");
|
||||
|
||||
// identify computer which ran the test
|
||||
sXml += "<ComputerName>";
|
||||
sXml += sComputerName;
|
||||
sXml += "</ComputerName>\n";
|
||||
|
||||
// identify tool version which performed the test
|
||||
sXml += "<Tool>\n";
|
||||
sXml += "<Version>" DISKSPD_NUMERIC_VERSION_STRING "</Version>\n";
|
||||
sXml += "<VersionDate>" DISKSPD_DATE_VERSION_STRING "</VersionDate>\n";
|
||||
sXml += "</Tool>\n";
|
||||
|
||||
sXml += "</System>\n";
|
||||
|
||||
return sXml;
|
||||
}
|
||||
};
|
||||
|
||||
struct Synchronization
|
||||
{
|
||||
ULONG ulStructSize; //size of the structure that the caller is aware of (to easier achieve backward compatibility in a future)
|
||||
HANDLE hStopEvent; //an event to be signalled if the scenario is to be stop before time ellapses
|
||||
HANDLE hStartEvent; //an event for signalling start
|
||||
CALLBACK_TEST_STARTED pfnCallbackTestStarted; //a function to be called if the measured test is about to start
|
||||
CALLBACK_TEST_FINISHED pfnCallbackTestFinished; //a function to be called as soon as the measrued test finishes
|
||||
};
|
||||
|
||||
#define STRUCT_SYNCHRONIZATION_SUPPORTS(pSynch, Field) ( \
|
||||
(NULL != (pSynch)) && \
|
||||
((pSynch)->ulStructSize >= offsetof(struct Synchronization, Field) + sizeof((pSynch)->Field)) \
|
||||
)
|
||||
|
||||
class Target
|
||||
{
|
||||
public:
|
||||
|
||||
Target() :
|
||||
_dwBlockSize(64 * 1024),
|
||||
_dwRequestCount(2),
|
||||
_ullBlockAlignment(64 * 1024),
|
||||
_fBlockAlignmentValid(false),
|
||||
_fUseRandomAccessPattern(false),
|
||||
_ullBaseFileOffset(0),
|
||||
_fParallelAsyncIO(false),
|
||||
_fInterlockedSequential(false),
|
||||
_fDisableOSCache(false),
|
||||
_fDisableAllCache(false),
|
||||
_fZeroWriteBuffers(false),
|
||||
_dwThreadsPerFile(1),
|
||||
_ullThreadStride(0),
|
||||
_fCreateFile(false),
|
||||
_fPrecreated(false),
|
||||
_ullFileSize(0),
|
||||
_ullMaxFileSize(0),
|
||||
_ulWriteRatio(0),
|
||||
_fUseBurstSize(false),
|
||||
_dwBurstSize(0),
|
||||
_dwThinkTime(0),
|
||||
_fThinkTime(false),
|
||||
_fSequentialScanHint(false),
|
||||
_fRandomAccessHint(false),
|
||||
_fUseLargePages(false),
|
||||
_ioPriorityHint(IoPriorityHintNormal),
|
||||
_dwThroughputBytesPerMillisecond(0),
|
||||
_cbRandomDataWriteBuffer(0),
|
||||
_sRandomDataWriteBufferSourcePath(),
|
||||
_pRandomDataWriteBuffer(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void SetPath(string sPath) { _sPath = sPath; }
|
||||
string GetPath() const { return _sPath; }
|
||||
|
||||
void SetBlockSizeInBytes(DWORD dwBlockSize) { _dwBlockSize = dwBlockSize; }
|
||||
DWORD GetBlockSizeInBytes() const { return _dwBlockSize; }
|
||||
|
||||
void SetBlockAlignmentInBytes(UINT64 ullBlockAlignment)
|
||||
{
|
||||
_ullBlockAlignment = ullBlockAlignment;
|
||||
_fBlockAlignmentValid = true;
|
||||
}
|
||||
|
||||
UINT64 GetBlockAlignmentInBytes() const
|
||||
{
|
||||
return _fBlockAlignmentValid ? _ullBlockAlignment : _dwBlockSize;
|
||||
}
|
||||
|
||||
void SetUseRandomAccessPattern(bool fUseRandomAccessPattern) { _fUseRandomAccessPattern = fUseRandomAccessPattern; }
|
||||
bool GetUseRandomAccessPattern() const { return _fUseRandomAccessPattern; }
|
||||
|
||||
void SetBaseFileOffsetInBytes(UINT64 ullBaseFileOffset) { _ullBaseFileOffset = ullBaseFileOffset; }
|
||||
UINT64 GetBaseFileOffsetInBytes() const { return _ullBaseFileOffset; }
|
||||
|
||||
void SetSequentialScanHint(bool fSequentialScanHint) { _fSequentialScanHint = fSequentialScanHint; }
|
||||
bool GetSequentialScanHint() const { return _fSequentialScanHint; }
|
||||
|
||||
void SetRandomAccessHint(bool fRandomAccessHint) { _fRandomAccessHint = fRandomAccessHint; }
|
||||
bool GetRandomAccessHint() const { return _fRandomAccessHint; }
|
||||
|
||||
void SetUseLargePages(bool fUseLargePages) { _fUseLargePages = fUseLargePages; }
|
||||
bool GetUseLargePages() const { return _fUseLargePages; }
|
||||
|
||||
void SetRequestCount(DWORD dwRequestCount) { _dwRequestCount = dwRequestCount; }
|
||||
DWORD GetRequestCount() const { return _dwRequestCount; }
|
||||
|
||||
void SetDisableOSCache(bool fDisableOSCache) { _fDisableOSCache = fDisableOSCache; }
|
||||
bool GetDisableOSCache() const { return _fDisableOSCache; }
|
||||
|
||||
void SetDisableAllCache(bool fDisableAllCache) { _fDisableAllCache = fDisableAllCache; }
|
||||
bool GetDisableAllCache() const { return _fDisableAllCache; }
|
||||
|
||||
void SetZeroWriteBuffers(bool fZeroWriteBuffers) { _fZeroWriteBuffers = fZeroWriteBuffers; }
|
||||
bool GetZeroWriteBuffers() const { return _fZeroWriteBuffers; }
|
||||
|
||||
void SetRandomDataWriteBufferSize(UINT64 cbWriteBuffer) { _cbRandomDataWriteBuffer = cbWriteBuffer; }
|
||||
UINT64 GetRandomDataWriteBufferSize(void) const { return _cbRandomDataWriteBuffer; }
|
||||
|
||||
void SetRandomDataWriteBufferSourcePath(string sPath) { _sRandomDataWriteBufferSourcePath = sPath; }
|
||||
string GetRandomDataWriteBufferSourcePath() const { return _sRandomDataWriteBufferSourcePath; }
|
||||
|
||||
void SetUseBurstSize(bool fUseBurstSize) { _fUseBurstSize = fUseBurstSize; }
|
||||
bool GetUseBurstSize() const { return _fUseBurstSize; }
|
||||
|
||||
void SetBurstSize(DWORD dwBurstSize) { _dwBurstSize = dwBurstSize; }
|
||||
DWORD GetBurstSize() const { return _dwBurstSize; }
|
||||
|
||||
void SetThinkTime(DWORD dwThinkTime) { _dwThinkTime = dwThinkTime; }
|
||||
DWORD GetThinkTime() const { return _dwThinkTime; }
|
||||
|
||||
void SetEnableThinkTime(bool fEnable) { _fThinkTime = fEnable; }
|
||||
bool GetEnableThinkTime() const { return _fThinkTime; }
|
||||
|
||||
void SetThreadsPerFile(DWORD dwThreadsPerFile) { _dwThreadsPerFile = dwThreadsPerFile; }
|
||||
DWORD GetThreadsPerFile() const { return _dwThreadsPerFile; }
|
||||
|
||||
void SetCreateFile(bool fCreateFile) { _fCreateFile = fCreateFile; }
|
||||
bool GetCreateFile() const { return _fCreateFile; }
|
||||
|
||||
void SetFileSize(UINT64 ullFileSize) { _ullFileSize = ullFileSize; }
|
||||
UINT64 GetFileSize() const { return _ullFileSize; } // TODO: InBytes
|
||||
|
||||
void SetMaxFileSize(UINT64 ullMaxFileSize) { _ullMaxFileSize = ullMaxFileSize; }
|
||||
UINT64 GetMaxFileSize() const { return _ullMaxFileSize; }
|
||||
|
||||
void SetWriteRatio(UINT32 ulWriteRatio) { _ulWriteRatio = ulWriteRatio; }
|
||||
UINT32 GetWriteRatio() const { return _ulWriteRatio; }
|
||||
|
||||
void SetUseParallelAsyncIO(bool fParallelAsyncIO) { _fParallelAsyncIO = fParallelAsyncIO; }
|
||||
bool GetUseParallelAsyncIO() const { return _fParallelAsyncIO; }
|
||||
|
||||
void SetUseInterlockedSequential(bool fInterlockedSequential) { _fInterlockedSequential = fInterlockedSequential; }
|
||||
bool GetUseInterlockedSequential() const { return _fInterlockedSequential; }
|
||||
|
||||
void SetThreadStrideInBytes(UINT64 ullThreadStride) { _ullThreadStride = ullThreadStride; }
|
||||
UINT64 GetThreadStrideInBytes() const { return _ullThreadStride; }
|
||||
|
||||
void SetIOPriorityHint(PRIORITY_HINT _hint)
|
||||
{
|
||||
assert(_hint < MaximumIoPriorityHintType);
|
||||
_ioPriorityHint = _hint;
|
||||
}
|
||||
PRIORITY_HINT GetIOPriorityHint() const { return _ioPriorityHint; }
|
||||
|
||||
void SetPrecreated(bool fPrecreated) { _fPrecreated = fPrecreated; }
|
||||
bool GetPrecreated() const { return _fPrecreated; }
|
||||
|
||||
void SetThroughput(DWORD dwThroughputBytesPerMillisecond) { _dwThroughputBytesPerMillisecond = dwThroughputBytesPerMillisecond; }
|
||||
DWORD GetThroughputInBytesPerMillisecond() const { return _dwThroughputBytesPerMillisecond; }
|
||||
|
||||
string GetXml() const;
|
||||
|
||||
bool AllocateAndFillRandomDataWriteBuffer();
|
||||
void FreeRandomDataWriteBuffer();
|
||||
BYTE* GetRandomDataWriteBuffer();
|
||||
|
||||
private:
|
||||
string _sPath;
|
||||
DWORD _dwBlockSize;
|
||||
DWORD _dwRequestCount; // TODO: change the name to something more descriptive (OutstandingRequestCount?)
|
||||
|
||||
UINT64 _ullBlockAlignment;
|
||||
bool _fBlockAlignmentValid;
|
||||
bool _fUseRandomAccessPattern;
|
||||
|
||||
UINT64 _ullBaseFileOffset;
|
||||
bool _fParallelAsyncIO;
|
||||
bool _fInterlockedSequential;
|
||||
bool _fDisableOSCache;
|
||||
bool _fDisableAllCache;
|
||||
bool _fZeroWriteBuffers;
|
||||
DWORD _dwThreadsPerFile;
|
||||
UINT64 _ullThreadStride;
|
||||
|
||||
bool _fCreateFile;
|
||||
bool _fPrecreated; // used to track which files have been created before the first timespan and which have to be created later
|
||||
UINT64 _ullFileSize;
|
||||
UINT64 _ullMaxFileSize;
|
||||
UINT32 _ulWriteRatio;
|
||||
bool _fUseBurstSize; // TODO: "use" or "enable"?; since burst size must be specified with the think time, one variable should be sufficient
|
||||
DWORD _dwBurstSize; // number of IOs in a burst
|
||||
DWORD _dwThinkTime; // time to pause before issuing the next burst of IOs
|
||||
// TODO: could this be removed by using _dwThinkTime==0?
|
||||
bool _fThinkTime; //variable to decide whether to think between IOs (default is false)
|
||||
DWORD _dwThroughputBytesPerMillisecond; // set to 0 to disable throttling
|
||||
|
||||
bool _fSequentialScanHint; // open file with the FILE_FLAG_SEQUENTIAL_SCAN hint
|
||||
bool _fRandomAccessHint; // open file with the FILE_FLAG_RANDOM_ACCESS hint
|
||||
bool _fUseLargePages; // Use large pages for IO buffers
|
||||
|
||||
UINT64 _cbRandomDataWriteBuffer; // if > 0, then the write buffer should be filled with random data
|
||||
string _sRandomDataWriteBufferSourcePath; // file that should be used for filling the write buffer (if the path is not available, use a crypto provider)
|
||||
BYTE *_pRandomDataWriteBuffer; // a buffer used for write data when _cbWriteBuffer > 0; it's shared by all the threads working on this target
|
||||
|
||||
PRIORITY_HINT _ioPriorityHint;
|
||||
|
||||
bool _FillRandomDataWriteBuffer();
|
||||
|
||||
friend class UnitTests::ProfileUnitTests;
|
||||
friend class UnitTests::TargetUnitTests;
|
||||
};
|
||||
|
||||
class TimeSpan
|
||||
{
|
||||
public:
|
||||
TimeSpan() :
|
||||
_ulDuration(10),
|
||||
_ulWarmUp(5),
|
||||
_ulCoolDown(0),
|
||||
_ulRandSeed(0),
|
||||
_dwThreadCount(0),
|
||||
_fGroupAffinity(false),
|
||||
_fDisableAffinity(false),
|
||||
_fCompletionRoutines(false),
|
||||
_fMeasureLatency(false),
|
||||
_fCalculateIopsStdDev(false),
|
||||
_ulIoBucketDurationInMilliseconds(1000)
|
||||
{
|
||||
}
|
||||
|
||||
void AddAffinityAssignment(UINT32 ulAffinity)
|
||||
{
|
||||
_vAffinity.push_back(ulAffinity);
|
||||
}
|
||||
vector<UINT32> GetAffinityAssignments() const { return _vAffinity; }
|
||||
|
||||
void AddTarget(const Target& target)
|
||||
{
|
||||
_vTargets.push_back(Target(target));
|
||||
}
|
||||
vector<Target> GetTargets() const { return _vTargets; }
|
||||
|
||||
void SetDuration(UINT32 ulDuration) { _ulDuration = ulDuration; }
|
||||
UINT32 GetDuration() const { return _ulDuration; }
|
||||
|
||||
void SetWarmup(UINT32 ulWarmup) { _ulWarmUp = ulWarmup; }
|
||||
UINT32 GetWarmup() const { return _ulWarmUp; }
|
||||
|
||||
void SetCooldown(UINT32 ulCooldown) { _ulCoolDown = ulCooldown; }
|
||||
UINT32 GetCooldown() const { return _ulCoolDown; }
|
||||
|
||||
void SetRandSeed(UINT32 ulRandSeed) { _ulRandSeed = ulRandSeed; }
|
||||
UINT32 GetRandSeed() const { return _ulRandSeed; }
|
||||
|
||||
void SetThreadCount(DWORD dwThreadCount) { _dwThreadCount = dwThreadCount; }
|
||||
DWORD GetThreadCount() const { return _dwThreadCount; }
|
||||
|
||||
void SetGroupAffinity(bool fGroupAffinity) { _fGroupAffinity = fGroupAffinity; }
|
||||
bool GetGroupAffinity() const { return _fGroupAffinity; }
|
||||
|
||||
void SetDisableAffinity(bool fDisableAffinity) { _fDisableAffinity = fDisableAffinity; }
|
||||
bool GetDisableAffinity() const { return _fDisableAffinity; }
|
||||
|
||||
void SetCompletionRoutines(bool fCompletionRoutines) { _fCompletionRoutines = fCompletionRoutines; }
|
||||
bool GetCompletionRoutines() const { return _fCompletionRoutines; }
|
||||
|
||||
void SetMeasureLatency(bool fMeasureLatency) { _fMeasureLatency = fMeasureLatency; }
|
||||
bool GetMeasureLatency() const { return _fMeasureLatency; }
|
||||
|
||||
void SetCalculateIopsStdDev(bool fCalculateStdDev) { _fCalculateIopsStdDev = fCalculateStdDev; }
|
||||
bool GetCalculateIopsStdDev() const { return _fCalculateIopsStdDev; }
|
||||
|
||||
void SetIoBucketDurationInMilliseconds(UINT32 ulIoBucketDurationInMilliseconds) { _ulIoBucketDurationInMilliseconds = ulIoBucketDurationInMilliseconds; }
|
||||
UINT32 GetIoBucketDurationInMilliseconds() const { return _ulIoBucketDurationInMilliseconds; }
|
||||
|
||||
string GetXml() const;
|
||||
void MarkFilesAsPrecreated(const vector<string> vFiles);
|
||||
|
||||
private:
|
||||
vector<Target> _vTargets;
|
||||
UINT32 _ulDuration;
|
||||
UINT32 _ulWarmUp;
|
||||
UINT32 _ulCoolDown;
|
||||
UINT32 _ulRandSeed;
|
||||
DWORD _dwThreadCount;
|
||||
bool _fGroupAffinity;
|
||||
bool _fDisableAffinity;
|
||||
vector<UINT32> _vAffinity;
|
||||
bool _fCompletionRoutines;
|
||||
bool _fMeasureLatency;
|
||||
bool _fCalculateIopsStdDev;
|
||||
UINT32 _ulIoBucketDurationInMilliseconds;
|
||||
|
||||
friend class UnitTests::ProfileUnitTests;
|
||||
};
|
||||
|
||||
enum class ResultsFormat
|
||||
{
|
||||
Text,
|
||||
Xml
|
||||
};
|
||||
|
||||
enum class PrecreateFiles
|
||||
{
|
||||
None,
|
||||
UseMaxSize,
|
||||
OnlyFilesWithConstantSizes,
|
||||
OnlyFilesWithConstantOrZeroSizes
|
||||
};
|
||||
|
||||
class Profile
|
||||
{
|
||||
public:
|
||||
Profile() :
|
||||
_fVerbose(false),
|
||||
_dwProgress(0),
|
||||
_fEtwEnabled(false),
|
||||
_fEtwProcess(false),
|
||||
_fEtwThread(false),
|
||||
_fEtwImageLoad(false),
|
||||
_fEtwDiskIO(false),
|
||||
_fEtwMemoryPageFaults(false),
|
||||
_fEtwMemoryHardFaults(false),
|
||||
_fEtwNetwork(false),
|
||||
_fEtwRegistry(false),
|
||||
_fEtwUsePagedMemory(false),
|
||||
_fEtwUsePerfTimer(false),
|
||||
_fEtwUseSystemTimer(false),
|
||||
_fEtwUseCyclesCounter(false),
|
||||
_resultsFormat(ResultsFormat::Text),
|
||||
_precreateFiles(PrecreateFiles::None)
|
||||
{
|
||||
}
|
||||
|
||||
void AddTimeSpan(const TimeSpan& timeSpan)
|
||||
{
|
||||
_vTimeSpans.push_back(TimeSpan(timeSpan));
|
||||
}
|
||||
|
||||
const vector<TimeSpan>& GetTimeSpans() const { return _vTimeSpans; }
|
||||
|
||||
void SetVerbose(bool fVerbose) { _fVerbose = fVerbose; }
|
||||
bool GetVerbose() const { return _fVerbose; }
|
||||
|
||||
void SetProgress(DWORD dwProgress) { _dwProgress = dwProgress; }
|
||||
DWORD GetProgress() const { return _dwProgress; }
|
||||
|
||||
void SetCmdLine(string sCmdLine) { _sCmdLine = sCmdLine; }
|
||||
string GetCmdLine() const { return _sCmdLine; };
|
||||
|
||||
void SetResultsFormat(ResultsFormat format) { _resultsFormat = format; }
|
||||
ResultsFormat GetResultsFormat() const { return _resultsFormat; }
|
||||
|
||||
void SetPrecreateFiles(PrecreateFiles c) { _precreateFiles = c; }
|
||||
PrecreateFiles GetPrecreateFiles() const { return _precreateFiles; }
|
||||
|
||||
//ETW
|
||||
void SetEtwEnabled(bool fEtwEnabled) { _fEtwEnabled = fEtwEnabled; }
|
||||
void SetEtwProcess(bool fEtwProcess) { _fEtwProcess = fEtwProcess; }
|
||||
void SetEtwThread(bool fEtwThread) { _fEtwThread = fEtwThread; }
|
||||
void SetEtwImageLoad(bool fEtwImageLoad) { _fEtwImageLoad = fEtwImageLoad; }
|
||||
void SetEtwDiskIO(bool fEtwDiskIO) { _fEtwDiskIO = fEtwDiskIO; }
|
||||
void SetEtwMemoryPageFaults(bool fEtwMemoryPageFaults) { _fEtwMemoryPageFaults = fEtwMemoryPageFaults; }
|
||||
void SetEtwMemoryHardFaults(bool fEtwMemoryHardFaults) { _fEtwMemoryHardFaults = fEtwMemoryHardFaults; }
|
||||
void SetEtwNetwork(bool fEtwNetwork) { _fEtwNetwork = fEtwNetwork; }
|
||||
void SetEtwRegistry(bool fEtwRegistry) { _fEtwRegistry = fEtwRegistry; }
|
||||
void SetEtwUsePagedMemory(bool fEtwUsePagedMemory) { _fEtwUsePagedMemory = fEtwUsePagedMemory; }
|
||||
void SetEtwUsePerfTimer(bool fEtwUsePerfTimer) { _fEtwUsePerfTimer = fEtwUsePerfTimer; }
|
||||
void SetEtwUseSystemTimer(bool fEtwUseSystemTimer) { _fEtwUseSystemTimer = fEtwUseSystemTimer; }
|
||||
void SetEtwUseCyclesCounter(bool fEtwUseCyclesCounter) { _fEtwUseCyclesCounter = fEtwUseCyclesCounter; }
|
||||
|
||||
bool GetEtwEnabled() const { return _fEtwEnabled; }
|
||||
bool GetEtwProcess() const { return _fEtwProcess; }
|
||||
bool GetEtwThread() const { return _fEtwThread; }
|
||||
bool GetEtwImageLoad() const { return _fEtwImageLoad; }
|
||||
bool GetEtwDiskIO() const { return _fEtwDiskIO; }
|
||||
bool GetEtwMemoryPageFaults() const { return _fEtwMemoryPageFaults; }
|
||||
bool GetEtwMemoryHardFaults() const { return _fEtwMemoryHardFaults; }
|
||||
bool GetEtwNetwork() const { return _fEtwNetwork; }
|
||||
bool GetEtwRegistry() const { return _fEtwRegistry; }
|
||||
bool GetEtwUsePagedMemory() const { return _fEtwUsePagedMemory; }
|
||||
bool GetEtwUsePerfTimer() const { return _fEtwUsePerfTimer; }
|
||||
bool GetEtwUseSystemTimer() const { return _fEtwUseSystemTimer; }
|
||||
bool GetEtwUseCyclesCounter() const { return _fEtwUseCyclesCounter; }
|
||||
|
||||
string GetXml() const;
|
||||
bool Validate(bool fSingleSpec) const;
|
||||
void MarkFilesAsPrecreated(const vector<string> vFiles);
|
||||
|
||||
private:
|
||||
Profile(const Profile& T);
|
||||
|
||||
vector<TimeSpan>_vTimeSpans;
|
||||
bool _fVerbose;
|
||||
DWORD _dwProgress;
|
||||
string _sCmdLine;
|
||||
ResultsFormat _resultsFormat;
|
||||
PrecreateFiles _precreateFiles;
|
||||
|
||||
//ETW
|
||||
bool _fEtwEnabled;
|
||||
bool _fEtwProcess;
|
||||
bool _fEtwThread;
|
||||
bool _fEtwImageLoad;
|
||||
bool _fEtwDiskIO;
|
||||
bool _fEtwMemoryPageFaults;
|
||||
bool _fEtwMemoryHardFaults;
|
||||
bool _fEtwNetwork;
|
||||
bool _fEtwRegistry;
|
||||
bool _fEtwUsePagedMemory;
|
||||
bool _fEtwUsePerfTimer;
|
||||
bool _fEtwUseSystemTimer;
|
||||
bool _fEtwUseCyclesCounter;
|
||||
|
||||
friend class UnitTests::ProfileUnitTests;
|
||||
};
|
||||
|
||||
class ThreadParameters
|
||||
{
|
||||
public:
|
||||
ThreadParameters() :
|
||||
pProfile(nullptr),
|
||||
pTimeSpan(nullptr),
|
||||
pullSharedSequentialOffsets(nullptr),
|
||||
ulRandSeed(0),
|
||||
ulThreadNo(0),
|
||||
ulRelativeThreadNo(0)
|
||||
{
|
||||
}
|
||||
|
||||
const Profile *pProfile;
|
||||
const TimeSpan *pTimeSpan;
|
||||
|
||||
vector<Target> vTargets;
|
||||
vector<HANDLE> vhTargets;
|
||||
vector<UINT64> vullFileSizes;
|
||||
vector<BYTE *> vpDataBuffers;
|
||||
vector<OVERLAPPED> vOverlapped; // each target has RequestCount OVERLAPPED structures
|
||||
vector<size_t> vOverlappedIdToTargetId;
|
||||
vector<size_t> vFirstOverlappedIdForTargetId; //id of the first overlapped structure in the vOverlapped vector by target
|
||||
vector<IOOperation> vdwIoType; //as many as vOverlapped; used by the completion routines
|
||||
vector<UINT64> vIoStartTimes;
|
||||
|
||||
// For vanilla sequential access (-s):
|
||||
// Private per-thread offsets, incremented directly, indexed to number of targets
|
||||
vector<UINT64> vullPrivateSequentialOffsets;
|
||||
|
||||
// For interlocked sequential access (-si):
|
||||
// Pointers to offsets shared between threads, incremented with an interlocked op
|
||||
UINT64* pullSharedSequentialOffsets;
|
||||
|
||||
UINT32 ulRandSeed;
|
||||
UINT32 ulThreadNo;
|
||||
UINT32 ulRelativeThreadNo;
|
||||
|
||||
// accounting
|
||||
volatile bool *pfAccountingOn;
|
||||
PUINT64 pullStartTime;
|
||||
ThreadResults *pResults;
|
||||
|
||||
//group affinity
|
||||
WORD wGroupNum;
|
||||
DWORD dwProcNum;
|
||||
GROUP_AFFINITY GroupAffinity;
|
||||
|
||||
HANDLE hStartEvent;
|
||||
|
||||
// TODO: check how it's used
|
||||
HANDLE hEndEvent; //used only in case of completion routines (not for IO Completion Ports)
|
||||
|
||||
bool AllocateAndFillBufferForTarget(const Target& target);
|
||||
BYTE* GetReadBuffer(size_t iTarget, size_t iRequest);
|
||||
BYTE* GetWriteBuffer(size_t iTarget, size_t iRequest);
|
||||
DWORD GetTotalRequestCount() const;
|
||||
|
||||
private:
|
||||
ThreadParameters(const ThreadParameters& T);
|
||||
};
|
||||
|
||||
class IResultParser
|
||||
{
|
||||
public:
|
||||
virtual string ParseResults(Profile& profile, const SystemInformation& system, vector<Results> vResults) = 0;
|
||||
virtual int GetTotalScore() = 0;
|
||||
virtual double GetAverageLatency() = 0;
|
||||
};
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
|
||||
DISKSPD
|
||||
|
||||
Copyright(c) Microsoft Corporation
|
||||
All rights reserved.
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
|
||||
#pragma push_macro("min")
|
||||
#pragma push_macro("max")
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
template<typename T>
|
||||
class Histogram
|
||||
{
|
||||
private:
|
||||
|
||||
unsigned _samples;
|
||||
|
||||
#define USE_HASH_TABLE
|
||||
#ifdef USE_HASH_TABLE
|
||||
std::unordered_map<T,unsigned> _data;
|
||||
|
||||
std::map<T,unsigned> _GetSortedData() const
|
||||
{
|
||||
return std::map<T,unsigned>(_data.begin(), _data.end());
|
||||
}
|
||||
#else
|
||||
std::map<T,unsigned> _data;
|
||||
|
||||
std::map<T,unsigned> _GetSortedData() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
#endif
|
||||
public:
|
||||
|
||||
Histogram()
|
||||
: _samples(0)
|
||||
{}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
_data.clear();
|
||||
_samples = 0;
|
||||
}
|
||||
|
||||
void Add(T v)
|
||||
{
|
||||
_data[ v ]++;
|
||||
_samples++;
|
||||
}
|
||||
|
||||
void Merge(const Histogram<T> &other)
|
||||
{
|
||||
for (auto i : other._data)
|
||||
{
|
||||
_data[ i.first ] += i.second;
|
||||
}
|
||||
|
||||
_samples += other._samples;
|
||||
}
|
||||
|
||||
T GetMin() const
|
||||
{
|
||||
T min(std::numeric_limits<T>::max());
|
||||
|
||||
for (auto i : _data)
|
||||
{
|
||||
if (i.first < min)
|
||||
{
|
||||
min = i.first;
|
||||
}
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
T GetMax() const
|
||||
{
|
||||
T max(std::numeric_limits<T>::min());
|
||||
|
||||
for (auto i : _data)
|
||||
{
|
||||
if (i.first > max)
|
||||
{
|
||||
max = i.first;
|
||||
}
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
unsigned GetSampleSize() const
|
||||
{
|
||||
return _samples;
|
||||
}
|
||||
|
||||
T GetPercentile(double p) const
|
||||
{
|
||||
// ISSUE-REVIEW
|
||||
// What do the 0th and 100th percentile really mean?
|
||||
if ((p < 0) || (p > 1))
|
||||
{
|
||||
throw std::invalid_argument("Percentile must be >= 0 and <= 1");
|
||||
}
|
||||
|
||||
const double target = GetSampleSize() * p;
|
||||
|
||||
unsigned cur = 0;
|
||||
for (auto i : _GetSortedData())
|
||||
{
|
||||
cur += i.second;
|
||||
if (cur >= target)
|
||||
{
|
||||
return i.first;
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("Percentile is undefined");
|
||||
}
|
||||
|
||||
T GetPercentile(int p) const
|
||||
{
|
||||
return GetPercentile(static_cast<double>(p)/100);
|
||||
}
|
||||
|
||||
T GetMedian() const
|
||||
{
|
||||
return GetPercentile(0.5);
|
||||
}
|
||||
|
||||
double GetStdDev() const { return GetStandardDeviation(); }
|
||||
double GetAvg() const { return GetMean(); }
|
||||
|
||||
double GetMean() const
|
||||
{
|
||||
double sum(0);
|
||||
unsigned samples = GetSampleSize();
|
||||
|
||||
for (auto i : _data)
|
||||
{
|
||||
double bucket_val =
|
||||
static_cast<double>(i.first) * i.second / samples;
|
||||
|
||||
if (sum + bucket_val < 0)
|
||||
{
|
||||
throw std::overflow_error("while trying to accumulate sum");
|
||||
}
|
||||
|
||||
sum += bucket_val;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
double GetStandardDeviation() const
|
||||
{
|
||||
double mean(GetMean());
|
||||
double ssd(0);
|
||||
|
||||
for (auto i : _data)
|
||||
{
|
||||
double dev = static_cast<double>(i.first) - mean;
|
||||
double sqdev = dev*dev;
|
||||
ssd += i.second * sqdev;
|
||||
}
|
||||
|
||||
return sqrt(ssd / GetSampleSize());
|
||||
}
|
||||
|
||||
std::string GetHistogramCsv(const unsigned bins) const
|
||||
{
|
||||
return GetHistogramCsv(bins, GetMin(), GetMax());
|
||||
}
|
||||
|
||||
std::string GetHistogramCsv(const unsigned bins, const T LOW, const T HIGH) const
|
||||
{
|
||||
// ISSUE-REVIEW
|
||||
// Currently bins are defined as strictly less-than
|
||||
// their upper limit, with the exception of the last
|
||||
// bin. Otherwise where would I put the max value?
|
||||
const double binSize = static_cast<double>((HIGH - LOW) / bins);
|
||||
double limit = static_cast<double>(LOW);
|
||||
|
||||
std::ostringstream os;
|
||||
os.precision(std::numeric_limits<T>::digits10);
|
||||
|
||||
std::map<T,unsigned> sortedData = _GetSortedData();
|
||||
|
||||
auto pos = sortedData.begin();
|
||||
|
||||
unsigned cumulative = 0;
|
||||
|
||||
for (unsigned bin = 1; bin <= bins; ++bin)
|
||||
{
|
||||
unsigned count = 0;
|
||||
limit += binSize;
|
||||
|
||||
while (pos != sortedData.end() &&
|
||||
(pos->first < limit || bin == bins))
|
||||
{
|
||||
count += pos->second;
|
||||
++pos;
|
||||
}
|
||||
|
||||
cumulative += count;
|
||||
|
||||
os << limit << "," << count << "," << cumulative << std::endl;
|
||||
}
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string GetRawCsv() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os.precision(std::numeric_limits<T>::digits10);
|
||||
|
||||
for (auto i : _GetSortedData())
|
||||
{
|
||||
os << i.first << "," << i.second << std::endl;
|
||||
}
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string GetRaw() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
for (auto i : _GetSortedData())
|
||||
{
|
||||
os << i.second << " " << i.first << std::endl;
|
||||
}
|
||||
|
||||
return os.str();
|
||||
}
|
||||
};
|
||||
|
||||
#pragma pop_macro("min")
|
||||
#pragma pop_macro("max")
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
|
||||
DISKSPD
|
||||
|
||||
Copyright(c) Microsoft Corporation
|
||||
All rights reserved.
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include "IoBucketizer.h"
|
||||
|
||||
const unsigned __int64 INVALID_BUCKET_DURATION = 0;
|
||||
|
||||
IoBucketizer::IoBucketizer()
|
||||
: _bucketDuration(INVALID_BUCKET_DURATION),
|
||||
_validBuckets(0)
|
||||
{}
|
||||
|
||||
void IoBucketizer::Initialize(unsigned __int64 bucketDuration, size_t validBuckets)
|
||||
{
|
||||
if (_bucketDuration != INVALID_BUCKET_DURATION)
|
||||
{
|
||||
throw std::runtime_error("IoBucketizer has already been initialized");
|
||||
}
|
||||
if (bucketDuration == INVALID_BUCKET_DURATION)
|
||||
{
|
||||
throw std::invalid_argument("Bucket duration must be a positive integer");
|
||||
}
|
||||
|
||||
_bucketDuration = bucketDuration;
|
||||
_validBuckets = validBuckets;
|
||||
_vBuckets.reserve(_validBuckets);
|
||||
}
|
||||
|
||||
void IoBucketizer::Add(unsigned __int64 ioCompletionTime)
|
||||
{
|
||||
if (_bucketDuration == INVALID_BUCKET_DURATION)
|
||||
{
|
||||
throw std::runtime_error("IoBucketizer has not been initialized");
|
||||
}
|
||||
|
||||
size_t bucketNumber = static_cast<size_t>(ioCompletionTime / _bucketDuration);
|
||||
size_t currentSize = _vBuckets.size();
|
||||
if (currentSize < bucketNumber + 1)
|
||||
{
|
||||
_vBuckets.resize(bucketNumber + 1);
|
||||
// Zero the new entries. Note that size is 1-based and bucketNumber is 0-based.
|
||||
for (size_t i = currentSize; i <= bucketNumber; i++)
|
||||
{
|
||||
_vBuckets[i] = 0;
|
||||
}
|
||||
}
|
||||
_vBuckets[bucketNumber]++;
|
||||
}
|
||||
|
||||
size_t IoBucketizer::GetNumberOfValidBuckets() const
|
||||
{
|
||||
// Buckets beyond this may exist since Add is willing to extend the vector
|
||||
// beyond the expected number of valid buckets, but they are not comparable
|
||||
// buckets (straggling IOs over the timespan boundary).
|
||||
return (_vBuckets.size() > _validBuckets ? _validBuckets : _vBuckets.size());
|
||||
}
|
||||
|
||||
size_t IoBucketizer::GetNumberOfBuckets() const
|
||||
{
|
||||
return _vBuckets.size();
|
||||
}
|
||||
|
||||
unsigned int IoBucketizer::GetIoBucket(size_t bucketNumber) const
|
||||
{
|
||||
return _vBuckets[bucketNumber];
|
||||
}
|
||||
|
||||
double IoBucketizer::_GetMean() const
|
||||
{
|
||||
size_t numBuckets = GetNumberOfValidBuckets();
|
||||
double sum = 0;
|
||||
|
||||
for (size_t i = 0; i < numBuckets; i++)
|
||||
{
|
||||
sum += static_cast<double>(_vBuckets[i]) / numBuckets;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
double IoBucketizer::GetStandardDeviation() const
|
||||
{
|
||||
size_t numBuckets = GetNumberOfValidBuckets();
|
||||
|
||||
if(numBuckets == 0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double mean = _GetMean();
|
||||
double ssd = 0;
|
||||
|
||||
for (size_t i = 0; i < numBuckets; i++)
|
||||
{
|
||||
double dev = static_cast<double>(_vBuckets[i]) - mean;
|
||||
double sqdev = dev*dev;
|
||||
ssd += sqdev;
|
||||
}
|
||||
|
||||
return sqrt(ssd / numBuckets);
|
||||
}
|
||||
|
||||
void IoBucketizer::Merge(const IoBucketizer& other)
|
||||
{
|
||||
if(other._vBuckets.size() > _vBuckets.size())
|
||||
{
|
||||
_vBuckets.resize(other._vBuckets.size());
|
||||
}
|
||||
if (other._validBuckets > _validBuckets)
|
||||
{
|
||||
_validBuckets = other._validBuckets;
|
||||
}
|
||||
for(size_t i = 0; i < other._vBuckets.size(); i++)
|
||||
{
|
||||
_vBuckets[i] += other.GetIoBucket(i);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
|
||||
DISKSPD
|
||||
|
||||
Copyright(c) Microsoft Corporation
|
||||
All rights reserved.
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
class IoBucketizer
|
||||
{
|
||||
public:
|
||||
IoBucketizer();
|
||||
void Initialize(unsigned __int64 bucketDuration, size_t validBuckets);
|
||||
|
||||
size_t GetNumberOfValidBuckets() const;
|
||||
size_t GetNumberOfBuckets() const;
|
||||
unsigned int GetIoBucket(size_t bucketNumber) const;
|
||||
void Add(unsigned __int64 ioCompletionTime);
|
||||
double GetStandardDeviation() const;
|
||||
void Merge(const IoBucketizer& other);
|
||||
private:
|
||||
double _GetMean() const;
|
||||
|
||||
unsigned __int64 _bucketDuration;
|
||||
size_t _validBuckets;
|
||||
std::vector<unsigned int> _vBuckets;
|
||||
};
|
||||
Reference in New Issue
Block a user