Project

General

Profile

Download (9.24 KB) Statistics
| Branch: | Tag: | Revision:
a5734307 David Sorber
#include <algorithm>
a0d1d164 David Sorber
#include <chrono>
058717b0 David Sorber
#include <cstdlib>
f12339cf david.sorber
#include <filesystem>
a0d1d164 David Sorber
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>

058717b0 David Sorber
#include <sys/utsname.h>

a5734307 David Sorber
#include <boost/program_options.hpp>

a0d1d164 David Sorber
#include "AIO_ExecDriver.h"
#include "ExecDriver.h"
864b608c David Sorber
#include "URingExecDriver.h"
a0d1d164 David Sorber
#include "Utilities/BufferAllocator.h"
#include "Utilities/FileUtils.h"

a5734307 David Sorber
// Alias the namespace for readability
namespace bpo = boost::program_options;
namespace bsl = BlackLynx::SearchLynx;
f12339cf david.sorber
namespace sfs = std::filesystem;
a5734307 David Sorber
const std::string BOLD("\033[1m");
const std::string ENDC("\033[0m");
const std::string RED("\033[31m");
const std::string YELLOW("\033[33m");
const std::string UP_ONE = "\033[1A";

f12339cf david.sorber
const std::string VERSION("ReadTest version 1.2.0-dev");
a5734307 David Sorber
f12339cf david.sorber
const std::vector<std::string> UNITS{"b/sec", "KB/s", "MB/s", "GB/s", "TB/s"};
a0d1d164 David Sorber
double printRate(double rawRate, uint32_t& unitIdx)
{
uint32_t idx = 0;
double rate = rawRate;
while (rate > 1024)
{
rate /= 1024;
++idx;
}
unitIdx = idx;
return rate;
}

058717b0 David Sorber
void getKernelVersion(uint32_t& major, uint32_t& minor, uint32_t& bugfix)
{
// Read "uname" info
struct utsname unameBuf;
uname(&unameBuf);
// Release contains kernel version; e.g. 5.15.0-33-generic
char* startPtr = unameBuf.release;
char* endPtr = nullptr;
major = std::strtoul(startPtr, &endPtr, 10);
startPtr = endPtr + 1;
minor = std::strtoul(startPtr, &endPtr, 10);
startPtr = endPtr + 1;
bugfix = std::strtoul(startPtr, &endPtr, 10);
#if 0
std::cout << "MAJOR: " << major << std::endl;
std::cout << "MINOR: " << minor << std::endl;
std::cout << "BUGFIX: " << bugfix << std::endl;

std::cout << "sysname: " << unameBuf.sysname << std::endl;
std::cout << "nodename: " << unameBuf.nodename << std::endl;
std::cout << "release: " << unameBuf.release << std::endl;
std::cout << "version: " << unameBuf.version << std::endl;
std::cout << "machine: " << unameBuf.machine << std::endl;
#endif
}

bool validateKernelVersion(uint32_t major, uint32_t minor, uint32_t bugfix)
{
uint32_t majorVer, minorVer, bugfixVer;
getKernelVersion(majorVer, minorVer, bugfixVer);

if (majorVer >= major)
{
if (minorVer >= minor)
{
if (bugfixVer >= bugfix)
{
return true;
}
}
}
return false;
}

a5734307 David Sorber
void usage()
{
f12339cf david.sorber
std::cout << BOLD << "Read Test Utility\n\n" << ENDC
<< "Usage: read_test <--help | -h>\n"
<< " read_test --version\n\n"
<< "Options:\n"
<< " --input-file Path to the file to read.\n"
<< " --read-type The type of read to perform: auto, normal, aio, or uring\n"
<< " --num-threads The number of reader threads to use.\n"
<< " --chunk-size Chunk size in kilobytes.\n\n";
a5734307 David Sorber
}

template <typename T>
void lowercase(std::basic_string<T>& str)
{
std::transform(str.begin(), str.end(), str.begin(), tolower);
}

// Type used to set "read-type" value
enum class ReadType
{
DEFAULT_INVALID,
AUTO,
NORMAL,
864b608c David Sorber
AIO,
URING
a5734307 David Sorber
};

struct ReadTypeValue
{
ReadTypeValue()
{
}
ReadTypeValue(std::string const& val)
: value(val)
{
}
ReadType getValue()
{
if (value == "auto")
{
return ReadType::AUTO;
}
else if (value == "normal")
{
return ReadType::NORMAL;
}
else if (value == "aio")
{
return ReadType::AIO;
}
864b608c David Sorber
else if (value == "uring")
{
return ReadType::URING;
}
a5734307 David Sorber
return ReadType::DEFAULT_INVALID;
}
std::string value;
};

void validate(
boost::any& v,
std::vector<std::string> const& values,
ReadTypeValue*,
int)
{
// Make sure no previous assignment to 'v' was made.
bpo::validators::check_first_occurrence(v);
// Extract the first string from 'values'. If there is more than one string,
// it's an error, and exception will be thrown.
std::string val = bpo::validators::get_single_string(values);
lowercase(val);
// Check for valid values
864b608c David Sorber
if (val == "auto" || val == "normal" || val == "aio" || val == "uring")
a5734307 David Sorber
{
v = boost::any(ReadTypeValue(val));
}
else
{
throw bpo::validation_error(bpo::validation_error::invalid_option_value);
}
}
a0d1d164 David Sorber
int main(int argc, char** argv)
{
a5734307 David Sorber
bpo::variables_map optionsMap;
ReadTypeValue readType("default");
f12339cf david.sorber
std::string filePath;
864b608c David Sorber
uint32_t numThreads = 0;
f12339cf david.sorber
uint32_t chunkSize = 65536; // in Kb
a0d1d164 David Sorber
a5734307 David Sorber
try
{
// General options
bpo::options_description generalOpts("General options");
generalOpts.add_options()
("help,h", "display this help message")
("version",
"output version number and exit")
("read-type", bpo::value<ReadTypeValue>(&readType),
"read type")
864b608c David Sorber
("num-threads", bpo::value<uint32_t>(&numThreads),
"number reader threads")
f12339cf david.sorber
("input-file", bpo::value<std::string>(&filePath)->required(),
a5734307 David Sorber
"input file")
f12339cf david.sorber
("chunk-size", bpo::value<uint32_t>(&chunkSize), "chunk size in Kb")
a5734307 David Sorber
;
// Positional options
bpo::positional_options_description posOpts;
posOpts.add("input-file", 1);
bpo::parsed_options parsed = bpo::command_line_parser(argc, argv).
options(generalOpts).positional(posOpts)
.run();
bpo::store(parsed, optionsMap);
// Display help if the option is listed or if no arguments are provided
if (optionsMap.count("help"))// || (argc == 1))
{
usage();
return 1;
}
// Display version and exist if option listed
if (optionsMap.count("version"))
{
std::cout << VERSION << std::endl;
return 2;
}
// Notify any errors
bpo::notify(optionsMap);
}
catch (std::exception& e)
a0d1d164 David Sorber
{
a5734307 David Sorber
std::cerr << BOLD << RED << "\nERROR: " << ENDC << e.what() << "\n"
<< std::endl;
a0d1d164 David Sorber
return -1;
}
a5734307 David Sorber
std::cout << "Read Test Utility\n" << std::endl;
std::cout << "Read type: " << readType.value << "\n";
f12339cf david.sorber
std::cout << "Input file: " << filePath << "\n";
a0d1d164 David Sorber
f12339cf david.sorber
// Validate kernel version if user selected uring read type
058717b0 David Sorber
if (readType.getValue() == ReadType::URING &&
(! validateKernelVersion(5, 6, 0)))
{
uint32_t majorVer, minorVer, bugfixVer;
getKernelVersion(majorVer, minorVer, bugfixVer);
std::cerr << BOLD << RED << "\nERROR: " << ENDC << "The uring read type "
<< "requires kernel version >= 5.6; the current kernel version"
<< " " << majorVer << "." << minorVer << " is too old.\n"
<< std::endl;
return -1;
}
a0d1d164 David Sorber
// Start time measurement
auto start = std::chrono::system_clock::now();
f12339cf david.sorber
uint64_t fileSize = sfs::file_size(filePath);
a5734307 David Sorber
// Prime the config object then allocate buffers
auto bufAllocator = bsl::BufferAllocator::getInstance();
f12339cf david.sorber
bufAllocator->initAllocation(chunkSize, fileSize);
a0d1d164 David Sorber
a5734307 David Sorber
// Use the read type argument to decide which ExecDriver instance to create
bsl::ExecDriver* driver = nullptr;
switch (readType.getValue())
{
case ReadType::DEFAULT_INVALID:
case ReadType::AUTO:
case ReadType::AIO:
864b608c David Sorber
// Set default number of threads
if (numThreads == 0)
{
numThreads = 3;
}
f12339cf david.sorber
driver = new bsl::AIO_ExecDriver(filePath, numThreads, chunkSize);
a5734307 David Sorber
break;
case ReadType::NORMAL:
864b608c David Sorber
// Set default number of threads
if (numThreads == 0)
{
numThreads = 22;
}
f12339cf david.sorber
driver = new bsl::ExecDriver(filePath, numThreads, chunkSize);
864b608c David Sorber
break;
case ReadType::URING:
3dda5d70 david.sorber
// numThreads = 1;
driver = new bsl::URingExecDriver(filePath, numThreads, chunkSize);
a5734307 David Sorber
break;
}
864b608c David Sorber
std::cout << "Num threads: " << numThreads << "\n";
11aeae96 david.sorber
std::cout << "Chunk size: " << chunkSize << " KB\n";
a5734307 David Sorber
driver->start();
driver->waitUntilComplete();
a0d1d164 David Sorber
// Deallocate buffers
bufAllocator->deallocateBuffers();
// End time measurement
auto end = std::chrono::system_clock::now();
auto diff = end - start;
double duration = std::chrono::duration <double>(diff).count() ;
a5734307 David Sorber
std::cout << "Duration: " << std::fixed << std::setprecision(3)
11aeae96 david.sorber
<< duration << " s" << std::endl;
a0d1d164 David Sorber
double rawRate = fileSize / duration;
a5734307 David Sorber
std::cout << "Raw rate: " << rawRate << " b/sec" << std::endl;
a0d1d164 David Sorber
uint32_t idx = 0;
double rate = printRate(rawRate, idx);
a5734307 David Sorber
std::cout << "Rate: " << std::fixed << std::setprecision(3) << rate
a0d1d164 David Sorber
<< " " << UNITS[idx] << std::endl;
a5734307 David Sorber
// Clean up
delete driver;
driver = nullptr;
a0d1d164 David Sorber
return 0;
}