256 líneas
11 KiB
C++
256 líneas
11 KiB
C++
/*
|
|
Copyright (c) 2021-2022 Advanced Micro Devices, Inc. All rights reserved.
|
|
|
|
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.
|
|
*/
|
|
|
|
#ifndef ENVVARS_HPP
|
|
#define ENVVARS_HPP
|
|
|
|
#include <algorithm>
|
|
|
|
// This class manages environment variable that affect TransferBench
|
|
class EnvVars
|
|
{
|
|
public:
|
|
// Default configuration values
|
|
int const DEFAULT_NUM_WARMUPS = 3;
|
|
int const DEFAULT_NUM_ITERATIONS = 10;
|
|
int const DEFAULT_SAMPLING_FACTOR = 1;
|
|
int const DEFAULT_NUM_CPU_PER_LINK = 4;
|
|
|
|
// Environment variables
|
|
int useHipCall; // Use hipMemcpy/hipMemset instead of custom shader kernels
|
|
int useMemset; // Perform a memset instead of a copy (ignores source memory)
|
|
int useSingleSync; // Perform synchronization only once after all iterations instead of per iteration
|
|
int useInteractive; // Pause for user-input before starting transfer loop
|
|
int useSleep; // Adds a 100ms sleep after each synchronization
|
|
int combineTiming; // Combines the timing with kernel launch
|
|
int showAddr; // Print out memory addresses for each Link
|
|
int outputToCsv; // Output in CSV format
|
|
int byteOffset; // Byte-offset for memory allocations
|
|
int numWarmups; // Number of un-timed warmup iterations to perform
|
|
int numIterations; // Number of timed iterations to perform
|
|
int samplingFactor; // Affects how many different values of N are generated (when N set to 0)
|
|
int numCpuPerLink; // Number of CPU child threads to use per CPU link
|
|
int sharedMemBytes; // Amount of shared memory to use per threadblock
|
|
int blockBytes; // Each CU, except the last, gets a multiple of this many bytes to copy
|
|
|
|
std::vector<float> fillPattern; // Pattern of floats used to fill source data
|
|
|
|
// Constructor that collects values
|
|
EnvVars()
|
|
{
|
|
int maxSharedMemBytes = 0;
|
|
hipDeviceGetAttribute(&maxSharedMemBytes,
|
|
hipDeviceAttributeMaxSharedMemoryPerMultiprocessor, 0);
|
|
|
|
useHipCall = GetEnvVar("USE_HIP_CALL" , 0);
|
|
useMemset = GetEnvVar("USE_MEMSET" , 0);
|
|
useSingleSync = GetEnvVar("USE_SINGLE_SYNC" , 1);
|
|
useInteractive = GetEnvVar("USE_INTERACTIVE" , 0);
|
|
combineTiming = GetEnvVar("COMBINE_TIMING" , 0);
|
|
showAddr = GetEnvVar("SHOW_ADDR" , 0);
|
|
outputToCsv = GetEnvVar("OUTPUT_TO_CSV" , 0);
|
|
byteOffset = GetEnvVar("BYTE_OFFSET" , 0);
|
|
numWarmups = GetEnvVar("NUM_WARMUPS" , DEFAULT_NUM_WARMUPS);
|
|
numIterations = GetEnvVar("NUM_ITERATIONS" , DEFAULT_NUM_ITERATIONS);
|
|
samplingFactor = GetEnvVar("SAMPLING_FACTOR" , DEFAULT_SAMPLING_FACTOR);
|
|
numCpuPerLink = GetEnvVar("NUM_CPU_PER_LINK" , DEFAULT_NUM_CPU_PER_LINK);
|
|
sharedMemBytes = GetEnvVar("SHARED_MEM_BYTES" , maxSharedMemBytes / 2 + 1);
|
|
blockBytes = GetEnvVar("BLOCK_BYTES" , 256);
|
|
|
|
// Check for fill pattern
|
|
char* pattern = getenv("FILL_PATTERN");
|
|
if (pattern != NULL)
|
|
{
|
|
int patternLen = strlen(pattern);
|
|
if (patternLen % 2)
|
|
{
|
|
printf("[ERROR] FILL_PATTERN must contain an even-number of hex digits\n");
|
|
exit(1);
|
|
}
|
|
|
|
// Read in bytes
|
|
std::vector<unsigned char> bytes;
|
|
unsigned char val = 0;
|
|
for (int i = 0; i < patternLen; i++)
|
|
{
|
|
if ('0' <= pattern[i] && pattern[i] <= '9')
|
|
val += (pattern[i] - '0');
|
|
else if ('A' <= pattern[i] && pattern[i] <= 'F')
|
|
val += (pattern[i] - 'A' + 10);
|
|
else if ('a' <= pattern[i] && pattern[i] <= 'f')
|
|
val += (pattern[i] - 'a' + 10);
|
|
else
|
|
{
|
|
printf("[ERROR] FILL_PATTERN must contain an even-number of hex digits (0-9'/a-f/A-F). (not %c)\n", pattern[i]);
|
|
exit(1);
|
|
}
|
|
|
|
if (i % 2 == 0)
|
|
val <<= 4;
|
|
else
|
|
{
|
|
bytes.push_back(val);
|
|
val = 0;
|
|
}
|
|
}
|
|
|
|
// Reverse bytes (input is assumed to be given in big-endian)
|
|
std::reverse(bytes.begin(), bytes.end());
|
|
|
|
// Figure out how many copies of the pattern are necessary to fill a 4-byte float properly
|
|
int copies;
|
|
switch (patternLen % 8)
|
|
{
|
|
case 0: copies = 1; break;
|
|
case 4: copies = 2; break;
|
|
default: copies = 4; break;
|
|
}
|
|
|
|
// Fill floats
|
|
int numFloats = copies * patternLen / 8;
|
|
fillPattern.resize(numFloats);
|
|
unsigned char* rawData = (unsigned char*) fillPattern.data();
|
|
for (int i = 0; i < numFloats * 4; i++)
|
|
rawData[i] = bytes[i % bytes.size()];
|
|
}
|
|
else fillPattern.clear();
|
|
|
|
// Perform some basic validation
|
|
if (byteOffset % sizeof(float))
|
|
{
|
|
printf("[ERROR] BYTE_OFFSET must be set to multiple of %lu\n", sizeof(float));
|
|
exit(1);
|
|
}
|
|
if (numWarmups < 0)
|
|
{
|
|
printf("[ERROR] NUM_WARMUPS must be set to a non-negative number\n");
|
|
exit(1);
|
|
}
|
|
if (numIterations <= 0)
|
|
{
|
|
printf("[ERROR] NUM_ITERATIONS must be set to a positive number\n");
|
|
exit(1);
|
|
}
|
|
if (samplingFactor < 1)
|
|
{
|
|
printf("[ERROR] SAMPLING_FACTOR must be greater or equal to 1\n");
|
|
exit(1);
|
|
}
|
|
if (numCpuPerLink < 1)
|
|
{
|
|
printf("[ERROR] NUM_CPU_PER_LINK must be greater or equal to 1\n");
|
|
exit(1);
|
|
}
|
|
if (sharedMemBytes < 0 || sharedMemBytes > maxSharedMemBytes)
|
|
{
|
|
printf("[ERROR] SHARED_MEM_BYTES must be between 0 and %d\n", maxSharedMemBytes);
|
|
exit(1);
|
|
}
|
|
if (blockBytes <= 0 || blockBytes % 4)
|
|
{
|
|
printf("[ERROR] BLOCK_BYTES must be a positive multiple of 4\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
// Display info on the env vars that can be used
|
|
static void DisplayUsage()
|
|
{
|
|
printf("Environment variables:\n");
|
|
printf("======================\n");
|
|
printf(" USE_HIP_CALL - Use hipMemcpy/hipMemset instead of custom shader kernels for GPU-executed copies\n");
|
|
printf(" USE_MEMSET - Perform a memset instead of a copy (ignores source memory)\n");
|
|
printf(" USE_SINGLE_SYNC - Perform synchronization only once after all iterations instead of per iteration\n");
|
|
printf(" USE_INTERACTIVE - Pause for user-input before starting transfer loop\n");
|
|
printf(" COMBINE_TIMING - Combines timing with launch (potentially lower timing overhead)\n");
|
|
printf(" SHOW_ADDR - Print out memory addresses for each Link\n");
|
|
printf(" OUTPUT_TO_CSV - Outputs to CSV format if set\n");
|
|
printf(" BYTE_OFFSET - Initial byte-offset for memory allocations. Must be multiple of 4. Defaults to 0\n");
|
|
printf(" NUM_WARMUPS=W - Perform W untimed warmup iteration(s) per test\n");
|
|
printf(" NUM_ITERATIONS=I - Perform I timed iteration(s) per test\n");
|
|
printf(" SAMPLING_FACTOR=F - Add F samples (when possible) between powers of 2 when auto-generating data sizes\n");
|
|
printf(" NUM_CPU_PER_LINK=C - Use C threads per Link for CPU-executed copies\n");
|
|
printf(" FILL_PATTERN=STR - Fill input buffer with pattern specified in hex digits (0-9,a-f,A-F). Must be even number of digits, (byte-level big-endian)\n");
|
|
printf(" SHARED_MEM_BYTES=X - Use X shared mem bytes per threadblock, potentially to avoid multiple threadblocks per CU\n");
|
|
printf(" BLOCK_BYTES=B - Each CU (except the last) receives a multiple of BLOCK_BYTES to copy\n");
|
|
}
|
|
|
|
// Display env var settings
|
|
void DisplayEnvVars() const
|
|
{
|
|
if (!outputToCsv)
|
|
{
|
|
printf("Run configuration\n");
|
|
printf("=====================================================\n");
|
|
printf("%-20s = %12d : Using %s for GPU-executed copies\n", "USE_HIP_CALL", useHipCall,
|
|
useHipCall ? "HIP functions" : "custom kernels");
|
|
printf("%-20s = %12d : Performing %s\n", "USE_MEMSET", useMemset,
|
|
useMemset ? "memset" : "memcopy");
|
|
if (useHipCall && !useMemset)
|
|
{
|
|
char* env = getenv("HSA_ENABLE_SDMA");
|
|
printf("%-20s = %12s : %s\n", "HSA_ENABLE_SDMA", env,
|
|
(env && !strcmp(env, "0")) ? "Using blit kernels for hipMemcpy" : "Using DMA copy engines");
|
|
}
|
|
printf("%-20s = %12d : %s\n", "USE_SINGLE_SYNC", useSingleSync,
|
|
useSingleSync ? "Synchronizing only once, after all iterations" : "Synchronizing per iteration");
|
|
printf("%-20s = %12d : Running in %s mode\n", "USE_INTERACTIVE", useInteractive,
|
|
useInteractive ? "interactive" : "non-interactive");
|
|
printf("%-20s = %12d : %s\n", "COMBINE_TIMING", combineTiming,
|
|
combineTiming ? "Using combined timing+launch" : "Using separate timing / launch");
|
|
printf("%-20s = %12d : %s\n", "SHOW_ADDR", showAddr,
|
|
showAddr ? "Displaying src/dst mem addresses" : "Not displaying src/dst mem addresses");
|
|
printf("%-20s = %12d : Output to %s\n", "OUTPUT_TO_CSV", outputToCsv,
|
|
outputToCsv ? "CSV" : "console");
|
|
printf("%-20s = %12d : Using byte offset of %d\n", "BYTE_OFFSET", byteOffset, byteOffset);
|
|
printf("%-20s = %12d : Running %d warmup iteration(s) per topology\n", "NUM_WARMUPS", numWarmups, numWarmups);
|
|
printf("%-20s = %12d : Running %d timed iteration(s) per topology\n", "NUM_ITERATIONS", numIterations, numIterations);
|
|
printf("%-20s = %12d : Using %d CPU thread(s) per CPU-based-copy Link\n", "NUM_CPU_PER_LINK", numCpuPerLink, numCpuPerLink);
|
|
printf("%-20s = %12s : ", "FILL_PATTERN", getenv("FILL_PATTERN") ? "(specified)" : "(unset)");
|
|
if (fillPattern.size())
|
|
{
|
|
printf("Pattern: %s", getenv("FILL_PATTERN"));
|
|
}
|
|
else
|
|
{
|
|
printf("Pseudo-random: (Element i = i modulo 383 + 31)");
|
|
}
|
|
printf("\n");
|
|
printf("%-20s = %12s : Using %d shared mem per threadblock\n", "SHARED_MEM_BYTES",
|
|
getenv("SHARED_MEM_BYTES") ? "(specified)" : "(unset)", sharedMemBytes);
|
|
printf("%-20s = %12d : Each CU gets a multiple of %d bytes to copy\n", "BLOCK_BYTES", blockBytes, blockBytes);
|
|
printf("\n");
|
|
}
|
|
};
|
|
|
|
private:
|
|
// Helper function that gets parses environment variable or sets to default value
|
|
int GetEnvVar(std::string const varname, int defaultValue)
|
|
{
|
|
if (getenv(varname.c_str()))
|
|
return atoi(getenv(varname.c_str()));
|
|
return defaultValue;
|
|
}
|
|
};
|
|
|
|
#endif
|