Unverified Commit f2599137 authored by Yuting Jiang's avatar Yuting Jiang Committed by GitHub
Browse files

Benchmarks: Add benchmark - Add source code of DirectxGPUCopy microbenchmark (#486)

**Description**
Add source code of DirectxGPUCopy microbenchmark.
parent af4d18de
...@@ -37,9 +37,8 @@ class BenchmarkOptions : public Options { ...@@ -37,9 +37,8 @@ class BenchmarkOptions : public Options {
* @brief Parse the arguments. * @brief Parse the arguments.
*/ */
virtual void parse_arguments() { virtual void parse_arguments() {
num_loops = get_cmd_line_argument_int("--num_loops", 10); num_loops = get_cmd_line_argument_int("--num_loops", 10);
num_warm_up = get_cmd_line_argument_int("--num_loops", 0); num_warm_up = get_cmd_line_argument_int("--num_warm_up", 0);
m = get_cmd_line_argument_int("--m", 16 * 256); m = get_cmd_line_argument_int("--m", 16 * 256);
n = get_cmd_line_argument_int("--n", 16 * 256); n = get_cmd_line_argument_int("--n", 16 * 256);
k = get_cmd_line_argument_int("--k", 16 * 256); k = get_cmd_line_argument_int("--k", 16 * 256);
......
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include <iostream>
#include <sstream>
#include <string>
#include "../directx_utils/Options.h"
class BenchmarkOptions : public Options {
public:
// Size of data for GPU copy.
unsigned long long size;
// Run size from min_size to max_size for GPU copy.
unsigned long long min_size = 0;
// Run size from min_size to max_size for GPU copy.
unsigned long long max_size = 0;
// Number of warm up copy times to run.
int num_warm_up = 0;
// Number of copy times to run.
int num_loops = 0;
// Host-to-device copy mode.
bool htod_enabled = false;
// device-to-host copy mode.
bool dtoh_enabled = false;
// Whether check data after copy.
bool check_data = false;
/**
* @brief Construct a new BenchmarkOptions object.
*/
BenchmarkOptions(int argc, char *argv[]) : Options(argc, argv) {}
/**
* @brief Parse the arguments.
*/
virtual void parse_arguments() override {
size = get_cmd_line_argument_int("--size", -1);
num_warm_up = get_cmd_line_argument_int("--warm_up", 20);
num_loops = get_cmd_line_argument_int("--num_loops", 100000);
min_size = get_cmd_line_argument_int("--minbytes", 64);
max_size = get_cmd_line_argument_ulonglong("--maxbytes", 8 * 1024 * 1024);
htod_enabled = get_cmd_line_argument_bool("--htod");
dtoh_enabled = get_cmd_line_argument_bool("--dtoh");
check_data = get_cmd_line_argument_bool("--check");
if (!htod_enabled && !dtoh_enabled) {
std::cerr << "Error: Please specify copy mode!" << std::endl;
exit(-1);
}
}
/**
* @brief Get the option usage.
*/
void get_option_usage() override {
std::cout << "Usage: " << std::endl;
std::cout << " --size <int> Size of data for GPU copy." << std::endl;
std::cout << " --warm_up <int> Number of warm up copy times to run." << std::endl;
std::cout << " --num_loops <int> Number of copy times to run." << std::endl;
std::cout << " --minbytes <int> Run size from min_size to max_size for GPU copy." << std::endl;
std::cout << " --maxbytes <int> Run size from min_size to max_size for GPU copy." << std::endl;
std::cout << " --htod <bool> Host-to-device copy mode." << std::endl;
std::cout << " --dtoh <bool> Device-to-host copy mode." << std::endl;
std::cout << " --check <bool> Whether check data after copy." << std::endl;
}
};
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include <iostream>
#include <tchar.h>
#include <vector>
#include "GPUCopyBw.h"
/**
* @brief Run the benchmark.
*/
void GPUCopyBw::Run() {
CreatePipeline();
double time_ms = CopyResourceBench(opts->size, opts->num_loops, opts->num_warm_up);
double bw = opts->size * opts->num_loops / time_ms / 1e6;
string mode = opts->dtoh_enabled ? "dtoh" : "htod";
cout << mode << ": " << opts->size << "B " << bw << " GB/s" << endl;
}
/**
* @brief Allocate gpu resources, construct a array of buffers with given size.
* @param uSize the size of each buffer inside of array.
*/
void GPUCopyBw::InitializeBuffer(SIZE_T uSize) {
m_defaultBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(uSize);
// The output buffer (created below) is on a default heap, so only the GPU can access it.
auto defaultHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
ThrowIfFailed(m_device->CreateCommittedResource(&defaultHeapProperties, D3D12_HEAP_FLAG_NONE, &m_defaultBufferDesc,
D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
IID_PPV_ARGS(&m_defaultBuffer)));
// Create upload buffer to upload data to GPU.
auto uploadHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
ThrowIfFailed(m_device->CreateCommittedResource(&uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &m_defaultBufferDesc,
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS(&m_uploadBuffer)));
// Create read back buffer if dtoh mode.
if (opts->dtoh_enabled) {
auto readbackHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK);
ThrowIfFailed(m_device->CreateCommittedResource(&readbackHeapProperties, D3D12_HEAP_FLAG_NONE,
&m_defaultBufferDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
IID_PPV_ARGS(&m_readbackBuffer)));
}
}
/**
* @brief Allocate data on CPU side to prepare upload.
* @param byteSize the size of data to be uploaded.
*/
void GPUCopyBw::PrepareData(SIZE_T byteSize) {
m_pDataBegin = std::make_unique<uint8_t[]>(byteSize);
constexpr int uint8_mod = 256;
for (int j = 0; j < byteSize; j++) {
m_pDataBegin[j] = static_cast<uint8_t>(j % uint8_mod);
}
}
/**
* @brief Check result correctness.
* @param byteSize the size of data to be checked.
* @param pData the byte array that expect to be.
* @return true result is correct.
*/
bool GPUCopyBw::CheckData(SIZE_T byteSize, const uint8_t *pData) {
if (opts->dtoh_enabled) {
D3D12_RANGE readbackBufferRange{0, byteSize};
uint8_t *pReadbackBufferData{};
// Read back data from GPU.
ThrowIfFailed(m_readbackBuffer->Map(0, &readbackBufferRange, reinterpret_cast<void **>(&pReadbackBufferData)));
// Check result correctness.
for (int i = 0; i < byteSize; i++) {
if (pData[i] != pReadbackBufferData[i])
return false;
}
D3D12_RANGE emptyRange{0, 0};
m_readbackBuffer->Unmap(0, &emptyRange);
}
return true;
}
/**
* @brief GPU copy benchmark.
* @param size the size of data to copy.
* @param loops the number of copy times to measure the performance.
* @return double the time elapsed in ms.
*/
double GPUCopyBw::CopyResourceBench(SIZE_T size, int loops, int warm_up) {
// Prepare CPU side data buffer.
PrepareData(size);
// Prepare GPU resources and buffers.
InitializeBuffer(size);
// Set data into source buffer.
PrepareSourceBufferData(m_pDataBegin.get(), size);
// Run the copy command.
gpuTimer.init(m_device.Get(), m_commandQueue.Get(), 1, D3D12::QueueType::copy);
for (int i = 0; i < loops + warm_up; i++) {
if (i == warm_up) {
// Start timestamp.
this->gpuTimer.start(m_commandList.Get(), 0);
}
if (opts->htod_enabled) {
CopyResourceFromUploadToDefault();
} else if (opts->dtoh_enabled) {
CopyResourceFromDefaultToReadback();
}
}
// Stop timestamp.
this->gpuTimer.stop(m_commandList.Get(), 0);
this->gpuTimer.resolveQueryToCPU(m_commandList.Get(), 0);
// Close, execute (and optionally reset) the command list, and also to use a fence to wait for the command queue.
this->ExecuteWaitForCopyQueue();
// Check if result is correctly copied.
// The code below assumes that the GPU wrote FLOATs to the buffer.
if (opts->check_data) {
bool correctness = CheckData(size, m_pDataBegin.get());
if (!correctness) {
std::cout << "Error: Result is not correct!" << std::endl;
}
}
return this->gpuTimer.getElapsedMsByTimestampPair(0);
}
/**
* @brief Copy data from CPU side to GPU side.
*/
void GPUCopyBw::CopyResourceFromUploadToDefault() {
m_commandList->CopyResource(m_defaultBuffer.Get(), m_uploadBuffer.Get());
}
/**
* @brief Copy data from GPU side to GPU side.
*/
void GPUCopyBw::CopyResourceFromDefaultToDefault() {
m_commandList->CopyResource(m_defaultBuffer.Get(), m_defaultDescBuffer.Get());
}
/**
* @brief Copy data from GPU side to CPU side.
*/
void GPUCopyBw::CopyResourceFromDefaultToReadback() {
m_commandList->CopyResource(m_readbackBuffer.Get(), m_defaultBuffer.Get());
}
/**
* @brief Execute the commands and wait until command completed.
*/
void GPUCopyBw::ExecuteWaitForCopyQueue(DWORD dwMilliseconds) {
// Close, execute (and optionally reset) the command list, and also to use a fence to wait for the command queue.
ThrowIfFailed(m_commandList->Close());
ID3D12CommandList *listsToExecute[] = {m_commandList.Get()};
m_commandQueue->ExecuteCommandLists(ARRAYSIZE(listsToExecute), listsToExecute);
// Signal and increment the fence value.
const UINT64 fenceL = m_copyFenceValue;
ThrowIfFailed(m_commandQueue->Signal(m_copyFence.Get(), fenceL));
m_copyFenceValue++;
// Wait until command queue is done.
if (m_copyFence->GetCompletedValue() < fenceL) {
ThrowIfFailed(m_copyFence->SetEventOnCompletion(fenceL, m_copyEventHandle));
WaitForSingleObject(m_copyEventHandle, dwMilliseconds);
}
// Reset the command allocator and command list.
ID3D12CommandAllocator *activeAllocator = m_commandAllocator.Get();
ThrowIfFailed(activeAllocator->Reset());
ThrowIfFailed(m_commandList->Reset(activeAllocator, nullptr));
}
/**
* @brief Prepare data of the source buffer of benchmark.
* @param pData the data that should upload.
* @param byteSize the size of data.
*/
void GPUCopyBw::PrepareSourceBufferData(const void *pData, SIZE_T byteSize) {
// Upload data from CPU to upload buffer.
void *p;
ThrowIfFailed(m_uploadBuffer->Map(0, nullptr, &p));
memcpy(p, pData, byteSize);
m_uploadBuffer->Unmap(0, nullptr);
if (opts->dtoh_enabled) {
// Upload data from upload to default buffer.
CopyResourceFromUploadToDefault();
D3D12_RESOURCE_BARRIER outputBufferResourceBarrier{CD3DX12_RESOURCE_BARRIER::Transition(
m_defaultBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE)};
m_commandList->ResourceBarrier(1, &outputBufferResourceBarrier);
ExecuteWaitForCopyQueue();
}
}
/**
* @brief Create pipeline including
* create device object, command list, command queue
* and synchronization objects.
*/
void GPUCopyBw::CreatePipeline() {
UINT dxgiFactoryFlags = 0;
#if _DEBUG
// Enable the debug layer (requires the Graphics Tools "optional feature").
// NOTE: Enabling the debug layer after device creation will invalidate the active device.
{
ComPtr<ID3D12Debug> debugController;
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) {
debugController->EnableDebugLayer();
// Enable additional debug layers.
dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
}
}
#endif
ComPtr<IDXGIFactory4> factory;
ThrowIfFailed(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)));
ComPtr<IDXGIAdapter1> hardwareAdapter;
GetHardwareAdapter(factory.Get(), &hardwareAdapter);
ThrowIfFailed(D3D12CreateDevice(hardwareAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device)));
D3D12_COMMAND_QUEUE_DESC cqd3 = {};
cqd3.Type = D3D12_COMMAND_LIST_TYPE_COPY;
ThrowIfFailed(m_device->CreateCommandQueue(&cqd3, IID_PPV_ARGS(&m_commandQueue)));
ThrowIfFailed(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_COPY, IID_PPV_ARGS(&m_commandAllocator)));
// Create the command list.
ThrowIfFailed(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_COPY, m_commandAllocator.Get(), nullptr,
IID_PPV_ARGS(&m_commandList)));
ThrowIfFailed(m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_copyFence)));
m_copyFenceValue = 1;
// Create an event handle to use for GPU synchronization.
m_copyEventHandle = CreateEvent(0, false, false, 0);
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers.
#endif
#include <D3Dcompiler.h>
#include <DirectXMath.h>
#include <d3d12.h>
#include <d3d12shader.h>
#include <dxgi1_6.h>
#include <shellapi.h>
#include <string>
#include <wrl.h>
// linker
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3d12.lib")
#pragma comment(lib, "d3dcompiler.lib")
#if defined(_DEBUG)
#include <dxgidebug.h>
#endif
#include "../directx_third_party/DXSampleHelper.h"
#include "../directx_third_party/d3dx12.h"
#include "../directx_utils/D3D12Timer.h"
#include "BenchmarkOptions.h"
using namespace DirectX;
// Note that while ComPtr is used to manage the lifetime of resources on the CPU,
// it has no understanding of the lifetime of resources on the GPU. Apps must account
// for the GPU lifetime of resources to avoid destroying objects that may still be
// referenced by the GPU.
// An example of this can be found in the class method: OnDestroy().
using Microsoft::WRL::ComPtr;
using namespace std;
class GPUCopyBw {
public:
GPUCopyBw(BenchmarkOptions *opts) : opts(opts) {}
~GPUCopyBw() { CloseHandle(m_copyFence.Get()); }
/**
* @brief Run the benchmark.
*/
void Run();
/**
* @brief GPU copy benchmark.
* @param size the size of data to copy.
* @param loops the number of copy times to measure the performance.
* @return double the time elapsed in ms.
*/
double CopyResourceBench(SIZE_T size, int loops, int warm_up);
/**
* @brief Create pipeline including
* create device object, command list, command queue
* and synchronization objects.
*/
void CreatePipeline();
/**
* @brief Allocate data on CPU side to prepare upload.
* @param byteSize the size of data to be uploaded.
*/
void PrepareData(SIZE_T byteSize);
/**
* @brief Allocate gpu resources, construct a array of buffers with given size.
* @param uSize the size of each buffer inside of array.
*/
void InitializeBuffer(SIZE_T uSize);
/**
* @brief Prepare data of the source buffer of benchmark.
* @param pData the data that should upload.
* @param byteSize the size of data.
*/
void PrepareSourceBufferData(const void *pData, SIZE_T byteSize);
/**
* @brief Copy data from CPU side to GPU side.
*/
void CopyResourceFromUploadToDefault();
/**
* @brief Copy data from GPU side to CPU side.
*/
void CopyResourceFromDefaultToReadback();
/**
* @brief Copy data from GPU side to GPU side.
*/
void CopyResourceFromDefaultToDefault();
/**
* @brief Execute the commands and wait until command completed.
*/
void ExecuteWaitForCopyQueue(DWORD dwMilliseconds = 60000);
/**
* @brief Check result correctness.
* @param byteSize the size of data to be checked.
* @param pData the byte array that expect to be.
* @return true result is correct.
*/
bool CheckData(SIZE_T byteSize, const uint8_t *pData);
private:
// Pipeline objects.
ComPtr<ID3D12Device> m_device = nullptr;
ComPtr<ID3D12CommandAllocator> m_commandAllocator = nullptr;
ComPtr<ID3D12CommandQueue> m_commandQueue = nullptr;
ComPtr<ID3D12GraphicsCommandList> m_commandList = nullptr;
// App resources.
// Pointer of CPU size resource.
std::unique_ptr<uint8_t[]> m_pDataBegin = nullptr;
// GPU side buffer.
ComPtr<ID3D12Resource> m_defaultBuffer = nullptr;
// GPU side buffer as destination if in dtod mode.
ComPtr<ID3D12Resource> m_defaultDescBuffer = nullptr;
// Upload buffer to upload data from CPU to GPU.
ComPtr<ID3D12Resource> m_uploadBuffer = nullptr;
// Read back buffer to check data correctness.
ComPtr<ID3D12Resource> m_readbackBuffer = nullptr;
// Default buffer descriptor.
D3D12_RESOURCE_DESC m_defaultBufferDesc;
// Synchronization objects.
ComPtr<ID3D12Fence1> m_copyFence = nullptr;
HANDLE m_copyEventHandle = nullptr;
UINT64 m_copyFenceValue = 0;
// GPU timer.
D3D12::D3D12Timer gpuTimer;
// Options.
BenchmarkOptions *opts;
};
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{f561fb23-0ec2-492f-9c8d-9555a0f6a4f6}</ProjectGuid>
<RootNamespace>GPUCopyBw</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\directx_utils\D3D12Timer.cpp" />
<ClCompile Include="GPUCopyBw.cpp" />
<ClCompile Include="Main.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\directx_third_party\d3dx12.h" />
<ClInclude Include="..\directx_third_party\DXSampleHelper.h" />
<ClInclude Include="..\directx_utils\D3D12Timer.h" />
<ClInclude Include="..\directx_utils\Options.h" />
<ClInclude Include="GPUCopyBw.h" />
<ClInclude Include="BenchmarkOptions.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
\ No newline at end of file
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include <iostream>
#include <sstream>
#include "GPUCopyBw.h"
int main(int argc, char *argv[]) {
BenchmarkOptions option(argc, argv);
option.init();
if (option.size != -1) {
// Run only one size
GPUCopyBw benchmark(&option);
benchmark.Run();
} else {
// Run all sizes
for (SIZE_T usize = option.min_size; usize <= option.max_size; usize += usize) {
GPUCopyBw benchmark(&option);
benchmark.Run();
}
}
}
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <vector>
struct UInt3 { struct UInt3 {
unsigned int x; unsigned int x;
...@@ -55,6 +56,23 @@ class Options { ...@@ -55,6 +56,23 @@ class Options {
* @param defaults the default value. * @param defaults the default value.
* @return unsigned long long the unsigned long long type value of cmd line argument 'option'. * @return unsigned long long the unsigned long long type value of cmd line argument 'option'.
*/ */
unsigned long long get_cmd_line_argument_ulonglong(const std::string &option, unsigned long long defaults) {
if (char *value = get_cmd_option(option)) {
try {
return std::stoull(value);
} catch (const std::exception &e) {
std::cout << "Error: Invalid argument - " << option << " should be unsigned long long" << e.what()
<< '\n';
}
}
return defaults;
}
/**
* @brief Split the string by ',' and convert to unsigned int.
* @param str the string to be split.
* @return std::vector<unsigned int> the vector of unsigned int.
*/
std::vector<unsigned int> splitAndConvertToInt(const std::string &str) { std::vector<unsigned int> splitAndConvertToInt(const std::string &str) {
std::vector<unsigned int> result; std::vector<unsigned int> result;
std::stringstream ss(str); std::stringstream ss(str);
...@@ -71,10 +89,10 @@ class Options { ...@@ -71,10 +89,10 @@ class Options {
} }
/** /**
* @brief Get the unsigned int type value of cmd line argument. * @brief Get the unsigned int 3 type value of cmd line argument.
* @param option the cmd line argument. * @param option the cmd line argument.
* @param defaults the default value. * @param defaults the default value.
* @return unsigned int the unsigned int type value of cmd line argument 'option'. * @return unsigned int the unsigned int 3 type value of cmd line argument 'option'.
*/ */
UInt3 get_cmd_line_argument_uint3(const std::string &option, const UInt3 &defaults) { UInt3 get_cmd_line_argument_uint3(const std::string &option, const UInt3 &defaults) {
if (char *value = get_cmd_option(option)) { if (char *value = get_cmd_option(option)) {
...@@ -128,12 +146,12 @@ class Options { ...@@ -128,12 +146,12 @@ class Options {
/** /**
* @brief Get the option usage. * @brief Get the option usage.
*/ */
virtual void get_option_usage(){}; virtual void get_option_usage() = 0;
/** /**
* @brief Parse the arguments. * @brief Parse the arguments.
*/ */
virtual void parse_arguments(){}; virtual void parse_arguments() = 0;
public: public:
/** /**
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment