1300 lines
31 KiB
C
1300 lines
31 KiB
C
|
|
||
|
/*++
|
||
|
|
||
|
Copyright (c) 1994 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
yapt.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module contains the code for the Yet Another Performance Tool utility.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Chuck Park (chuckp) 07-Oct-1994
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
|
||
|
#include <nt.h>
|
||
|
#include <ntrtl.h>
|
||
|
#include <nturtl.h>
|
||
|
#include <windows.h>
|
||
|
|
||
|
#include <windowsx.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <ctype.h>
|
||
|
#include <time.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
#include "yapt.h"
|
||
|
|
||
|
|
||
|
//
|
||
|
// Test proc. declarations
|
||
|
//
|
||
|
|
||
|
BOOLEAN
|
||
|
ReadSequential(
|
||
|
ULONG Iterations
|
||
|
);
|
||
|
|
||
|
BOOLEAN
|
||
|
WriteSequential(
|
||
|
ULONG Iterations
|
||
|
);
|
||
|
|
||
|
BOOLEAN
|
||
|
ReadRandom(
|
||
|
ULONG Iterations
|
||
|
);
|
||
|
|
||
|
BOOLEAN
|
||
|
WriteRandom(
|
||
|
ULONG Iterations
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Common util. functions
|
||
|
//
|
||
|
|
||
|
BOOLEAN
|
||
|
CreateTestFile(
|
||
|
);
|
||
|
|
||
|
ULONG
|
||
|
GetRandomOffset(
|
||
|
ULONG min,
|
||
|
ULONG max
|
||
|
);
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
GetSectorSize(
|
||
|
PDWORD SectorSize,
|
||
|
PCHAR DrvLetter
|
||
|
);
|
||
|
|
||
|
VOID
|
||
|
LogError(
|
||
|
PCHAR ErrString,
|
||
|
DWORD UniqueId,
|
||
|
DWORD ErrCode
|
||
|
);
|
||
|
|
||
|
BOOLEAN
|
||
|
ParseCmdLine(
|
||
|
INT Argc,
|
||
|
CHAR *Argv[]
|
||
|
);
|
||
|
|
||
|
BOOLEAN
|
||
|
ValidateOption(
|
||
|
CHAR Switch,
|
||
|
CHAR *Value
|
||
|
);
|
||
|
|
||
|
VOID
|
||
|
VerifyFileName(
|
||
|
IN CHAR *FileName
|
||
|
);
|
||
|
|
||
|
VOID
|
||
|
Usage(
|
||
|
VOID
|
||
|
);
|
||
|
|
||
|
VOID
|
||
|
__cdecl
|
||
|
Log(
|
||
|
ULONG LogLevel,
|
||
|
PCHAR String,
|
||
|
...
|
||
|
);
|
||
|
|
||
|
DWORD
|
||
|
YaptSetFileCompression(
|
||
|
IN HANDLE FileHandle,
|
||
|
IN USHORT CompressionFormat
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Default parameters
|
||
|
//
|
||
|
|
||
|
#define DEFAULT_BUFFER_SIZE 65536
|
||
|
#define DEFAULT_FILE_SIZE (20*1024*1024);
|
||
|
|
||
|
ULONG SectorSize = 512;
|
||
|
ULONG BufferSize = DEFAULT_BUFFER_SIZE;
|
||
|
ULONG FileSize = DEFAULT_FILE_SIZE;
|
||
|
UCHAR FileName[MAX_PATH] = "test.dat";
|
||
|
UCHAR Test = READ;
|
||
|
UCHAR Access = SEQ;
|
||
|
ULONG Verbose = 0;
|
||
|
ULONG Iterations = 10;
|
||
|
UCHAR ReuseFile = TRUE;
|
||
|
UCHAR Yes = FALSE;
|
||
|
BOOLEAN ReadOnly = FALSE;
|
||
|
|
||
|
|
||
|
INT
|
||
|
_cdecl main (
|
||
|
INT Argc,
|
||
|
CHAR *Argv[]
|
||
|
)
|
||
|
{
|
||
|
UCHAR testCase;
|
||
|
|
||
|
//
|
||
|
// Parse cmd line
|
||
|
//
|
||
|
|
||
|
if (!ParseCmdLine(Argc,Argv)) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Create the test file
|
||
|
//
|
||
|
|
||
|
if (!CreateTestFile()) {
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Call appropriate test routine.
|
||
|
//
|
||
|
|
||
|
testCase = (Test << 1) | Access;
|
||
|
switch (testCase) {
|
||
|
case 0:
|
||
|
|
||
|
if (!ReadSequential(Iterations)) {
|
||
|
return 3;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
|
||
|
//
|
||
|
// Read random
|
||
|
//
|
||
|
|
||
|
if (!ReadRandom(Iterations)) {
|
||
|
return 3;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
|
||
|
if (!WriteSequential(Iterations)) {
|
||
|
return 3;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
|
||
|
//
|
||
|
// Write random
|
||
|
//
|
||
|
|
||
|
if (!WriteRandom(Iterations)) {
|
||
|
return 3;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
printf("Invalid test case\n");
|
||
|
return 3;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
}
|
||
|
|
||
|
BOOLEAN
|
||
|
CreateTestFile(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
PUCHAR buffer;
|
||
|
HANDLE port = INVALID_HANDLE_VALUE;
|
||
|
HANDLE fileHandle;
|
||
|
OVERLAPPED overlapped,*overlapped2;
|
||
|
DWORD bytesTransferred,bytesTransferred2;
|
||
|
DWORD_PTR key;
|
||
|
ULONG numberWrites;
|
||
|
BOOL status;
|
||
|
ULONG i;
|
||
|
|
||
|
DWORD currentSize;
|
||
|
|
||
|
buffer = VirtualAlloc(NULL,
|
||
|
DEFAULT_BUFFER_SIZE,
|
||
|
MEM_COMMIT,
|
||
|
PAGE_READWRITE);
|
||
|
|
||
|
if ( !buffer ) {
|
||
|
printf("Error allocating buffer %x\n",GetLastError());
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if(ReuseFile) {
|
||
|
|
||
|
fileHandle = CreateFile(FileName,
|
||
|
ReadOnly ? GENERIC_READ :
|
||
|
(GENERIC_READ | GENERIC_WRITE),
|
||
|
0,
|
||
|
NULL,
|
||
|
OPEN_EXISTING,
|
||
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
|
||
|
NULL);
|
||
|
|
||
|
|
||
|
|
||
|
if(fileHandle != INVALID_HANDLE_VALUE) {
|
||
|
|
||
|
Log(1, "Using existing test file %s\n", FileName);
|
||
|
goto InitializeTestFile;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
Log(1, "Error %d opening existing test file\n", GetLastError());
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
DeleteFile(FileName);
|
||
|
|
||
|
}
|
||
|
|
||
|
fileHandle = CreateFile(FileName,
|
||
|
GENERIC_READ | GENERIC_WRITE,
|
||
|
0,
|
||
|
NULL,
|
||
|
OPEN_ALWAYS,
|
||
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
|
||
|
NULL);
|
||
|
|
||
|
InitializeTestFile:
|
||
|
|
||
|
if ( fileHandle == INVALID_HANDLE_VALUE ) {
|
||
|
printf("Error opening file %s %d\n",FileName,GetLastError());
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
currentSize = GetFileSize(fileHandle, NULL);
|
||
|
|
||
|
if(currentSize >= FileSize) {
|
||
|
|
||
|
goto EndCreateTestFile;
|
||
|
|
||
|
} else if(ReadOnly) {
|
||
|
|
||
|
FileSize = currentSize;
|
||
|
goto EndCreateTestFile;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Uncompress the file - compressed files will always do cached I/O even
|
||
|
// if you tell them not to. If this fails assume it's because the
|
||
|
// underlying file system doesn't handle compression.
|
||
|
//
|
||
|
|
||
|
Log(1, "Trying to decompress %s\n", FileName);
|
||
|
|
||
|
YaptSetFileCompression(fileHandle, COMPRESSION_FORMAT_NONE);
|
||
|
|
||
|
port = CreateIoCompletionPort(fileHandle,
|
||
|
NULL,
|
||
|
(DWORD_PTR)fileHandle,
|
||
|
0);
|
||
|
if ( !port ) {
|
||
|
printf("Error creating completion port %d\n",GetLastError());
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Log(1,"Creating test file %s",FileName);
|
||
|
|
||
|
memset(&overlapped, 0, sizeof(overlapped));
|
||
|
|
||
|
numberWrites = FileSize / BufferSize;
|
||
|
|
||
|
for (i = 0; i < numberWrites; i++) {
|
||
|
|
||
|
retryWrite:
|
||
|
status = WriteFile(fileHandle,
|
||
|
buffer,
|
||
|
BufferSize,
|
||
|
&bytesTransferred,
|
||
|
&overlapped);
|
||
|
|
||
|
if ( !status && GetLastError() != ERROR_IO_PENDING ) {
|
||
|
if (GetLastError() == ERROR_INVALID_USER_BUFFER ||
|
||
|
GetLastError() == ERROR_NOT_ENOUGH_QUOTA ||
|
||
|
GetLastError() == ERROR_WORKING_SET_QUOTA ||
|
||
|
GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
|
||
|
|
||
|
goto retryWrite;
|
||
|
}
|
||
|
printf("Error creating test file %x\n",GetLastError());
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Log(2,".");
|
||
|
overlapped.Offset += BufferSize;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < numberWrites; i++ ) {
|
||
|
status = GetQueuedCompletionStatus(port,
|
||
|
&bytesTransferred2,
|
||
|
&key,
|
||
|
&overlapped2,
|
||
|
(DWORD)-1);
|
||
|
if ( !status ) {
|
||
|
printf("Error on completion during test file create %x\n",GetLastError());
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Log(1,".. Complete.\n\n");
|
||
|
|
||
|
EndCreateTestFile:
|
||
|
|
||
|
if(port != INVALID_HANDLE_VALUE) CloseHandle(port);
|
||
|
if(fileHandle != INVALID_HANDLE_VALUE) CloseHandle(fileHandle);
|
||
|
|
||
|
VirtualFree(buffer,
|
||
|
DEFAULT_BUFFER_SIZE,
|
||
|
MEM_DECOMMIT);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
ReadSequential(
|
||
|
ULONG Iterations
|
||
|
)
|
||
|
{
|
||
|
ULONG j,
|
||
|
outstandingRequests,
|
||
|
remaining = Iterations;
|
||
|
ULONG start,end;
|
||
|
DOUBLE testTime,thruPut = 0.0,seconds = 0;
|
||
|
HANDLE file,
|
||
|
port;
|
||
|
OVERLAPPED overlapped,
|
||
|
*overlapped2;
|
||
|
DWORD bytesRead,
|
||
|
bytesRead2,
|
||
|
errCode,
|
||
|
version;
|
||
|
DWORD_PTR completionKey;
|
||
|
BOOL status;
|
||
|
PUCHAR buffer;
|
||
|
|
||
|
|
||
|
printf("Starting Sequential Reads of %d Meg file, using %d K I/O size\n", FileSize / 1024*1024, BufferSize / 1024);
|
||
|
|
||
|
version = GetVersion() >> 16;
|
||
|
|
||
|
buffer = VirtualAlloc(NULL,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_COMMIT | MEM_RESERVE,
|
||
|
PAGE_READWRITE);
|
||
|
|
||
|
(ULONG_PTR)buffer &= ~((ULONG_PTR)SectorSize - 1);
|
||
|
|
||
|
if ( !buffer ) {
|
||
|
Log(0,"Error allocating buffer: %x\n",GetLastError());
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
file = CreateFile(FileName,
|
||
|
ReadOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE),
|
||
|
0,
|
||
|
NULL,
|
||
|
OPEN_EXISTING,
|
||
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
|
||
|
NULL );
|
||
|
|
||
|
if ( file == INVALID_HANDLE_VALUE ) {
|
||
|
Log(0,"Error opening Target file: %x\n",GetLastError());
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
port = CreateIoCompletionPort(file,
|
||
|
NULL,
|
||
|
(DWORD_PTR)file,
|
||
|
0);
|
||
|
if ( !port ) {
|
||
|
Log(0,"Error creating completion port: %x\n",GetLastError());
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
while (remaining--) {
|
||
|
|
||
|
start = GetTickCount();
|
||
|
|
||
|
memset(&overlapped,0,sizeof(overlapped));
|
||
|
|
||
|
outstandingRequests = 0;
|
||
|
|
||
|
for (j = 0; j < FileSize / BufferSize; j++) {
|
||
|
do {
|
||
|
|
||
|
status = ReadFile(file,
|
||
|
buffer,
|
||
|
BufferSize,
|
||
|
&bytesRead,
|
||
|
&overlapped);
|
||
|
|
||
|
errCode = GetLastError();
|
||
|
|
||
|
if (!status) {
|
||
|
if (errCode == ERROR_IO_PENDING) {
|
||
|
break;
|
||
|
} else if (errCode == ERROR_NOT_ENOUGH_QUOTA ||
|
||
|
errCode == ERROR_INVALID_USER_BUFFER ||
|
||
|
errCode == ERROR_WORKING_SET_QUOTA ||
|
||
|
errCode == ERROR_NOT_ENOUGH_MEMORY) {
|
||
|
//
|
||
|
// Allow this to retry.
|
||
|
//
|
||
|
|
||
|
} else {
|
||
|
Log(0,"Error in ReadFile: %x\n",errCode);
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
} while (!status);
|
||
|
|
||
|
outstandingRequests++;
|
||
|
overlapped.Offset += BufferSize;
|
||
|
}
|
||
|
|
||
|
for (j = 0; j < outstandingRequests; j++) {
|
||
|
|
||
|
status = GetQueuedCompletionStatus(port,
|
||
|
&bytesRead2,
|
||
|
&completionKey,
|
||
|
&overlapped2,
|
||
|
(DWORD)-1);
|
||
|
|
||
|
if (!status) {
|
||
|
Log(0,"GetQueuedCompletionStatus error: %x\n",GetLastError());
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
end = GetTickCount();
|
||
|
testTime = end - start;
|
||
|
testTime /= 1000.0;
|
||
|
seconds += testTime;
|
||
|
Log(1,"Iteration %2d -> %4.3f MB/S\n",Iterations - remaining,(FileSize / testTime)/(1024*1024));
|
||
|
}
|
||
|
|
||
|
CloseHandle(port);
|
||
|
CloseHandle(file);
|
||
|
|
||
|
thruPut = FileSize * Iterations / seconds;
|
||
|
printf("\nAverage Throughput %4.3f MB/S\n",thruPut/(1024*1024));
|
||
|
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
ReadRandom(
|
||
|
ULONG Iterations
|
||
|
)
|
||
|
{
|
||
|
ULONG j,
|
||
|
outstandingRequests,
|
||
|
remaining = Iterations;
|
||
|
ULONG start,end;
|
||
|
DOUBLE testTime,thruPut = 0.0,seconds = 0;
|
||
|
HANDLE file,
|
||
|
port;
|
||
|
OVERLAPPED overlapped,
|
||
|
*overlapped2;
|
||
|
DWORD bytesRead,
|
||
|
bytesRead2,
|
||
|
errCode,
|
||
|
version;
|
||
|
DWORD_PTR completionKey;
|
||
|
BOOL status;
|
||
|
PUCHAR buffer;
|
||
|
|
||
|
ULONG min = FileSize,max = 0;
|
||
|
|
||
|
|
||
|
printf("Starting Random Reads of %d Meg file, using %d K I/O size\n", FileSize / 1024*1024, BufferSize / 1024);
|
||
|
|
||
|
version = GetVersion() >> 16;
|
||
|
srand((unsigned)time(NULL));
|
||
|
|
||
|
buffer = VirtualAlloc(NULL,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_COMMIT | MEM_RESERVE,
|
||
|
PAGE_READWRITE);
|
||
|
|
||
|
(ULONG_PTR)buffer &= ~((ULONG_PTR)SectorSize - 1);
|
||
|
|
||
|
if ( !buffer ) {
|
||
|
Log(0,"Error allocating buffer: %x\n",GetLastError());
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
file = CreateFile(FileName,
|
||
|
ReadOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE),
|
||
|
0,
|
||
|
NULL,
|
||
|
OPEN_EXISTING,
|
||
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
|
||
|
NULL );
|
||
|
|
||
|
if ( file == INVALID_HANDLE_VALUE ) {
|
||
|
Log(0,"Error opening Target file: %x\n",GetLastError());
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
port = CreateIoCompletionPort(file,
|
||
|
NULL,
|
||
|
(DWORD_PTR)file,
|
||
|
0);
|
||
|
if ( !port ) {
|
||
|
Log(0,"Error creating completion port: %x\n",GetLastError());
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
while (remaining--) {
|
||
|
|
||
|
start = GetTickCount();
|
||
|
|
||
|
memset(&overlapped,0,sizeof(overlapped));
|
||
|
|
||
|
outstandingRequests = 0;
|
||
|
|
||
|
for (j = 0; j < FileSize / BufferSize; j++) {
|
||
|
do {
|
||
|
|
||
|
status = ReadFile(file,
|
||
|
buffer,
|
||
|
BufferSize,
|
||
|
&bytesRead,
|
||
|
&overlapped);
|
||
|
|
||
|
errCode = GetLastError();
|
||
|
|
||
|
if (!status) {
|
||
|
if (errCode == ERROR_IO_PENDING) {
|
||
|
break;
|
||
|
} else if (errCode == ERROR_NOT_ENOUGH_QUOTA ||
|
||
|
errCode == ERROR_INVALID_USER_BUFFER ||
|
||
|
errCode == ERROR_WORKING_SET_QUOTA ||
|
||
|
errCode == ERROR_NOT_ENOUGH_MEMORY) {
|
||
|
//
|
||
|
// Allow this to retry.
|
||
|
//
|
||
|
|
||
|
} else {
|
||
|
Log(0,"Error in ReadFile: %x\n",errCode);
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
} while (!status);
|
||
|
|
||
|
outstandingRequests++;
|
||
|
overlapped.Offset = GetRandomOffset(0,FileSize - BufferSize);
|
||
|
if (overlapped.Offset > max) {
|
||
|
max = overlapped.Offset;
|
||
|
}
|
||
|
if (overlapped.Offset < min) {
|
||
|
min = overlapped.Offset;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (j = 0; j < outstandingRequests; j++) {
|
||
|
|
||
|
status = GetQueuedCompletionStatus(port,
|
||
|
&bytesRead2,
|
||
|
&completionKey,
|
||
|
&overlapped2,
|
||
|
(DWORD)-1);
|
||
|
|
||
|
if (!status) {
|
||
|
Log(0,"GetQueuedCompletionStatus error: %x\n",GetLastError());
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
end = GetTickCount();
|
||
|
testTime = end - start;
|
||
|
testTime /= 1000.0;
|
||
|
seconds += testTime;
|
||
|
Log(1,"Iteration %2d -> %4.3f MB/S\n",Iterations - remaining,(FileSize / testTime)/(1024*1024));
|
||
|
|
||
|
}
|
||
|
|
||
|
CloseHandle(port);
|
||
|
CloseHandle(file);
|
||
|
|
||
|
thruPut = FileSize * Iterations / seconds;
|
||
|
printf("\nAverage Throughput %4.3f MB/S\n",thruPut/(1024*1024));
|
||
|
Log(2,"Min = %Lu, Max = %Lu, FileSize = %Lu\n",min,max,FileSize);
|
||
|
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
WriteSequential(
|
||
|
ULONG Iterations
|
||
|
)
|
||
|
{
|
||
|
ULONG j,
|
||
|
outstandingRequests,
|
||
|
remaining = Iterations;
|
||
|
ULONG start,end;
|
||
|
DOUBLE testTime,thruPut = 0.0,seconds = 0;
|
||
|
HANDLE file,
|
||
|
port;
|
||
|
OVERLAPPED overlapped,
|
||
|
*overlapped2;
|
||
|
DWORD bytesWritten,
|
||
|
bytesWritten2,
|
||
|
errCode,
|
||
|
version;
|
||
|
DWORD_PTR completionKey;
|
||
|
BOOL status;
|
||
|
PUCHAR buffer;
|
||
|
|
||
|
|
||
|
if(ReadOnly) {
|
||
|
printf("Can't run write tests on a read only file\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
printf("Starting Sequential Writes of %d Meg file, using %d K I/O size\n", FileSize / 1024*1024, BufferSize / 1024);
|
||
|
|
||
|
version = GetVersion() >> 16;
|
||
|
|
||
|
buffer = VirtualAlloc(NULL,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_COMMIT | MEM_RESERVE,
|
||
|
PAGE_READWRITE);
|
||
|
|
||
|
(ULONG_PTR)buffer &= ~((ULONG_PTR)SectorSize - 1);
|
||
|
|
||
|
if ( !buffer ) {
|
||
|
Log(0,"Error allocating buffer: %x\n",GetLastError());
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
file = CreateFile(FileName,
|
||
|
GENERIC_READ | GENERIC_WRITE,
|
||
|
0,
|
||
|
NULL,
|
||
|
OPEN_EXISTING,
|
||
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
|
||
|
NULL );
|
||
|
|
||
|
if ( file == INVALID_HANDLE_VALUE ) {
|
||
|
Log(0,"Error opening Target file: %x\n",GetLastError());
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
port = CreateIoCompletionPort(file,
|
||
|
NULL,
|
||
|
(DWORD_PTR)file,
|
||
|
0);
|
||
|
if ( !port ) {
|
||
|
Log(0,"Error creating completion port: %x\n",GetLastError());
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
while (remaining--) {
|
||
|
|
||
|
start = GetTickCount();
|
||
|
|
||
|
memset(&overlapped,0,sizeof(overlapped));
|
||
|
|
||
|
outstandingRequests = 0;
|
||
|
|
||
|
for (j = 0; j < FileSize / BufferSize; j++) {
|
||
|
do {
|
||
|
|
||
|
status = WriteFile(file,
|
||
|
buffer,
|
||
|
BufferSize,
|
||
|
&bytesWritten,
|
||
|
&overlapped);
|
||
|
|
||
|
errCode = GetLastError();
|
||
|
|
||
|
if (!status) {
|
||
|
if (errCode == ERROR_IO_PENDING) {
|
||
|
break;
|
||
|
} else if (errCode == ERROR_NOT_ENOUGH_QUOTA ||
|
||
|
errCode == ERROR_INVALID_USER_BUFFER ||
|
||
|
errCode == ERROR_WORKING_SET_QUOTA ||
|
||
|
errCode == ERROR_NOT_ENOUGH_MEMORY) {
|
||
|
//
|
||
|
// Allow this to retry.
|
||
|
//
|
||
|
|
||
|
} else {
|
||
|
Log(0,"Error in WriteFile: %x\n",errCode);
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
} while (!status);
|
||
|
|
||
|
outstandingRequests++;
|
||
|
overlapped.Offset += BufferSize;
|
||
|
}
|
||
|
|
||
|
for (j = 0; j < outstandingRequests; j++) {
|
||
|
|
||
|
status = GetQueuedCompletionStatus(port,
|
||
|
&bytesWritten2,
|
||
|
&completionKey,
|
||
|
&overlapped2,
|
||
|
(DWORD)-1);
|
||
|
|
||
|
if (!status) {
|
||
|
Log(0,"GetQueuedCompletionStatus error: %x\n",GetLastError());
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
end = GetTickCount();
|
||
|
testTime = end - start;
|
||
|
testTime /= 1000.0;
|
||
|
seconds += testTime;
|
||
|
Log(1,"Iteration %2d -> %4.3f MB/S\n",Iterations - remaining,(FileSize / testTime)/(1024*1024));
|
||
|
|
||
|
}
|
||
|
|
||
|
CloseHandle(port);
|
||
|
CloseHandle(file);
|
||
|
|
||
|
thruPut = FileSize * Iterations / seconds;
|
||
|
printf("\nAverage Throughput %4.3f MB/S\n",thruPut/(1024*1024));
|
||
|
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
WriteRandom(
|
||
|
ULONG Iterations
|
||
|
)
|
||
|
{
|
||
|
ULONG j,
|
||
|
outstandingRequests,
|
||
|
remaining = Iterations;
|
||
|
ULONG start,end;
|
||
|
DOUBLE testTime,thruPut = 0.0,seconds = 0;
|
||
|
HANDLE file,
|
||
|
port;
|
||
|
OVERLAPPED overlapped,
|
||
|
*overlapped2;
|
||
|
DWORD bytesWritten,
|
||
|
bytesWritten2,
|
||
|
errCode,
|
||
|
version;
|
||
|
DWORD_PTR completionKey;
|
||
|
BOOL status;
|
||
|
PUCHAR buffer;
|
||
|
ULONG min = FileSize,max = 0;
|
||
|
|
||
|
if(ReadOnly) {
|
||
|
printf("Can't run write tests on a read only file\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
printf("Starting Random Writes of %d Meg file, using %d K I/O size\n", FileSize / 1024*1024, BufferSize / 1024);
|
||
|
|
||
|
version = GetVersion() >> 16;
|
||
|
|
||
|
buffer = VirtualAlloc(NULL,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_COMMIT | MEM_RESERVE,
|
||
|
PAGE_READWRITE);
|
||
|
|
||
|
(ULONG_PTR)buffer &= ~((ULONG_PTR)SectorSize - 1);
|
||
|
|
||
|
if ( !buffer ) {
|
||
|
Log(0,"Error allocating buffer: %x\n",GetLastError());
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
file = CreateFile(FileName,
|
||
|
GENERIC_READ | GENERIC_WRITE,
|
||
|
0,
|
||
|
NULL,
|
||
|
OPEN_EXISTING,
|
||
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
|
||
|
NULL );
|
||
|
|
||
|
if ( file == INVALID_HANDLE_VALUE ) {
|
||
|
Log(0,"Error opening Target file: %x\n",GetLastError());
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
port = CreateIoCompletionPort(file,
|
||
|
NULL,
|
||
|
(DWORD_PTR)file,
|
||
|
0);
|
||
|
if ( !port ) {
|
||
|
Log(0,"Error creating completion port: %x\n",GetLastError());
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
while (remaining--) {
|
||
|
|
||
|
start = GetTickCount();
|
||
|
|
||
|
memset(&overlapped,0,sizeof(overlapped));
|
||
|
|
||
|
outstandingRequests = 0;
|
||
|
|
||
|
for (j = 0; j < FileSize / BufferSize; j++) {
|
||
|
do {
|
||
|
|
||
|
status = WriteFile(file,
|
||
|
buffer,
|
||
|
BufferSize,
|
||
|
&bytesWritten,
|
||
|
&overlapped);
|
||
|
|
||
|
errCode = GetLastError();
|
||
|
|
||
|
if (!status) {
|
||
|
if (errCode == ERROR_IO_PENDING) {
|
||
|
break;
|
||
|
} else if (errCode == ERROR_NOT_ENOUGH_QUOTA ||
|
||
|
errCode == ERROR_INVALID_USER_BUFFER ||
|
||
|
errCode == ERROR_WORKING_SET_QUOTA ||
|
||
|
errCode == ERROR_NOT_ENOUGH_MEMORY) {
|
||
|
//
|
||
|
// Allow this to retry.
|
||
|
//
|
||
|
|
||
|
} else {
|
||
|
Log(0,"Error in WriteFile: %x\n",errCode);
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
} while (!status);
|
||
|
|
||
|
outstandingRequests++;
|
||
|
overlapped.Offset = GetRandomOffset(0,FileSize - BufferSize);
|
||
|
if (overlapped.Offset > max) {
|
||
|
max = overlapped.Offset;
|
||
|
}
|
||
|
if (overlapped.Offset < min) {
|
||
|
min = overlapped.Offset;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (j = 0; j < outstandingRequests; j++) {
|
||
|
|
||
|
status = GetQueuedCompletionStatus(port,
|
||
|
&bytesWritten2,
|
||
|
&completionKey,
|
||
|
&overlapped2,
|
||
|
(DWORD)-1);
|
||
|
|
||
|
if (!status) {
|
||
|
Log(0,"GetQueuedCompletionStatus error: %x\n",GetLastError());
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
end = GetTickCount();
|
||
|
testTime = end - start;
|
||
|
testTime /= 1000.0;
|
||
|
seconds += testTime;
|
||
|
Log(1,"Iteration %2d -> %4.3f MB/S\n",Iterations - remaining,(FileSize / testTime)/(1024*1024));
|
||
|
|
||
|
}
|
||
|
|
||
|
CloseHandle(port);
|
||
|
CloseHandle(file);
|
||
|
|
||
|
thruPut = FileSize * Iterations / seconds;
|
||
|
printf("\nAverage Throughput %4.3f MB/S\n",thruPut/(1024*1024));
|
||
|
Log(2,"Min = %Lu, Max = %Lu, FileSize = %Lu\n",min,max,FileSize);
|
||
|
|
||
|
VirtualFree(buffer,
|
||
|
BufferSize + SectorSize - 1,
|
||
|
MEM_DECOMMIT);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
ULONG
|
||
|
GetRandomOffset(
|
||
|
ULONG min,
|
||
|
ULONG max
|
||
|
)
|
||
|
{
|
||
|
|
||
|
INT base = rand();
|
||
|
ULONG retval = ((max - min) / RAND_MAX) * base;
|
||
|
retval += SectorSize - 1;
|
||
|
retval &= ~(SectorSize - 1);
|
||
|
if (retval < min) {
|
||
|
return min;
|
||
|
} else if (retval > max ){
|
||
|
return max & ~(SectorSize - 1);
|
||
|
} else{
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
BOOLEAN
|
||
|
ParseCmdLine(
|
||
|
INT Argc,
|
||
|
CHAR *Argv[]
|
||
|
)
|
||
|
{
|
||
|
|
||
|
INT i;
|
||
|
CHAR *switches = " ,-";
|
||
|
CHAR swtch,*value,*str;
|
||
|
BOOLEAN gotSwitch = FALSE;
|
||
|
|
||
|
|
||
|
if (Argc <= 1) {
|
||
|
|
||
|
//
|
||
|
// Using defaults
|
||
|
//
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
for (i = 1; i < Argc; i++) {
|
||
|
str = Argv[i];
|
||
|
value = strtok(str, switches);
|
||
|
if (gotSwitch) {
|
||
|
if (!ValidateOption(swtch,value)) {
|
||
|
Usage();
|
||
|
return FALSE;
|
||
|
} else {
|
||
|
gotSwitch = FALSE;
|
||
|
}
|
||
|
} else {
|
||
|
gotSwitch = TRUE;
|
||
|
swtch = value[0];
|
||
|
if (value[1] || swtch == '?') {
|
||
|
Usage();
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (gotSwitch) {
|
||
|
|
||
|
Usage();
|
||
|
return FALSE;
|
||
|
} else {
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
BOOLEAN
|
||
|
ValidateOption(
|
||
|
CHAR Switch,
|
||
|
CHAR *Value
|
||
|
)
|
||
|
{
|
||
|
Switch = (CHAR)toupper(Switch);
|
||
|
switch (Switch) {
|
||
|
|
||
|
case 'F':
|
||
|
|
||
|
VerifyFileName(Value);
|
||
|
strcpy(FileName,Value);
|
||
|
break;
|
||
|
|
||
|
case 'T':
|
||
|
if (_stricmp(Value,"READ")==0) {
|
||
|
Test = READ;
|
||
|
} else if (_stricmp(Value,"WRITE")==0) {
|
||
|
Test = WRITE;
|
||
|
} else {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case 'A':
|
||
|
|
||
|
if (_strnicmp(Value,"SEQ",3)==0) {
|
||
|
Access = SEQ;
|
||
|
} else if (_strnicmp(Value,"RAND",4)==0) {
|
||
|
Access = RAND;
|
||
|
} else {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case 'B':
|
||
|
BufferSize = atol(Value);
|
||
|
|
||
|
//
|
||
|
// TODO:Adjust buffersize to multiple of a sector and #of K.
|
||
|
//
|
||
|
|
||
|
BufferSize *= 1024;
|
||
|
break;
|
||
|
|
||
|
case 'S':
|
||
|
|
||
|
FileSize = atol(Value);
|
||
|
|
||
|
//
|
||
|
// TODO: Adjust filesize to multiple of buffersize and #of Meg.
|
||
|
//
|
||
|
|
||
|
FileSize *= 1024*1024;
|
||
|
break;
|
||
|
|
||
|
case 'V':
|
||
|
|
||
|
Verbose = atol(Value);
|
||
|
break;
|
||
|
|
||
|
case 'I':
|
||
|
|
||
|
Iterations = atol(Value);
|
||
|
break;
|
||
|
|
||
|
case 'R':
|
||
|
|
||
|
if(tolower(Value[0]) == 'y') {
|
||
|
ReuseFile = TRUE;
|
||
|
} else {
|
||
|
ReuseFile = FALSE;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case 'O':
|
||
|
|
||
|
if(tolower(Value[0]) == 'y') {
|
||
|
ReadOnly = TRUE;
|
||
|
} else {
|
||
|
ReadOnly = FALSE;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
Usage(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
|
||
|
fprintf(stderr,"usage: YAPT (Yet another performance tool)\n"
|
||
|
" -f [filename]\n"
|
||
|
" -t [Test:Read,Write]\n"
|
||
|
" -a [Access Mode:Seq or Random]\n"
|
||
|
" -b [buffer size in KB]\n"
|
||
|
" -s [filesize]\n"
|
||
|
" -i [Iterations]\n"
|
||
|
" -v [Verbosity: 0-2]\n"
|
||
|
" -r [Reuse test file: yes, no]\n"
|
||
|
" -o [Read Only: yes, no]\n"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
__cdecl
|
||
|
Log(
|
||
|
ULONG LogLevel,
|
||
|
PCHAR String,
|
||
|
...
|
||
|
)
|
||
|
{
|
||
|
|
||
|
CHAR Buffer[256];
|
||
|
|
||
|
va_list argp;
|
||
|
va_start(argp, String);
|
||
|
|
||
|
if (LogLevel <= Verbose) {
|
||
|
vsprintf(Buffer, String, argp);
|
||
|
printf("%s",Buffer);
|
||
|
}
|
||
|
|
||
|
va_end(argp);
|
||
|
}
|
||
|
|
||
|
DWORD
|
||
|
YaptSetFileCompression(
|
||
|
IN HANDLE FileHandle,
|
||
|
IN USHORT CompressionFormat
|
||
|
)
|
||
|
|
||
|
{
|
||
|
DWORD bytesReturned;
|
||
|
|
||
|
assert(FileHandle != INVALID_HANDLE_VALUE);
|
||
|
|
||
|
if(!DeviceIoControl(
|
||
|
FileHandle,
|
||
|
FSCTL_SET_COMPRESSION,
|
||
|
&CompressionFormat,
|
||
|
sizeof(CompressionFormat),
|
||
|
NULL,
|
||
|
0,
|
||
|
&bytesReturned,
|
||
|
NULL)) {
|
||
|
|
||
|
}
|
||
|
|
||
|
return GetLastError();
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
VerifyFileName(
|
||
|
IN CHAR *FileName
|
||
|
)
|
||
|
|
||
|
{
|
||
|
CHAR input[32];
|
||
|
|
||
|
if(Yes) return;
|
||
|
|
||
|
if(((FileName[0] == '\\') || (FileName[0] == '/')) &&
|
||
|
((FileName[1] == '\\') || (FileName[1] == '/')) &&
|
||
|
((FileName[2] == '.') || (FileName[2] == '?')) &&
|
||
|
((FileName[3] == '\\') || (FileName[3] == '/'))) {
|
||
|
|
||
|
printf("\n\t%s looks like a raw device\n"
|
||
|
"\tdo you want to continue? ", FileName);
|
||
|
|
||
|
fflush(stdout);
|
||
|
|
||
|
if(fgets(input, 32, stdin) == NULL) {
|
||
|
|
||
|
printf("\nQuitting\n");
|
||
|
exit(-1);
|
||
|
}
|
||
|
|
||
|
if(tolower(input[0]) != 'y') {
|
||
|
|
||
|
printf("\nNegative response - quitting\n");
|
||
|
exit(-1);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|