mirror of
https://github.com/NVIDIA/cuda-samples.git
synced 2026-10-25 22:28:17 +08:00
Apply consistent code formatting across the repo. Add clang-format and pre-commit hooks.
This commit is contained in:
@@ -29,9 +29,9 @@
|
||||
* This is a simple test showing performance and usability
|
||||
* improvements with large kernel parameters introduced in CUDA 12.1
|
||||
*/
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
// Utility includes
|
||||
#include <helper_cuda.h>
|
||||
@@ -40,134 +40,141 @@ using namespace std;
|
||||
using namespace std::chrono;
|
||||
|
||||
#define TEST_ITERATIONS (1000)
|
||||
#define TOTAL_PARAMS (8000) // ints
|
||||
#define KERNEL_PARAM_LIMIT (1024) // ints
|
||||
#define TOTAL_PARAMS (8000) // ints
|
||||
#define KERNEL_PARAM_LIMIT (1024) // ints
|
||||
#define CONST_COPIED_PARAMS (TOTAL_PARAMS - KERNEL_PARAM_LIMIT)
|
||||
|
||||
__constant__ int excess_params[CONST_COPIED_PARAMS];
|
||||
|
||||
typedef struct {
|
||||
int param[KERNEL_PARAM_LIMIT];
|
||||
typedef struct
|
||||
{
|
||||
int param[KERNEL_PARAM_LIMIT];
|
||||
} param_t;
|
||||
|
||||
typedef struct {
|
||||
int param[TOTAL_PARAMS];
|
||||
typedef struct
|
||||
{
|
||||
int param[TOTAL_PARAMS];
|
||||
} param_large_t;
|
||||
|
||||
// Kernel with 4KB kernel parameter limit
|
||||
__global__ void kernelDefault(__grid_constant__ const param_t p, int *result) {
|
||||
int tmp = 0;
|
||||
__global__ void kernelDefault(__grid_constant__ const param_t p, int *result)
|
||||
{
|
||||
int tmp = 0;
|
||||
|
||||
// accumulate kernel parameters
|
||||
for (int i = 0; i < KERNEL_PARAM_LIMIT; ++i) {
|
||||
tmp += p.param[i];
|
||||
}
|
||||
// accumulate kernel parameters
|
||||
for (int i = 0; i < KERNEL_PARAM_LIMIT; ++i) {
|
||||
tmp += p.param[i];
|
||||
}
|
||||
|
||||
// accumulate excess values passed via const memory
|
||||
for (int i = 0; i < CONST_COPIED_PARAMS; ++i) {
|
||||
tmp += excess_params[i];
|
||||
}
|
||||
// accumulate excess values passed via const memory
|
||||
for (int i = 0; i < CONST_COPIED_PARAMS; ++i) {
|
||||
tmp += excess_params[i];
|
||||
}
|
||||
|
||||
*result = tmp;
|
||||
*result = tmp;
|
||||
}
|
||||
|
||||
// Kernel with 32,764 byte kernel parameter limit
|
||||
__global__ void kernelLargeParam(__grid_constant__ const param_large_t p, int *result) {
|
||||
int tmp = 0;
|
||||
__global__ void kernelLargeParam(__grid_constant__ const param_large_t p, int *result)
|
||||
{
|
||||
int tmp = 0;
|
||||
|
||||
// accumulate kernel parameters
|
||||
for (int i = 0; i < TOTAL_PARAMS; ++i) {
|
||||
tmp += p.param[i];
|
||||
}
|
||||
// accumulate kernel parameters
|
||||
for (int i = 0; i < TOTAL_PARAMS; ++i) {
|
||||
tmp += p.param[i];
|
||||
}
|
||||
|
||||
*result = tmp;
|
||||
*result = tmp;
|
||||
}
|
||||
|
||||
static void report_time(std::chrono::time_point<std::chrono::steady_clock> start,
|
||||
std::chrono::time_point<std::chrono::steady_clock> end,
|
||||
int iters) {
|
||||
auto usecs = duration_cast<duration<float,
|
||||
microseconds::period>>(end - start);
|
||||
cout << usecs.count() / iters << endl;
|
||||
int iters)
|
||||
{
|
||||
auto usecs = duration_cast<duration<float, microseconds::period>>(end - start);
|
||||
cout << usecs.count() / iters << endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int rc;
|
||||
cudaFree(0);
|
||||
int main()
|
||||
{
|
||||
int rc;
|
||||
cudaFree(0);
|
||||
|
||||
param_t p;
|
||||
param_large_t p_large;
|
||||
param_t p;
|
||||
param_large_t p_large;
|
||||
|
||||
// pageable host memory that holds excess constants passed via constant memory
|
||||
int *copied_params = (int *)malloc(CONST_COPIED_PARAMS * sizeof(int));
|
||||
assert(copied_params);
|
||||
// pageable host memory that holds excess constants passed via constant memory
|
||||
int *copied_params = (int *)malloc(CONST_COPIED_PARAMS * sizeof(int));
|
||||
assert(copied_params);
|
||||
|
||||
// storage for computed result
|
||||
int *d_result;
|
||||
int h_result;
|
||||
checkCudaErrors(cudaMalloc(&d_result, sizeof(int)));
|
||||
// storage for computed result
|
||||
int *d_result;
|
||||
int h_result;
|
||||
checkCudaErrors(cudaMalloc(&d_result, sizeof(int)));
|
||||
|
||||
int expected_result = 0;
|
||||
int expected_result = 0;
|
||||
|
||||
// fill in data for validation
|
||||
for (int i = 0; i < KERNEL_PARAM_LIMIT; ++i) {
|
||||
p.param[i] = (i & 0xFF);
|
||||
}
|
||||
for (int i = KERNEL_PARAM_LIMIT; i < TOTAL_PARAMS; ++i) {
|
||||
copied_params[i - KERNEL_PARAM_LIMIT] = (i & 0xFF);
|
||||
}
|
||||
for (int i = 0; i < TOTAL_PARAMS; ++i) {
|
||||
p_large.param[i] = (i & 0xFF);
|
||||
expected_result += (i & 0xFF);
|
||||
}
|
||||
|
||||
// warmup, verify correctness
|
||||
checkCudaErrors(cudaMemcpyToSymbol(excess_params, copied_params, CONST_COPIED_PARAMS * sizeof(int), 0, cudaMemcpyHostToDevice));
|
||||
kernelDefault<<<1,1>>>(p, d_result);
|
||||
checkCudaErrors(cudaMemcpy(&h_result, d_result, sizeof(int), cudaMemcpyDeviceToHost));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
if(h_result != expected_result) {
|
||||
std::cout << "Test failed" << std::endl;
|
||||
rc=-1;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
kernelLargeParam<<<1,1>>>(p_large, d_result);
|
||||
checkCudaErrors(cudaMemcpy(&h_result, d_result, sizeof(int), cudaMemcpyDeviceToHost));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
if(h_result != expected_result) {
|
||||
std::cout << "Test failed" << std::endl;
|
||||
rc=-1;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
// benchmark default kernel parameter limit
|
||||
{
|
||||
auto start = steady_clock::now();
|
||||
for (int i = 0; i < TEST_ITERATIONS; ++i) {
|
||||
checkCudaErrors(cudaMemcpyToSymbol(excess_params, copied_params, CONST_COPIED_PARAMS * sizeof(int), 0, cudaMemcpyHostToDevice));
|
||||
kernelDefault<<<1, 1>>>(p, d_result);
|
||||
// fill in data for validation
|
||||
for (int i = 0; i < KERNEL_PARAM_LIMIT; ++i) {
|
||||
p.param[i] = (i & 0xFF);
|
||||
}
|
||||
for (int i = KERNEL_PARAM_LIMIT; i < TOTAL_PARAMS; ++i) {
|
||||
copied_params[i - KERNEL_PARAM_LIMIT] = (i & 0xFF);
|
||||
}
|
||||
for (int i = 0; i < TOTAL_PARAMS; ++i) {
|
||||
p_large.param[i] = (i & 0xFF);
|
||||
expected_result += (i & 0xFF);
|
||||
}
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
auto end = steady_clock::now();
|
||||
std::cout << "Kernel 4KB parameter limit - time (us):";
|
||||
report_time(start, end, TEST_ITERATIONS);
|
||||
|
||||
// benchmark large kernel parameter limit
|
||||
start = steady_clock::now();
|
||||
for (int i = 0; i < TEST_ITERATIONS; ++i) {
|
||||
kernelLargeParam<<<1, 1>>>(p_large, d_result);
|
||||
}
|
||||
// warmup, verify correctness
|
||||
checkCudaErrors(
|
||||
cudaMemcpyToSymbol(excess_params, copied_params, CONST_COPIED_PARAMS * sizeof(int), 0, cudaMemcpyHostToDevice));
|
||||
kernelDefault<<<1, 1>>>(p, d_result);
|
||||
checkCudaErrors(cudaMemcpy(&h_result, d_result, sizeof(int), cudaMemcpyDeviceToHost));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
end = steady_clock::now();
|
||||
std::cout << "Kernel 32,764 byte parameter limit - time (us):";
|
||||
report_time(start, end, TEST_ITERATIONS);
|
||||
}
|
||||
std::cout << "Test passed!" << std::endl;
|
||||
rc=0;
|
||||
if (h_result != expected_result) {
|
||||
std::cout << "Test failed" << std::endl;
|
||||
rc = -1;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
kernelLargeParam<<<1, 1>>>(p_large, d_result);
|
||||
checkCudaErrors(cudaMemcpy(&h_result, d_result, sizeof(int), cudaMemcpyDeviceToHost));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
if (h_result != expected_result) {
|
||||
std::cout << "Test failed" << std::endl;
|
||||
rc = -1;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
// benchmark default kernel parameter limit
|
||||
{
|
||||
auto start = steady_clock::now();
|
||||
for (int i = 0; i < TEST_ITERATIONS; ++i) {
|
||||
checkCudaErrors(cudaMemcpyToSymbol(
|
||||
excess_params, copied_params, CONST_COPIED_PARAMS * sizeof(int), 0, cudaMemcpyHostToDevice));
|
||||
kernelDefault<<<1, 1>>>(p, d_result);
|
||||
}
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
auto end = steady_clock::now();
|
||||
std::cout << "Kernel 4KB parameter limit - time (us):";
|
||||
report_time(start, end, TEST_ITERATIONS);
|
||||
|
||||
// benchmark large kernel parameter limit
|
||||
start = steady_clock::now();
|
||||
for (int i = 0; i < TEST_ITERATIONS; ++i) {
|
||||
kernelLargeParam<<<1, 1>>>(p_large, d_result);
|
||||
}
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
end = steady_clock::now();
|
||||
std::cout << "Kernel 32,764 byte parameter limit - time (us):";
|
||||
report_time(start, end, TEST_ITERATIONS);
|
||||
}
|
||||
std::cout << "Test passed!" << std::endl;
|
||||
rc = 0;
|
||||
Exit:
|
||||
// cleanup
|
||||
cudaFree(d_result);
|
||||
free(copied_params);
|
||||
return rc;
|
||||
// cleanup
|
||||
cudaFree(d_result);
|
||||
free(copied_params);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -33,33 +33,28 @@
|
||||
#define ONE_MB (ONE_KB * ONE_KB)
|
||||
|
||||
extern size_t maxSampleSizeInMb;
|
||||
extern int numKernelRuns;
|
||||
extern int verboseResults;
|
||||
extern int numKernelRuns;
|
||||
extern int verboseResults;
|
||||
|
||||
extern unsigned int findNumSizesToTest(unsigned int minSize,
|
||||
unsigned int maxSize,
|
||||
unsigned int multiplier);
|
||||
extern unsigned int findNumSizesToTest(unsigned int minSize, unsigned int maxSize, unsigned int multiplier);
|
||||
|
||||
// For Tracking the different memory allocation types
|
||||
typedef enum memAllocType_enum {
|
||||
MEMALLOC_TYPE_START,
|
||||
USE_MANAGED_MEMORY_WITH_HINTS = MEMALLOC_TYPE_START,
|
||||
USE_MANAGED_MEMORY_WITH_HINTS_ASYNC,
|
||||
USE_MANAGED_MEMORY,
|
||||
USE_ZERO_COPY,
|
||||
USE_HOST_PAGEABLE_AND_DEVICE_MEMORY,
|
||||
USE_HOST_PAGEABLE_AND_DEVICE_MEMORY_ASYNC,
|
||||
USE_HOST_PAGELOCKED_AND_DEVICE_MEMORY,
|
||||
USE_HOST_PAGELOCKED_AND_DEVICE_MEMORY_ASYNC,
|
||||
MEMALLOC_TYPE_END = USE_HOST_PAGELOCKED_AND_DEVICE_MEMORY_ASYNC,
|
||||
MEMALLOC_TYPE_INVALID,
|
||||
MEMALLOC_TYPE_COUNT = MEMALLOC_TYPE_INVALID
|
||||
MEMALLOC_TYPE_START,
|
||||
USE_MANAGED_MEMORY_WITH_HINTS = MEMALLOC_TYPE_START,
|
||||
USE_MANAGED_MEMORY_WITH_HINTS_ASYNC,
|
||||
USE_MANAGED_MEMORY,
|
||||
USE_ZERO_COPY,
|
||||
USE_HOST_PAGEABLE_AND_DEVICE_MEMORY,
|
||||
USE_HOST_PAGEABLE_AND_DEVICE_MEMORY_ASYNC,
|
||||
USE_HOST_PAGELOCKED_AND_DEVICE_MEMORY,
|
||||
USE_HOST_PAGELOCKED_AND_DEVICE_MEMORY_ASYNC,
|
||||
MEMALLOC_TYPE_END = USE_HOST_PAGELOCKED_AND_DEVICE_MEMORY_ASYNC,
|
||||
MEMALLOC_TYPE_INVALID,
|
||||
MEMALLOC_TYPE_COUNT = MEMALLOC_TYPE_INVALID
|
||||
} MemAllocType;
|
||||
|
||||
typedef enum bandwidthType_enum {
|
||||
READ_BANDWIDTH,
|
||||
WRITE_BANDWIDTH
|
||||
} BandwidthType;
|
||||
typedef enum bandwidthType_enum { READ_BANDWIDTH, WRITE_BANDWIDTH } BandwidthType;
|
||||
|
||||
extern const char *memAllocTypeStr[];
|
||||
extern const char *memAllocTypeShortStr[];
|
||||
@@ -67,22 +62,20 @@ extern const char *memAllocTypeShortStr[];
|
||||
struct resultsData;
|
||||
struct testResults;
|
||||
|
||||
void createAndInitTestResults(struct testResults **results,
|
||||
const char *testName,
|
||||
unsigned int numMeasurements,
|
||||
unsigned int numSizesToTest);
|
||||
void createAndInitTestResults(struct testResults **results,
|
||||
const char *testName,
|
||||
unsigned int numMeasurements,
|
||||
unsigned int numSizesToTest);
|
||||
unsigned long *getPtrSizesToTest(struct testResults *results);
|
||||
|
||||
void freeTestResultsAndAllResultsData(struct testResults *results);
|
||||
|
||||
void createResultDataAndAddToTestResults(struct resultsData **ptrData,
|
||||
struct testResults *results,
|
||||
const char *resultsName,
|
||||
bool printOnlyInVerbose,
|
||||
bool reportAsBandwidth);
|
||||
double *getPtrRunTimesInMs(struct resultsData *data, int allocType,
|
||||
int sizeIndex);
|
||||
void createResultDataAndAddToTestResults(struct resultsData **ptrData,
|
||||
struct testResults *results,
|
||||
const char *resultsName,
|
||||
bool printOnlyInVerbose,
|
||||
bool reportAsBandwidth);
|
||||
double *getPtrRunTimesInMs(struct resultsData *data, int allocType, int sizeIndex);
|
||||
|
||||
void printResults(struct testResults *results,
|
||||
bool print_launch_transfer_results, bool print_std_deviation);
|
||||
void printResults(struct testResults *results, bool print_launch_transfer_results, bool print_std_deviation);
|
||||
#endif
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
|
||||
#include "commonKernels.hpp"
|
||||
|
||||
__global__ void spinWhileLessThanOne(volatile unsigned int *latch) {
|
||||
while (latch[0] < 1)
|
||||
;
|
||||
__global__ void spinWhileLessThanOne(volatile unsigned int *latch)
|
||||
{
|
||||
while (latch[0] < 1)
|
||||
;
|
||||
}
|
||||
|
||||
@@ -27,277 +27,267 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "commonDefs.hpp"
|
||||
#define CU_INIT_UUID
|
||||
#include <cmath>
|
||||
|
||||
#define UNITS_Time "ms"
|
||||
#define UNITS_BW "MB/s"
|
||||
#define KB_str "KB"
|
||||
#define MB_str "MB"
|
||||
#define UNITS_BW "MB/s"
|
||||
#define KB_str "KB"
|
||||
#define MB_str "MB"
|
||||
|
||||
struct resultsData {
|
||||
char resultsName[64];
|
||||
struct testResults *results;
|
||||
// this has MEMALLOC_TYPE_COUNT * results->numSizesToTest *
|
||||
// results->numMeasurements elements
|
||||
double **runTimesInMs[MEMALLOC_TYPE_COUNT];
|
||||
double *averageRunTimesInMs[MEMALLOC_TYPE_COUNT];
|
||||
double *stdDevRunTimesInMs[MEMALLOC_TYPE_COUNT];
|
||||
double *stdDevBandwidthInMBps[MEMALLOC_TYPE_COUNT];
|
||||
bool printOnlyInVerbose;
|
||||
bool reportAsBandwidth;
|
||||
struct resultsData *next;
|
||||
struct resultsData
|
||||
{
|
||||
char resultsName[64];
|
||||
struct testResults *results;
|
||||
// this has MEMALLOC_TYPE_COUNT * results->numSizesToTest *
|
||||
// results->numMeasurements elements
|
||||
double **runTimesInMs[MEMALLOC_TYPE_COUNT];
|
||||
double *averageRunTimesInMs[MEMALLOC_TYPE_COUNT];
|
||||
double *stdDevRunTimesInMs[MEMALLOC_TYPE_COUNT];
|
||||
double *stdDevBandwidthInMBps[MEMALLOC_TYPE_COUNT];
|
||||
bool printOnlyInVerbose;
|
||||
bool reportAsBandwidth;
|
||||
struct resultsData *next;
|
||||
};
|
||||
|
||||
struct testResults {
|
||||
char testName[64];
|
||||
unsigned int numMeasurements;
|
||||
unsigned long *sizesToTest;
|
||||
unsigned int numSizesToTest;
|
||||
struct resultsData *resultsDataHead;
|
||||
struct resultsData *resultsDataTail;
|
||||
struct testResults
|
||||
{
|
||||
char testName[64];
|
||||
unsigned int numMeasurements;
|
||||
unsigned long *sizesToTest;
|
||||
unsigned int numSizesToTest;
|
||||
struct resultsData *resultsDataHead;
|
||||
struct resultsData *resultsDataTail;
|
||||
};
|
||||
|
||||
unsigned int findNumSizesToTest(unsigned int minSize, unsigned int maxSize,
|
||||
unsigned int multiplier) {
|
||||
unsigned int numSizesToTest = 0;
|
||||
while (minSize <= maxSize) {
|
||||
numSizesToTest++;
|
||||
minSize *= multiplier;
|
||||
}
|
||||
return numSizesToTest;
|
||||
unsigned int findNumSizesToTest(unsigned int minSize, unsigned int maxSize, unsigned int multiplier)
|
||||
{
|
||||
unsigned int numSizesToTest = 0;
|
||||
while (minSize <= maxSize) {
|
||||
numSizesToTest++;
|
||||
minSize *= multiplier;
|
||||
}
|
||||
return numSizesToTest;
|
||||
}
|
||||
|
||||
int compareDoubles(const void *ptr1, const void *ptr2) {
|
||||
return (*(double *)ptr1 > *(double *)ptr2) ? 1 : -1;
|
||||
}
|
||||
int compareDoubles(const void *ptr1, const void *ptr2) { return (*(double *)ptr1 > *(double *)ptr2) ? 1 : -1; }
|
||||
|
||||
static inline double getTimeOrBandwidth(double runTimeInMs, unsigned long size,
|
||||
bool getBandwidth) {
|
||||
return (getBandwidth) ? (1000 * (size / runTimeInMs)) / ONE_MB : runTimeInMs;
|
||||
static inline double getTimeOrBandwidth(double runTimeInMs, unsigned long size, bool getBandwidth)
|
||||
{
|
||||
return (getBandwidth) ? (1000 * (size / runTimeInMs)) / ONE_MB : runTimeInMs;
|
||||
}
|
||||
|
||||
void createAndInitTestResults(struct testResults **ptrResults,
|
||||
const char *testName,
|
||||
unsigned int numMeasurements,
|
||||
unsigned int numSizesToTest) {
|
||||
unsigned int i;
|
||||
struct testResults *results;
|
||||
results = (struct testResults *)malloc(sizeof(struct testResults));
|
||||
memset(results, 0, sizeof(struct testResults));
|
||||
strcpy(results->testName, testName);
|
||||
results->numMeasurements = numMeasurements;
|
||||
results->numSizesToTest = numSizesToTest;
|
||||
results->sizesToTest =
|
||||
(unsigned long *)malloc(numSizesToTest * sizeof(unsigned long));
|
||||
results->resultsDataHead = NULL;
|
||||
results->resultsDataTail = NULL;
|
||||
const char *testName,
|
||||
unsigned int numMeasurements,
|
||||
unsigned int numSizesToTest)
|
||||
{
|
||||
unsigned int i;
|
||||
struct testResults *results;
|
||||
results = (struct testResults *)malloc(sizeof(struct testResults));
|
||||
memset(results, 0, sizeof(struct testResults));
|
||||
strcpy(results->testName, testName);
|
||||
results->numMeasurements = numMeasurements;
|
||||
results->numSizesToTest = numSizesToTest;
|
||||
results->sizesToTest = (unsigned long *)malloc(numSizesToTest * sizeof(unsigned long));
|
||||
results->resultsDataHead = NULL;
|
||||
results->resultsDataTail = NULL;
|
||||
|
||||
*ptrResults = results;
|
||||
*ptrResults = results;
|
||||
}
|
||||
|
||||
unsigned long *getPtrSizesToTest(struct testResults *results) {
|
||||
return results->sizesToTest;
|
||||
}
|
||||
unsigned long *getPtrSizesToTest(struct testResults *results) { return results->sizesToTest; }
|
||||
|
||||
void createResultDataAndAddToTestResults(struct resultsData **ptrData,
|
||||
struct testResults *results,
|
||||
const char *resultsName,
|
||||
bool printOnlyInVerbose,
|
||||
bool reportAsBandwidth) {
|
||||
unsigned int i, j;
|
||||
struct resultsData *data;
|
||||
data = (struct resultsData *)malloc(sizeof(struct resultsData));
|
||||
memset(data, 0, sizeof(struct resultsData));
|
||||
strcpy(data->resultsName, resultsName);
|
||||
data->results = results;
|
||||
for (i = 0; i < MEMALLOC_TYPE_COUNT; i++) {
|
||||
data->runTimesInMs[i] =
|
||||
(double **)malloc(results->numSizesToTest * sizeof(double *));
|
||||
for (j = 0; j < results->numSizesToTest; j++) {
|
||||
data->runTimesInMs[i][j] =
|
||||
(double *)malloc(results->numMeasurements * sizeof(double));
|
||||
}
|
||||
data->averageRunTimesInMs[i] =
|
||||
(double *)malloc(results->numSizesToTest * sizeof(double));
|
||||
data->stdDevRunTimesInMs[i] =
|
||||
(double *)malloc(results->numSizesToTest * sizeof(double));
|
||||
data->stdDevBandwidthInMBps[i] =
|
||||
(double *)malloc(results->numSizesToTest * sizeof(double));
|
||||
}
|
||||
data->printOnlyInVerbose = printOnlyInVerbose;
|
||||
data->reportAsBandwidth = reportAsBandwidth;
|
||||
data->next = NULL;
|
||||
*ptrData = data;
|
||||
if (results->resultsDataHead == NULL) {
|
||||
results->resultsDataHead = data;
|
||||
results->resultsDataTail = data;
|
||||
} else {
|
||||
results->resultsDataTail->next = data;
|
||||
results->resultsDataTail = data;
|
||||
}
|
||||
}
|
||||
|
||||
double *getPtrRunTimesInMs(struct resultsData *data, int allocType,
|
||||
int sizeIndex) {
|
||||
return data->runTimesInMs[allocType][sizeIndex];
|
||||
}
|
||||
|
||||
void freeTestResultsAndAllResultsData(struct testResults *results) {
|
||||
struct resultsData *data, *dataToFree;
|
||||
unsigned int i, j;
|
||||
for (data = results->resultsDataHead; data != NULL;) {
|
||||
struct testResults *results,
|
||||
const char *resultsName,
|
||||
bool printOnlyInVerbose,
|
||||
bool reportAsBandwidth)
|
||||
{
|
||||
unsigned int i, j;
|
||||
struct resultsData *data;
|
||||
data = (struct resultsData *)malloc(sizeof(struct resultsData));
|
||||
memset(data, 0, sizeof(struct resultsData));
|
||||
strcpy(data->resultsName, resultsName);
|
||||
data->results = results;
|
||||
for (i = 0; i < MEMALLOC_TYPE_COUNT; i++) {
|
||||
for (j = 0; j < results->numSizesToTest; j++) {
|
||||
free(data->runTimesInMs[i][j]);
|
||||
}
|
||||
free(data->runTimesInMs[i]);
|
||||
free(data->averageRunTimesInMs[i]);
|
||||
free(data->stdDevRunTimesInMs[i]);
|
||||
free(data->stdDevBandwidthInMBps[i]);
|
||||
}
|
||||
dataToFree = data;
|
||||
data = data->next;
|
||||
free(dataToFree);
|
||||
}
|
||||
free(results->sizesToTest);
|
||||
free(results);
|
||||
}
|
||||
|
||||
void calculateAverageAndStdDev(double *pAverage, double *pStdDev,
|
||||
double *allResults, unsigned int count) {
|
||||
unsigned int i;
|
||||
double average = 0.0;
|
||||
double stdDev = 0.0;
|
||||
for (i = 0; i < count; i++) {
|
||||
average += allResults[i];
|
||||
}
|
||||
average /= count;
|
||||
for (i = 0; i < count; i++) {
|
||||
stdDev += (allResults[i] - average) * (allResults[i] - average);
|
||||
}
|
||||
stdDev /= count;
|
||||
stdDev = sqrt(stdDev);
|
||||
*pAverage = average;
|
||||
*pStdDev = (average == 0.0) ? 0.0 : ((100.0 * stdDev) / average);
|
||||
}
|
||||
|
||||
void calculateStdDevBandwidth(double *pStdDev, double *allResults,
|
||||
unsigned int count, unsigned long size) {
|
||||
unsigned int i;
|
||||
double bandwidth;
|
||||
double average = 0.0;
|
||||
double stdDev = 0.0;
|
||||
for (i = 0; i < count; i++) {
|
||||
bandwidth = (1000 * (size / allResults[i])) / ONE_MB;
|
||||
average += bandwidth;
|
||||
}
|
||||
average /= count;
|
||||
for (i = 0; i < count; i++) {
|
||||
bandwidth = (1000 * (size / allResults[i])) / ONE_MB;
|
||||
stdDev += (bandwidth - average) * (bandwidth - average);
|
||||
}
|
||||
stdDev /= count;
|
||||
stdDev = sqrt(stdDev);
|
||||
*pStdDev = (average == 0.0) ? 0.0 : ((100.0 * stdDev) / average);
|
||||
}
|
||||
|
||||
void printTimesInTableFormat(struct testResults *results,
|
||||
struct resultsData *data, bool printAverage,
|
||||
bool printStdDev) {
|
||||
unsigned int i, j;
|
||||
bool printStdDevBandwidth = printStdDev && data->reportAsBandwidth;
|
||||
printf("Size_KB");
|
||||
for (i = 0; i < MEMALLOC_TYPE_COUNT; i++) {
|
||||
printf("\t%7s", memAllocTypeShortStr[i]);
|
||||
}
|
||||
printf("\n");
|
||||
for (j = 0; j < results->numSizesToTest; j++) {
|
||||
printf("%lu", results->sizesToTest[j] / ONE_KB);
|
||||
for (i = 0; i < MEMALLOC_TYPE_COUNT; i++) {
|
||||
printf(data->reportAsBandwidth ? "\t%7.2lf" : "\t%7.3lf",
|
||||
printStdDevBandwidth
|
||||
? data->stdDevBandwidthInMBps[i][j]
|
||||
: getTimeOrBandwidth(
|
||||
printAverage ? data->averageRunTimesInMs[i][j]
|
||||
: data->stdDevRunTimesInMs[i][j],
|
||||
results->sizesToTest[j], data->reportAsBandwidth));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void printAllResultsInVerboseMode(struct testResults *results,
|
||||
struct resultsData *data) {
|
||||
unsigned int i, j, k;
|
||||
for (i = 0; i < MEMALLOC_TYPE_COUNT; i++) {
|
||||
printf("Verbose mode, printing all results for %s\n", memAllocTypeStr[i]);
|
||||
printf("Instance");
|
||||
for (j = 0; j < results->numSizesToTest; j++) {
|
||||
printf("\t%lu", results->sizesToTest[j] / ONE_KB);
|
||||
}
|
||||
printf("\n");
|
||||
for (k = 0; k < results->numMeasurements; k++) {
|
||||
printf("%u", k);
|
||||
for (j = 0; j < results->numSizesToTest; j++) {
|
||||
printf(data->reportAsBandwidth ? "\t%7.2lf" : "\t%7.3lf",
|
||||
getTimeOrBandwidth(data->runTimesInMs[i][j][k],
|
||||
results->sizesToTest[j],
|
||||
data->reportAsBandwidth));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void printResults(struct testResults *results,
|
||||
bool print_launch_transfer_results,
|
||||
bool print_std_deviation) {
|
||||
char vulcanPrint[256];
|
||||
char resultNameNoSpaces[64];
|
||||
unsigned int i, j, k;
|
||||
struct resultsData *resultsIter;
|
||||
bool sizeGreaterThan1MB;
|
||||
for (resultsIter = results->resultsDataHead; resultsIter != NULL;
|
||||
resultsIter = resultsIter->next) {
|
||||
if (!verboseResults && resultsIter->printOnlyInVerbose) {
|
||||
continue;
|
||||
}
|
||||
if (!print_launch_transfer_results) {
|
||||
if (!(strcmp(resultsIter->resultsName, "Overall Time") == 0)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// regular print
|
||||
printf("\n%s For %s ", resultsIter->resultsName, results->testName);
|
||||
printf("\n");
|
||||
for (j = 0; j < results->numSizesToTest; j++) {
|
||||
for (i = 0; i < MEMALLOC_TYPE_COUNT; i++) {
|
||||
calculateAverageAndStdDev(&resultsIter->averageRunTimesInMs[i][j],
|
||||
&resultsIter->stdDevRunTimesInMs[i][j],
|
||||
resultsIter->runTimesInMs[i][j],
|
||||
results->numMeasurements);
|
||||
if (resultsIter->reportAsBandwidth) {
|
||||
calculateStdDevBandwidth(&resultsIter->stdDevBandwidthInMBps[i][j],
|
||||
resultsIter->runTimesInMs[i][j],
|
||||
results->numMeasurements,
|
||||
results->sizesToTest[j]);
|
||||
data->runTimesInMs[i] = (double **)malloc(results->numSizesToTest * sizeof(double *));
|
||||
for (j = 0; j < results->numSizesToTest; j++) {
|
||||
data->runTimesInMs[i][j] = (double *)malloc(results->numMeasurements * sizeof(double));
|
||||
}
|
||||
}
|
||||
data->averageRunTimesInMs[i] = (double *)malloc(results->numSizesToTest * sizeof(double));
|
||||
data->stdDevRunTimesInMs[i] = (double *)malloc(results->numSizesToTest * sizeof(double));
|
||||
data->stdDevBandwidthInMBps[i] = (double *)malloc(results->numSizesToTest * sizeof(double));
|
||||
}
|
||||
printf("\nPrinting Average of %u measurements in (%s)\n",
|
||||
results->numMeasurements,
|
||||
resultsIter->reportAsBandwidth ? UNITS_BW : UNITS_Time);
|
||||
printTimesInTableFormat(results, resultsIter, true, false);
|
||||
if (print_std_deviation) {
|
||||
printf(
|
||||
"\nPrinting Standard Deviation as %% of average of %u measurements\n",
|
||||
results->numMeasurements);
|
||||
printTimesInTableFormat(results, resultsIter, false, true);
|
||||
data->printOnlyInVerbose = printOnlyInVerbose;
|
||||
data->reportAsBandwidth = reportAsBandwidth;
|
||||
data->next = NULL;
|
||||
*ptrData = data;
|
||||
if (results->resultsDataHead == NULL) {
|
||||
results->resultsDataHead = data;
|
||||
results->resultsDataTail = data;
|
||||
}
|
||||
if (verboseResults) {
|
||||
printAllResultsInVerboseMode(results, resultsIter);
|
||||
else {
|
||||
results->resultsDataTail->next = data;
|
||||
results->resultsDataTail = data;
|
||||
}
|
||||
}
|
||||
|
||||
double *getPtrRunTimesInMs(struct resultsData *data, int allocType, int sizeIndex)
|
||||
{
|
||||
return data->runTimesInMs[allocType][sizeIndex];
|
||||
}
|
||||
|
||||
void freeTestResultsAndAllResultsData(struct testResults *results)
|
||||
{
|
||||
struct resultsData *data, *dataToFree;
|
||||
unsigned int i, j;
|
||||
for (data = results->resultsDataHead; data != NULL;) {
|
||||
for (i = 0; i < MEMALLOC_TYPE_COUNT; i++) {
|
||||
for (j = 0; j < results->numSizesToTest; j++) {
|
||||
free(data->runTimesInMs[i][j]);
|
||||
}
|
||||
free(data->runTimesInMs[i]);
|
||||
free(data->averageRunTimesInMs[i]);
|
||||
free(data->stdDevRunTimesInMs[i]);
|
||||
free(data->stdDevBandwidthInMBps[i]);
|
||||
}
|
||||
dataToFree = data;
|
||||
data = data->next;
|
||||
free(dataToFree);
|
||||
}
|
||||
free(results->sizesToTest);
|
||||
free(results);
|
||||
}
|
||||
|
||||
void calculateAverageAndStdDev(double *pAverage, double *pStdDev, double *allResults, unsigned int count)
|
||||
{
|
||||
unsigned int i;
|
||||
double average = 0.0;
|
||||
double stdDev = 0.0;
|
||||
for (i = 0; i < count; i++) {
|
||||
average += allResults[i];
|
||||
}
|
||||
average /= count;
|
||||
for (i = 0; i < count; i++) {
|
||||
stdDev += (allResults[i] - average) * (allResults[i] - average);
|
||||
}
|
||||
stdDev /= count;
|
||||
stdDev = sqrt(stdDev);
|
||||
*pAverage = average;
|
||||
*pStdDev = (average == 0.0) ? 0.0 : ((100.0 * stdDev) / average);
|
||||
}
|
||||
|
||||
void calculateStdDevBandwidth(double *pStdDev, double *allResults, unsigned int count, unsigned long size)
|
||||
{
|
||||
unsigned int i;
|
||||
double bandwidth;
|
||||
double average = 0.0;
|
||||
double stdDev = 0.0;
|
||||
for (i = 0; i < count; i++) {
|
||||
bandwidth = (1000 * (size / allResults[i])) / ONE_MB;
|
||||
average += bandwidth;
|
||||
}
|
||||
average /= count;
|
||||
for (i = 0; i < count; i++) {
|
||||
bandwidth = (1000 * (size / allResults[i])) / ONE_MB;
|
||||
stdDev += (bandwidth - average) * (bandwidth - average);
|
||||
}
|
||||
stdDev /= count;
|
||||
stdDev = sqrt(stdDev);
|
||||
*pStdDev = (average == 0.0) ? 0.0 : ((100.0 * stdDev) / average);
|
||||
}
|
||||
|
||||
void printTimesInTableFormat(struct testResults *results, struct resultsData *data, bool printAverage, bool printStdDev)
|
||||
{
|
||||
unsigned int i, j;
|
||||
bool printStdDevBandwidth = printStdDev && data->reportAsBandwidth;
|
||||
printf("Size_KB");
|
||||
for (i = 0; i < MEMALLOC_TYPE_COUNT; i++) {
|
||||
printf("\t%7s", memAllocTypeShortStr[i]);
|
||||
}
|
||||
printf("\n");
|
||||
for (j = 0; j < results->numSizesToTest; j++) {
|
||||
printf("%lu", results->sizesToTest[j] / ONE_KB);
|
||||
for (i = 0; i < MEMALLOC_TYPE_COUNT; i++) {
|
||||
printf(data->reportAsBandwidth ? "\t%7.2lf" : "\t%7.3lf",
|
||||
printStdDevBandwidth ? data->stdDevBandwidthInMBps[i][j]
|
||||
: getTimeOrBandwidth(printAverage ? data->averageRunTimesInMs[i][j]
|
||||
: data->stdDevRunTimesInMs[i][j],
|
||||
results->sizesToTest[j],
|
||||
data->reportAsBandwidth));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void printAllResultsInVerboseMode(struct testResults *results, struct resultsData *data)
|
||||
{
|
||||
unsigned int i, j, k;
|
||||
for (i = 0; i < MEMALLOC_TYPE_COUNT; i++) {
|
||||
printf("Verbose mode, printing all results for %s\n", memAllocTypeStr[i]);
|
||||
printf("Instance");
|
||||
for (j = 0; j < results->numSizesToTest; j++) {
|
||||
printf("\t%lu", results->sizesToTest[j] / ONE_KB);
|
||||
}
|
||||
printf("\n");
|
||||
for (k = 0; k < results->numMeasurements; k++) {
|
||||
printf("%u", k);
|
||||
for (j = 0; j < results->numSizesToTest; j++) {
|
||||
printf(
|
||||
data->reportAsBandwidth ? "\t%7.2lf" : "\t%7.3lf",
|
||||
getTimeOrBandwidth(data->runTimesInMs[i][j][k], results->sizesToTest[j], data->reportAsBandwidth));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void printResults(struct testResults *results, bool print_launch_transfer_results, bool print_std_deviation)
|
||||
{
|
||||
char vulcanPrint[256];
|
||||
char resultNameNoSpaces[64];
|
||||
unsigned int i, j, k;
|
||||
struct resultsData *resultsIter;
|
||||
bool sizeGreaterThan1MB;
|
||||
for (resultsIter = results->resultsDataHead; resultsIter != NULL; resultsIter = resultsIter->next) {
|
||||
if (!verboseResults && resultsIter->printOnlyInVerbose) {
|
||||
continue;
|
||||
}
|
||||
if (!print_launch_transfer_results) {
|
||||
if (!(strcmp(resultsIter->resultsName, "Overall Time") == 0)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// regular print
|
||||
printf("\n%s For %s ", resultsIter->resultsName, results->testName);
|
||||
printf("\n");
|
||||
for (j = 0; j < results->numSizesToTest; j++) {
|
||||
for (i = 0; i < MEMALLOC_TYPE_COUNT; i++) {
|
||||
calculateAverageAndStdDev(&resultsIter->averageRunTimesInMs[i][j],
|
||||
&resultsIter->stdDevRunTimesInMs[i][j],
|
||||
resultsIter->runTimesInMs[i][j],
|
||||
results->numMeasurements);
|
||||
if (resultsIter->reportAsBandwidth) {
|
||||
calculateStdDevBandwidth(&resultsIter->stdDevBandwidthInMBps[i][j],
|
||||
resultsIter->runTimesInMs[i][j],
|
||||
results->numMeasurements,
|
||||
results->sizesToTest[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("\nPrinting Average of %u measurements in (%s)\n",
|
||||
results->numMeasurements,
|
||||
resultsIter->reportAsBandwidth ? UNITS_BW : UNITS_Time);
|
||||
printTimesInTableFormat(results, resultsIter, true, false);
|
||||
if (print_std_deviation) {
|
||||
printf("\nPrinting Standard Deviation as %% of average of %u measurements\n", results->numMeasurements);
|
||||
printTimesInTableFormat(results, resultsIter, false, true);
|
||||
}
|
||||
if (verboseResults) {
|
||||
printAllResultsInVerboseMode(results, resultsIter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -41,8 +41,8 @@
|
||||
#include <string.h>
|
||||
|
||||
// includes, project
|
||||
#include <helper_cuda.h> // helper functions for CUDA error checking and initialization
|
||||
#include <helper_functions.h> // helper utility functions
|
||||
#include <helper_cuda.h> // helper functions for CUDA error checking and initialization
|
||||
#include <helper_functions.h> // helper utility functions
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Misaligned types
|
||||
@@ -51,46 +51,50 @@ typedef unsigned char uint8;
|
||||
|
||||
typedef unsigned short int uint16;
|
||||
|
||||
typedef struct {
|
||||
unsigned char r, g, b, a;
|
||||
typedef struct
|
||||
{
|
||||
unsigned char r, g, b, a;
|
||||
} RGBA8_misaligned;
|
||||
|
||||
typedef struct {
|
||||
unsigned int l, a;
|
||||
typedef struct
|
||||
{
|
||||
unsigned int l, a;
|
||||
} LA32_misaligned;
|
||||
|
||||
typedef struct {
|
||||
unsigned int r, g, b;
|
||||
typedef struct
|
||||
{
|
||||
unsigned int r, g, b;
|
||||
} RGB32_misaligned;
|
||||
|
||||
typedef struct {
|
||||
unsigned int r, g, b, a;
|
||||
typedef struct
|
||||
{
|
||||
unsigned int r, g, b, a;
|
||||
} RGBA32_misaligned;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Aligned types
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct __align__(4) {
|
||||
unsigned char r, g, b, a;
|
||||
}
|
||||
RGBA8;
|
||||
typedef struct __align__(4)
|
||||
{
|
||||
unsigned char r, g, b, a;
|
||||
} RGBA8;
|
||||
|
||||
typedef unsigned int I32;
|
||||
|
||||
typedef struct __align__(8) {
|
||||
unsigned int l, a;
|
||||
}
|
||||
LA32;
|
||||
typedef struct __align__(8)
|
||||
{
|
||||
unsigned int l, a;
|
||||
} LA32;
|
||||
|
||||
typedef struct __align__(16) {
|
||||
unsigned int r, g, b;
|
||||
}
|
||||
RGB32;
|
||||
typedef struct __align__(16)
|
||||
{
|
||||
unsigned int r, g, b;
|
||||
} RGB32;
|
||||
|
||||
typedef struct __align__(16) {
|
||||
unsigned int r, g, b, a;
|
||||
}
|
||||
RGBA32;
|
||||
typedef struct __align__(16)
|
||||
{
|
||||
unsigned int r, g, b, a;
|
||||
} RGBA32;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Because G80 class hardware natively supports global memory operations
|
||||
@@ -101,10 +105,10 @@ RGBA32;
|
||||
// "Structure of arrays" storage strategy offers best performance
|
||||
// in general case. See section 5.1.2 of the Programming Guide.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct __align__(16) {
|
||||
RGBA32 c1, c2;
|
||||
}
|
||||
RGBA32_2;
|
||||
typedef struct __align__(16)
|
||||
{
|
||||
RGBA32 c1, c2;
|
||||
} RGBA32_2;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Common host and device functions
|
||||
@@ -126,14 +130,14 @@ int iAlignDown(int a, int b) { return a - a % b; }
|
||||
// Copy is carried out on per-element basis,
|
||||
// so it's not per-byte in case of padded structures.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <class TData>
|
||||
__global__ void testKernel(TData *d_odata, TData *d_idata, int numElements) {
|
||||
const int tid = blockDim.x * blockIdx.x + threadIdx.x;
|
||||
const int numThreads = blockDim.x * gridDim.x;
|
||||
template <class TData> __global__ void testKernel(TData *d_odata, TData *d_idata, int numElements)
|
||||
{
|
||||
const int tid = blockDim.x * blockIdx.x + threadIdx.x;
|
||||
const int numThreads = blockDim.x * gridDim.x;
|
||||
|
||||
for (int pos = tid; pos < numElements; pos += numThreads) {
|
||||
d_odata[pos] = d_idata[pos];
|
||||
}
|
||||
for (int pos = tid; pos < numElements; pos += numThreads) {
|
||||
d_odata[pos] = d_idata[pos];
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -144,171 +148,166 @@ __global__ void testKernel(TData *d_odata, TData *d_idata, int numElements) {
|
||||
// is undefined, since padding is merely a placeholder
|
||||
// and doesn't contain any user data.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <class TData>
|
||||
int testCPU(TData *h_odata, TData *h_idata, int numElements,
|
||||
int packedElementSize) {
|
||||
for (int pos = 0; pos < numElements; pos++) {
|
||||
TData src = h_idata[pos];
|
||||
TData dst = h_odata[pos];
|
||||
template <class TData> int testCPU(TData *h_odata, TData *h_idata, int numElements, int packedElementSize)
|
||||
{
|
||||
for (int pos = 0; pos < numElements; pos++) {
|
||||
TData src = h_idata[pos];
|
||||
TData dst = h_odata[pos];
|
||||
|
||||
for (int i = 0; i < packedElementSize; i++)
|
||||
if (((char *)&src)[i] != ((char *)&dst)[i]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < packedElementSize; i++)
|
||||
if (((char *)&src)[i] != ((char *)&dst)[i]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Data configuration
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Memory chunk size in bytes. Reused for test
|
||||
const int MEM_SIZE = 50000000;
|
||||
const int MEM_SIZE = 50000000;
|
||||
const int NUM_ITERATIONS = 32;
|
||||
|
||||
// GPU input and output data
|
||||
unsigned char *d_idata, *d_odata;
|
||||
// CPU input data and instance of GPU output data
|
||||
unsigned char *h_idataCPU, *h_odataGPU;
|
||||
unsigned char *h_idataCPU, *h_odataGPU;
|
||||
StopWatchInterface *hTimer = NULL;
|
||||
|
||||
template <class TData>
|
||||
int runTest(int packedElementSize, int memory_size) {
|
||||
const int totalMemSizeAligned = iAlignDown(memory_size, sizeof(TData));
|
||||
const int numElements = iDivDown(memory_size, sizeof(TData));
|
||||
template <class TData> int runTest(int packedElementSize, int memory_size)
|
||||
{
|
||||
const int totalMemSizeAligned = iAlignDown(memory_size, sizeof(TData));
|
||||
const int numElements = iDivDown(memory_size, sizeof(TData));
|
||||
|
||||
// Clean output buffer before current test
|
||||
checkCudaErrors(cudaMemset(d_odata, 0, memory_size));
|
||||
// Run test
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
sdkResetTimer(&hTimer);
|
||||
sdkStartTimer(&hTimer);
|
||||
// Clean output buffer before current test
|
||||
checkCudaErrors(cudaMemset(d_odata, 0, memory_size));
|
||||
// Run test
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
sdkResetTimer(&hTimer);
|
||||
sdkStartTimer(&hTimer);
|
||||
|
||||
for (int i = 0; i < NUM_ITERATIONS; i++) {
|
||||
testKernel<TData>
|
||||
<<<64, 256>>>((TData *)d_odata, (TData *)d_idata, numElements);
|
||||
getLastCudaError("testKernel() execution failed\n");
|
||||
}
|
||||
for (int i = 0; i < NUM_ITERATIONS; i++) {
|
||||
testKernel<TData><<<64, 256>>>((TData *)d_odata, (TData *)d_idata, numElements);
|
||||
getLastCudaError("testKernel() execution failed\n");
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
sdkStopTimer(&hTimer);
|
||||
double gpuTime = sdkGetTimerValue(&hTimer) / NUM_ITERATIONS;
|
||||
printf("Avg. time: %f ms / Copy throughput: %f GB/s.\n", gpuTime,
|
||||
(double)totalMemSizeAligned / (gpuTime * 0.001 * 1073741824.0));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
sdkStopTimer(&hTimer);
|
||||
double gpuTime = sdkGetTimerValue(&hTimer) / NUM_ITERATIONS;
|
||||
printf("Avg. time: %f ms / Copy throughput: %f GB/s.\n",
|
||||
gpuTime,
|
||||
(double)totalMemSizeAligned / (gpuTime * 0.001 * 1073741824.0));
|
||||
|
||||
// Read back GPU results and run validation
|
||||
checkCudaErrors(
|
||||
cudaMemcpy(h_odataGPU, d_odata, memory_size, cudaMemcpyDeviceToHost));
|
||||
int flag = testCPU((TData *)h_odataGPU, (TData *)h_idataCPU, numElements,
|
||||
packedElementSize);
|
||||
// Read back GPU results and run validation
|
||||
checkCudaErrors(cudaMemcpy(h_odataGPU, d_odata, memory_size, cudaMemcpyDeviceToHost));
|
||||
int flag = testCPU((TData *)h_odataGPU, (TData *)h_idataCPU, numElements, packedElementSize);
|
||||
|
||||
printf(flag ? "\tTEST OK\n" : "\tTEST FAILURE\n");
|
||||
printf(flag ? "\tTEST OK\n" : "\tTEST FAILURE\n");
|
||||
|
||||
return !flag;
|
||||
return !flag;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int i, nTotalFailures = 0;
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i, nTotalFailures = 0;
|
||||
|
||||
int devID;
|
||||
cudaDeviceProp deviceProp;
|
||||
printf("[%s] - Starting...\n", argv[0]);
|
||||
int devID;
|
||||
cudaDeviceProp deviceProp;
|
||||
printf("[%s] - Starting...\n", argv[0]);
|
||||
|
||||
// find first CUDA device
|
||||
devID = findCudaDevice(argc, (const char **)argv);
|
||||
// find first CUDA device
|
||||
devID = findCudaDevice(argc, (const char **)argv);
|
||||
|
||||
// get number of SMs on this GPU
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));
|
||||
printf("[%s] has %d MP(s) x %d (Cores/MP) = %d (Cores)\n", deviceProp.name,
|
||||
deviceProp.multiProcessorCount,
|
||||
_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor),
|
||||
_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) *
|
||||
deviceProp.multiProcessorCount);
|
||||
// get number of SMs on this GPU
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));
|
||||
printf("[%s] has %d MP(s) x %d (Cores/MP) = %d (Cores)\n",
|
||||
deviceProp.name,
|
||||
deviceProp.multiProcessorCount,
|
||||
_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor),
|
||||
_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * deviceProp.multiProcessorCount);
|
||||
|
||||
// Anything that is less than 192 Cores will have a scaled down workload
|
||||
float scale_factor =
|
||||
max((192.0f / (_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) *
|
||||
(float)deviceProp.multiProcessorCount)),
|
||||
1.0f);
|
||||
// Anything that is less than 192 Cores will have a scaled down workload
|
||||
float scale_factor = max(
|
||||
(192.0f / (_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * (float)deviceProp.multiProcessorCount)),
|
||||
1.0f);
|
||||
|
||||
int MemorySize = (int)(MEM_SIZE / scale_factor) &
|
||||
0xffffff00; // force multiple of 256 bytes
|
||||
int MemorySize = (int)(MEM_SIZE / scale_factor) & 0xffffff00; // force multiple of 256 bytes
|
||||
|
||||
printf("> Compute scaling value = %4.2f\n", scale_factor);
|
||||
printf("> Memory Size = %d\n", MemorySize);
|
||||
printf("> Compute scaling value = %4.2f\n", scale_factor);
|
||||
printf("> Memory Size = %d\n", MemorySize);
|
||||
|
||||
sdkCreateTimer(&hTimer);
|
||||
sdkCreateTimer(&hTimer);
|
||||
|
||||
printf("Allocating memory...\n");
|
||||
h_idataCPU = (unsigned char *)malloc(MemorySize);
|
||||
h_odataGPU = (unsigned char *)malloc(MemorySize);
|
||||
checkCudaErrors(cudaMalloc((void **)&d_idata, MemorySize));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_odata, MemorySize));
|
||||
printf("Allocating memory...\n");
|
||||
h_idataCPU = (unsigned char *)malloc(MemorySize);
|
||||
h_odataGPU = (unsigned char *)malloc(MemorySize);
|
||||
checkCudaErrors(cudaMalloc((void **)&d_idata, MemorySize));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_odata, MemorySize));
|
||||
|
||||
printf("Generating host input data array...\n");
|
||||
printf("Generating host input data array...\n");
|
||||
|
||||
for (i = 0; i < MemorySize; i++) {
|
||||
h_idataCPU[i] = (i & 0xFF) + 1;
|
||||
}
|
||||
for (i = 0; i < MemorySize; i++) {
|
||||
h_idataCPU[i] = (i & 0xFF) + 1;
|
||||
}
|
||||
|
||||
printf("Uploading input data to GPU memory...\n");
|
||||
checkCudaErrors(
|
||||
cudaMemcpy(d_idata, h_idataCPU, MemorySize, cudaMemcpyHostToDevice));
|
||||
printf("Uploading input data to GPU memory...\n");
|
||||
checkCudaErrors(cudaMemcpy(d_idata, h_idataCPU, MemorySize, cudaMemcpyHostToDevice));
|
||||
|
||||
printf("Testing misaligned types...\n");
|
||||
printf("uint8...\n");
|
||||
nTotalFailures += runTest<uint8>(1, MemorySize);
|
||||
printf("Testing misaligned types...\n");
|
||||
printf("uint8...\n");
|
||||
nTotalFailures += runTest<uint8>(1, MemorySize);
|
||||
|
||||
printf("uint16...\n");
|
||||
nTotalFailures += runTest<uint16>(2, MemorySize);
|
||||
printf("uint16...\n");
|
||||
nTotalFailures += runTest<uint16>(2, MemorySize);
|
||||
|
||||
printf("RGBA8_misaligned...\n");
|
||||
nTotalFailures += runTest<RGBA8_misaligned>(4, MemorySize);
|
||||
printf("RGBA8_misaligned...\n");
|
||||
nTotalFailures += runTest<RGBA8_misaligned>(4, MemorySize);
|
||||
|
||||
printf("LA32_misaligned...\n");
|
||||
nTotalFailures += runTest<LA32_misaligned>(8, MemorySize);
|
||||
printf("LA32_misaligned...\n");
|
||||
nTotalFailures += runTest<LA32_misaligned>(8, MemorySize);
|
||||
|
||||
printf("RGB32_misaligned...\n");
|
||||
nTotalFailures += runTest<RGB32_misaligned>(12, MemorySize);
|
||||
printf("RGB32_misaligned...\n");
|
||||
nTotalFailures += runTest<RGB32_misaligned>(12, MemorySize);
|
||||
|
||||
printf("RGBA32_misaligned...\n");
|
||||
nTotalFailures += runTest<RGBA32_misaligned>(16, MemorySize);
|
||||
printf("RGBA32_misaligned...\n");
|
||||
nTotalFailures += runTest<RGBA32_misaligned>(16, MemorySize);
|
||||
|
||||
printf("Testing aligned types...\n");
|
||||
printf("RGBA8...\n");
|
||||
nTotalFailures += runTest<RGBA8>(4, MemorySize);
|
||||
printf("Testing aligned types...\n");
|
||||
printf("RGBA8...\n");
|
||||
nTotalFailures += runTest<RGBA8>(4, MemorySize);
|
||||
|
||||
printf("I32...\n");
|
||||
nTotalFailures += runTest<I32>(4, MemorySize);
|
||||
printf("I32...\n");
|
||||
nTotalFailures += runTest<I32>(4, MemorySize);
|
||||
|
||||
printf("LA32...\n");
|
||||
nTotalFailures += runTest<LA32>(8, MemorySize);
|
||||
printf("LA32...\n");
|
||||
nTotalFailures += runTest<LA32>(8, MemorySize);
|
||||
|
||||
printf("RGB32...\n");
|
||||
nTotalFailures += runTest<RGB32>(12, MemorySize);
|
||||
printf("RGB32...\n");
|
||||
nTotalFailures += runTest<RGB32>(12, MemorySize);
|
||||
|
||||
printf("RGBA32...\n");
|
||||
nTotalFailures += runTest<RGBA32>(16, MemorySize);
|
||||
printf("RGBA32...\n");
|
||||
nTotalFailures += runTest<RGBA32>(16, MemorySize);
|
||||
|
||||
printf("RGBA32_2...\n");
|
||||
nTotalFailures += runTest<RGBA32_2>(32, MemorySize);
|
||||
printf("RGBA32_2...\n");
|
||||
nTotalFailures += runTest<RGBA32_2>(32, MemorySize);
|
||||
|
||||
printf("\n[alignedTypes] -> Test Results: %d Failures\n", nTotalFailures);
|
||||
printf("\n[alignedTypes] -> Test Results: %d Failures\n", nTotalFailures);
|
||||
|
||||
printf("Shutting down...\n");
|
||||
checkCudaErrors(cudaFree(d_idata));
|
||||
checkCudaErrors(cudaFree(d_odata));
|
||||
free(h_odataGPU);
|
||||
free(h_idataCPU);
|
||||
printf("Shutting down...\n");
|
||||
checkCudaErrors(cudaFree(d_idata));
|
||||
checkCudaErrors(cudaFree(d_odata));
|
||||
free(h_odataGPU);
|
||||
free(h_idataCPU);
|
||||
|
||||
sdkDeleteTimer(&hTimer);
|
||||
sdkDeleteTimer(&hTimer);
|
||||
|
||||
if (nTotalFailures != 0) {
|
||||
printf("Test failed!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (nTotalFailures != 0) {
|
||||
printf("Test failed!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("Test passed\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
printf("Test passed\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@ typedef struct{
|
||||
float b;
|
||||
} testStructure;
|
||||
|
||||
Without alignment specification the compiler will not automatically use a single 64-bit global memory load/store instruction, but will emit two 32-bit load instructions instead.
|
||||
This significantly impacts aggregate load/store bandwidth, since the latter breaks coalescing rules because of incontiguous memory access pattern. Refer to section 5.1.2.1 of the Programming Guide.
|
||||
Without alignment specification the compiler will not automatically use a single 64-bit global memory load/store instruction, but will emit two 32-bit load instructions instead.
|
||||
This significantly impacts aggregate load/store bandwidth, since the latter breaks coalescing rules because of incontiguous memory access pattern. Refer to section 5.1.2.1 of the Programming Guide.
|
||||
|
||||
@@ -30,4 +30,3 @@ cudaStreamBeginCapture, cudaGraphInstantiate, cudaGraphLaunch, cudaGraphUpload
|
||||
Download and install the [CUDA Toolkit 12.5](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
|
||||
## References (for more details)
|
||||
|
||||
|
||||
@@ -31,10 +31,10 @@
|
||||
|
||||
#define USE_NVTX
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cuda_runtime.h>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
|
||||
typedef volatile int LatchType;
|
||||
|
||||
@@ -43,75 +43,72 @@ std::chrono::time_point<std::chrono::high_resolution_clock> getCpuTime()
|
||||
return std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
float getMicroSecondDuration(T start, T end)
|
||||
template <typename T> float getMicroSecondDuration(T start, T end)
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(end-start).count() *.001f;
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count() * .001f;
|
||||
}
|
||||
|
||||
float getAsyncMicroSecondDuration(cudaEvent_t start, cudaEvent_t end)
|
||||
{
|
||||
float ms;
|
||||
cudaEventElapsedTime(&ms, start, end);
|
||||
return ms*1000;
|
||||
return ms * 1000;
|
||||
}
|
||||
|
||||
#ifdef USE_NVTX
|
||||
#include <nvtx3/nvToolsExt.h>
|
||||
|
||||
class Tracer {
|
||||
class Tracer
|
||||
{
|
||||
public:
|
||||
Tracer(const char* name) {
|
||||
nvtxRangePushA(name);
|
||||
}
|
||||
~Tracer() {
|
||||
nvtxRangePop();
|
||||
}
|
||||
Tracer(const char *name) { nvtxRangePushA(name); }
|
||||
~Tracer() { nvtxRangePop(); }
|
||||
};
|
||||
#define RANGE(name) Tracer uniq_name_using_macros(name);
|
||||
#define RANGE(name) Tracer uniq_name_using_macros(name);
|
||||
#define RANGE_PUSH(name) nvtxRangePushA(name)
|
||||
#define RANGE_POP() nvtxRangePop();
|
||||
#define RANGE_POP() nvtxRangePop();
|
||||
#else
|
||||
#define RANGE(name)
|
||||
#endif
|
||||
|
||||
std::vector<cudaStream_t> stream;
|
||||
cudaEvent_t event[1];
|
||||
cudaEvent_t timingEvent[2];
|
||||
cudaEvent_t event[1];
|
||||
cudaEvent_t timingEvent[2];
|
||||
|
||||
struct hostData {
|
||||
struct hostData
|
||||
{
|
||||
long long timeElapsed;
|
||||
bool timeoutDetected;
|
||||
bool timeoutDetected;
|
||||
long long timeElapsed2;
|
||||
bool timeoutDetected2;
|
||||
bool timeoutDetected2;
|
||||
LatchType latch;
|
||||
LatchType latch2;
|
||||
};
|
||||
|
||||
struct hostData *hostData;
|
||||
|
||||
__global__ void empty()
|
||||
{
|
||||
}
|
||||
__global__ void empty() {}
|
||||
|
||||
// Function to read the GPU nanosecond timer in a kernel
|
||||
__device__ __forceinline__ unsigned long long __globaltimer() {
|
||||
unsigned long long globaltimer;
|
||||
asm volatile ("mov.u64 %0, %globaltimer;" : "=l"(globaltimer));
|
||||
return globaltimer;
|
||||
__device__ __forceinline__ unsigned long long __globaltimer()
|
||||
{
|
||||
unsigned long long globaltimer;
|
||||
asm volatile("mov.u64 %0, %globaltimer;" : "=l"(globaltimer));
|
||||
return globaltimer;
|
||||
}
|
||||
|
||||
__global__ void delay(long long ticks)
|
||||
{
|
||||
long long endTime = clock64() + ticks;
|
||||
while (clock64() < endTime);
|
||||
while (clock64() < endTime)
|
||||
;
|
||||
}
|
||||
|
||||
__global__ void waitWithTimeout(long long nanoseconds, bool* timeoutDetected, long long *timeElapsed, LatchType* latch)
|
||||
__global__ void waitWithTimeout(long long nanoseconds, bool *timeoutDetected, long long *timeElapsed, LatchType *latch)
|
||||
{
|
||||
long long startTime = __globaltimer();
|
||||
long long endTime = startTime + nanoseconds;
|
||||
long long time = 0;
|
||||
long long endTime = startTime + nanoseconds;
|
||||
long long time = 0;
|
||||
do {
|
||||
time = __globaltimer();
|
||||
} while (time < endTime && (latch == NULL || *latch == 0));
|
||||
@@ -124,13 +121,9 @@ __global__ void waitWithTimeout(long long nanoseconds, bool* timeoutDetected, lo
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void preUploadAnnotation()
|
||||
{
|
||||
}
|
||||
__global__ void preUploadAnnotation() {}
|
||||
|
||||
__global__ void postUploadAnnotation()
|
||||
{
|
||||
}
|
||||
__global__ void postUploadAnnotation() {}
|
||||
|
||||
cudaGraph_t createParallelChain(int length, int width, bool singleEntry = false)
|
||||
{
|
||||
@@ -138,9 +131,9 @@ cudaGraph_t createParallelChain(int length, int width, bool singleEntry = false)
|
||||
RANGE("capture");
|
||||
cudaGraph_t graph;
|
||||
cudaStreamBeginCapture(stream[0], cudaStreamCaptureModeGlobal);
|
||||
int streamIdx = 0;
|
||||
int streamIdx = 0;
|
||||
if (singleEntry) {
|
||||
empty<<<1,1,0,stream[streamIdx]>>>();
|
||||
empty<<<1, 1, 0, stream[streamIdx]>>>();
|
||||
}
|
||||
|
||||
cudaEventRecord(event[0], stream[0]);
|
||||
@@ -151,7 +144,7 @@ cudaGraph_t createParallelChain(int length, int width, bool singleEntry = false)
|
||||
for (int i = 0; i < width; i++) {
|
||||
streamIdx = i;
|
||||
for (int j = 0; j < length; j++) {
|
||||
empty<<<1,1,0,stream[streamIdx]>>>();
|
||||
empty<<<1, 1, 0, stream[streamIdx]>>>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,10 +157,10 @@ cudaGraph_t createParallelChain(int length, int width, bool singleEntry = false)
|
||||
return graph;
|
||||
}
|
||||
|
||||
std::vector<const char*> metricName;
|
||||
std::vector<float> metricValue;
|
||||
std::vector<const char *> metricName;
|
||||
std::vector<float> metricValue;
|
||||
|
||||
int counter2 = 0;
|
||||
int counter2 = 0;
|
||||
void runDemo(cudaGraph_t graph, int length, int width)
|
||||
{
|
||||
cudaGraphExec_t graphExec;
|
||||
@@ -203,12 +196,13 @@ void runDemo(cudaGraph_t graph, int length, int width)
|
||||
metricValue.push_back(getMicroSecondDuration(start, streamSync));
|
||||
}
|
||||
{
|
||||
// re-instantiating the exec to simulate first launch into a busy stream.
|
||||
// re-instantiating the exec to simulate first launch into a busy stream.
|
||||
cudaGraphExecDestroy(graphExec);
|
||||
cudaGraphInstantiateWithFlags(&graphExec, graph, 0);
|
||||
|
||||
long long maxTimeoutNanoSeconds = 4000 + 500*length*width;
|
||||
waitWithTimeout<<<1,1,0,stream[0]>>>(maxTimeoutNanoSeconds, &hostData->timeoutDetected, &hostData->timeElapsed, &hostData->latch);
|
||||
long long maxTimeoutNanoSeconds = 4000 + 500 * length * width;
|
||||
waitWithTimeout<<<1, 1, 0, stream[0]>>>(
|
||||
maxTimeoutNanoSeconds, &hostData->timeoutDetected, &hostData->timeElapsed, &hostData->latch);
|
||||
|
||||
RANGE("launch including upload in busy stream");
|
||||
cudaEventRecord(timingEvent[0], stream[0]);
|
||||
@@ -222,13 +216,14 @@ void runDemo(cudaGraph_t graph, int length, int width)
|
||||
metricValue.push_back(getAsyncMicroSecondDuration(timingEvent[0], timingEvent[1]));
|
||||
metricName.push_back("blockingKernelTimeoutDetected");
|
||||
metricValue.push_back(hostData->timeoutDetected);
|
||||
hostData->latch = 0;
|
||||
hostData->latch = 0;
|
||||
hostData->timeoutDetected = 0;
|
||||
}
|
||||
{
|
||||
RANGE("repeat lauch in busy stream");
|
||||
long long maxTimeoutNanoSeconds = 4000 + 500*length*width;
|
||||
waitWithTimeout<<<1,1,0,stream[0]>>>(maxTimeoutNanoSeconds, &hostData->timeoutDetected, &hostData->timeElapsed, &hostData->latch);
|
||||
long long maxTimeoutNanoSeconds = 4000 + 500 * length * width;
|
||||
waitWithTimeout<<<1, 1, 0, stream[0]>>>(
|
||||
maxTimeoutNanoSeconds, &hostData->timeoutDetected, &hostData->timeElapsed, &hostData->latch);
|
||||
cudaEventRecord(timingEvent[0], stream[0]);
|
||||
cudaGraphLaunch(graphExec, stream[0]);
|
||||
cudaEventRecord(timingEvent[1], stream[0]);
|
||||
@@ -240,34 +235,37 @@ void runDemo(cudaGraph_t graph, int length, int width)
|
||||
metricValue.push_back(getAsyncMicroSecondDuration(timingEvent[0], timingEvent[1]));
|
||||
metricName.push_back("blockingKernelTimeoutDetected");
|
||||
metricValue.push_back(hostData->timeoutDetected);
|
||||
hostData->latch = 0;
|
||||
hostData->latch = 0;
|
||||
hostData->timeoutDetected = 0;
|
||||
}
|
||||
{
|
||||
// re-instantiating the exec to provide upload with work to do.
|
||||
cudaGraphExecDestroy(graphExec);
|
||||
cudaGraphInstantiateWithFlags(&graphExec, graph, 0);
|
||||
long long maxTimeoutNanoSeconds = 4000 + 1000*length*width;
|
||||
waitWithTimeout<<<1,1,0,stream[0]>>>(maxTimeoutNanoSeconds, &hostData->timeoutDetected2, &hostData->timeElapsed2, &hostData->latch2);
|
||||
maxTimeoutNanoSeconds = 2000 + 500*length*width;
|
||||
waitWithTimeout<<<1,1,0,stream[1]>>>(maxTimeoutNanoSeconds, &hostData->timeoutDetected, &hostData->timeElapsed, &hostData->latch);
|
||||
long long maxTimeoutNanoSeconds = 4000 + 1000 * length * width;
|
||||
waitWithTimeout<<<1, 1, 0, stream[0]>>>(
|
||||
maxTimeoutNanoSeconds, &hostData->timeoutDetected2, &hostData->timeElapsed2, &hostData->latch2);
|
||||
maxTimeoutNanoSeconds = 2000 + 500 * length * width;
|
||||
waitWithTimeout<<<1, 1, 0, stream[1]>>>(
|
||||
maxTimeoutNanoSeconds, &hostData->timeoutDetected, &hostData->timeElapsed, &hostData->latch);
|
||||
|
||||
RANGE("uploading a graph off of the critical path");
|
||||
preUploadAnnotation<<<1,1,0,stream[1]>>>();
|
||||
preUploadAnnotation<<<1, 1, 0, stream[1]>>>();
|
||||
cudaEventRecord(timingEvent[0], stream[0]);
|
||||
auto start = getCpuTime();
|
||||
cudaGraphUpload(graphExec, stream[1]);
|
||||
auto apiReturn = getCpuTime();
|
||||
cudaEventRecord(event[0],stream[1]);
|
||||
cudaEventRecord(event[0], stream[1]);
|
||||
cudaEventRecord(timingEvent[1], stream[0]);
|
||||
postUploadAnnotation<<<1,1,0,stream[1]>>>();
|
||||
postUploadAnnotation<<<1, 1, 0, stream[1]>>>();
|
||||
|
||||
hostData->latch = 1; // release the blocking kernel for the upload
|
||||
cudaStreamWaitEvent(stream[0],event[0]);
|
||||
cudaStreamWaitEvent(stream[0], event[0]);
|
||||
cudaGraphLaunch(graphExec, stream[0]);
|
||||
cudaEventSynchronize(event[0]); // upload done, similuate critical path being ready for the graph to run by the release of the second latch
|
||||
cudaEventSynchronize(event[0]); // upload done, similuate critical path being ready for the graph to run by the
|
||||
// release of the second latch
|
||||
|
||||
hostData->latch2 = 1; // release the work
|
||||
hostData->latch2 = 1; // release the work
|
||||
cudaStreamSynchronize(stream[0]);
|
||||
|
||||
metricName.push_back("upload_api_time");
|
||||
@@ -277,9 +275,9 @@ void runDemo(cudaGraph_t graph, int length, int width)
|
||||
metricName.push_back("blockingKernelTimeoutDetected");
|
||||
metricValue.push_back(hostData->timeoutDetected);
|
||||
|
||||
hostData->latch = 0;
|
||||
hostData->latch2 = 0;
|
||||
hostData->timeoutDetected = 0;
|
||||
hostData->latch = 0;
|
||||
hostData->latch2 = 0;
|
||||
hostData->timeoutDetected = 0;
|
||||
hostData->timeoutDetected2 = 0;
|
||||
}
|
||||
cudaGraphExecDestroy(graphExec);
|
||||
@@ -287,7 +285,8 @@ void runDemo(cudaGraph_t graph, int length, int width)
|
||||
RANGE_POP();
|
||||
}
|
||||
|
||||
void usage() {
|
||||
void usage()
|
||||
{
|
||||
printf("programName [outputFmt] [numTrials] [length] [width] [pattern] [stride] [maxLength] \n");
|
||||
printf("\toutputFmt - program output, default=3 (see below)\n");
|
||||
printf("\tnumTrials (per length)\n");
|
||||
@@ -312,34 +311,36 @@ void usage() {
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if(argc < 1) {
|
||||
if (argc < 1) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int numTrials=1, length=20, width=1, outputFmt=3, pattern=0, stride = 1;
|
||||
if(argc > 1) outputFmt = atoi(argv[1]);
|
||||
if(argc > 2) numTrials = atoi(argv[2]);
|
||||
if(argc > 3) length= atoi(argv[3]);
|
||||
if(argc > 4) width= atoi(argv[4]);
|
||||
if(argc > 5) pattern = atoi(argv[5]);
|
||||
if(argc > 6) stride = atoi(argv[6]);
|
||||
int numTrials = 1, length = 20, width = 1, outputFmt = 3, pattern = 0, stride = 1;
|
||||
if (argc > 1)
|
||||
outputFmt = atoi(argv[1]);
|
||||
if (argc > 2)
|
||||
numTrials = atoi(argv[2]);
|
||||
if (argc > 3)
|
||||
length = atoi(argv[3]);
|
||||
if (argc > 4)
|
||||
width = atoi(argv[4]);
|
||||
if (argc > 5)
|
||||
pattern = atoi(argv[5]);
|
||||
if (argc > 6)
|
||||
stride = atoi(argv[6]);
|
||||
int maxLength = length;
|
||||
if(argc > 7) maxLength = atoi(argv[7]);
|
||||
if (argc > 7)
|
||||
maxLength = atoi(argv[7]);
|
||||
if (maxLength < length) {
|
||||
maxLength = length;
|
||||
}
|
||||
|
||||
if((outputFmt & 4) && (outputFmt & 2)) {
|
||||
if ((outputFmt & 4) && (outputFmt & 2)) {
|
||||
printf("printing average and all samples doesn't make sense\n");
|
||||
}
|
||||
|
||||
if(length == 0 ||
|
||||
width == 0 ||
|
||||
outputFmt == 0 ||
|
||||
outputFmt > 5 ||
|
||||
pattern > 1)
|
||||
{
|
||||
if (length == 0 || width == 0 || outputFmt == 0 || outputFmt > 5 || pattern > 1) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
@@ -351,10 +352,10 @@ int main(int argc, char **argv)
|
||||
cudaFree(0);
|
||||
cudaMallocHost(&hostData, sizeof(*hostData));
|
||||
int numStreams = width;
|
||||
if (numStreams == 1) numStreams = 2; // demo needs two streams even if capture only needs 1.
|
||||
if (numStreams == 1)
|
||||
numStreams = 2; // demo needs two streams even if capture only needs 1.
|
||||
stream.resize(numStreams);
|
||||
for (int i = 0; i < numStreams; i++)
|
||||
{
|
||||
for (int i = 0; i < numStreams; i++) {
|
||||
cudaStreamCreate(&stream[i]);
|
||||
}
|
||||
|
||||
@@ -364,15 +365,14 @@ int main(int argc, char **argv)
|
||||
|
||||
{
|
||||
RANGE("warmup");
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
empty<<<1,1,0,stream[i]>>>();
|
||||
for (int i = 0; i < width; i++) {
|
||||
empty<<<1, 1, 0, stream[i]>>>();
|
||||
}
|
||||
cudaStreamSynchronize(stream[0]);
|
||||
|
||||
auto start = getCpuTime();
|
||||
graph = createParallelChain(length, width, singleEntry);
|
||||
auto end = getCpuTime();
|
||||
graph = createParallelChain(length, width, singleEntry);
|
||||
auto end = getCpuTime();
|
||||
metricValue.push_back(getMicroSecondDuration(start, end));
|
||||
metricName.push_back("capture");
|
||||
runDemo(graph, length, width);
|
||||
@@ -382,7 +382,7 @@ int main(int argc, char **argv)
|
||||
printf("length, width, pattern, ");
|
||||
for (int i = 0; i < metricName.size(); i++) {
|
||||
printf("%s, ", metricName[i]);
|
||||
}
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ int main(int argc, char **argv)
|
||||
printf("skipping trials since no output is expected\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
std::vector<double> metricTotal;
|
||||
metricTotal.resize(metricValue.size());
|
||||
|
||||
@@ -399,32 +399,32 @@ int main(int argc, char **argv)
|
||||
metricName.clear();
|
||||
metricValue.clear();
|
||||
auto start = getCpuTime();
|
||||
graph = createParallelChain(length, width, singleEntry);
|
||||
auto end = getCpuTime();
|
||||
graph = createParallelChain(length, width, singleEntry);
|
||||
auto end = getCpuTime();
|
||||
metricValue.push_back(getMicroSecondDuration(start, end));
|
||||
|
||||
runDemo(graph, length, width);
|
||||
|
||||
if (outputFmt & 2) {
|
||||
printf("%d, %d, %d, ",length, width, pattern);
|
||||
printf("%d, %d, %d, ", length, width, pattern);
|
||||
for (int i = 0; i < metricValue.size(); i++) {
|
||||
printf("%0.3f, ", metricValue[i]);
|
||||
}
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
if (outputFmt & 4) {
|
||||
for (int i = 0; i < metricTotal.size(); i++) {
|
||||
metricTotal[i] += metricValue[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (outputFmt & 4) {
|
||||
printf("%d, %d, %d, ",length, width, pattern);
|
||||
printf("%d, %d, %d, ", length, width, pattern);
|
||||
for (int i = 0; i < metricTotal.size(); i++) {
|
||||
printf("%0.3f, ", metricTotal[i]/numTrials);
|
||||
printf("%0.3f, ", metricTotal[i] / numTrials);
|
||||
metricTotal[i] = 0;
|
||||
}
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -42,9 +42,9 @@
|
||||
|
||||
namespace cg = cooperative_groups;
|
||||
// Utilities and system includes
|
||||
#include <helper_string.h> // helper for string parsing
|
||||
#include <helper_image.h> // helper for image and data comparison
|
||||
#include <helper_cuda.h> // helper for cuda error checking functions
|
||||
#include <helper_cuda.h> // helper for cuda error checking functions
|
||||
#include <helper_image.h> // helper for image and data comparison
|
||||
#include <helper_string.h> // helper for string parsing
|
||||
|
||||
const char *sSDKsample = "Transpose";
|
||||
|
||||
@@ -53,20 +53,19 @@ const char *sSDKsample = "Transpose";
|
||||
// TILE_DIM/BLOCK_ROWS elements. TILE_DIM must be an integral multiple of
|
||||
// BLOCK_ROWS
|
||||
|
||||
#define TILE_DIM 32
|
||||
#define TILE_DIM 32
|
||||
#define BLOCK_ROWS 16
|
||||
|
||||
// This sample assumes that MATRIX_SIZE_X = MATRIX_SIZE_Y
|
||||
int MATRIX_SIZE_X = 1024;
|
||||
int MATRIX_SIZE_Y = 1024;
|
||||
int MUL_FACTOR = TILE_DIM;
|
||||
int MUL_FACTOR = TILE_DIM;
|
||||
|
||||
#define FLOOR(a, b) (a - (a % b))
|
||||
|
||||
// Compute the tile size necessary to illustrate performance cases for SM20+
|
||||
// hardware
|
||||
int MAX_TILES = (FLOOR(MATRIX_SIZE_X, 512) * FLOOR(MATRIX_SIZE_Y, 512)) /
|
||||
(TILE_DIM * TILE_DIM);
|
||||
int MAX_TILES = (FLOOR(MATRIX_SIZE_X, 512) * FLOOR(MATRIX_SIZE_Y, 512)) / (TILE_DIM * TILE_DIM);
|
||||
|
||||
// Number of repetitions used for timing. Two sets of repetitions are
|
||||
// performed: 1) over kernel launches and 2) inside the kernel over just the
|
||||
@@ -79,41 +78,42 @@ int MAX_TILES = (FLOOR(MATRIX_SIZE_X, 512) * FLOOR(MATRIX_SIZE_Y, 512)) /
|
||||
// width and height must be integral multiples of TILE_DIM
|
||||
// -------------------------------------------------------
|
||||
|
||||
__global__ void copy(float *odata, float *idata, int width, int height) {
|
||||
int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;
|
||||
int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;
|
||||
__global__ void copy(float *odata, float *idata, int width, int height)
|
||||
{
|
||||
int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;
|
||||
int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;
|
||||
|
||||
int index = xIndex + width * yIndex;
|
||||
int index = xIndex + width * yIndex;
|
||||
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
odata[index + i * width] = idata[index + i * width];
|
||||
}
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
odata[index + i * width] = idata[index + i * width];
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void copySharedMem(float *odata, float *idata, int width,
|
||||
int height) {
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ float tile[TILE_DIM][TILE_DIM];
|
||||
__global__ void copySharedMem(float *odata, float *idata, int width, int height)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ float tile[TILE_DIM][TILE_DIM];
|
||||
|
||||
int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;
|
||||
int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;
|
||||
int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;
|
||||
int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;
|
||||
|
||||
int index = xIndex + width * yIndex;
|
||||
int index = xIndex + width * yIndex;
|
||||
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
if (xIndex < width && yIndex < height) {
|
||||
tile[threadIdx.y][threadIdx.x] = idata[index];
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
if (xIndex < width && yIndex < height) {
|
||||
tile[threadIdx.y][threadIdx.x] = idata[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
cg::sync(cta);
|
||||
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
if (xIndex < height && yIndex < width) {
|
||||
odata[index] = tile[threadIdx.y][threadIdx.x];
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
if (xIndex < height && yIndex < width) {
|
||||
odata[index] = tile[threadIdx.y][threadIdx.x];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
@@ -121,71 +121,71 @@ __global__ void copySharedMem(float *odata, float *idata, int width,
|
||||
// width and height must be integral multiples of TILE_DIM
|
||||
// -------------------------------------------------------
|
||||
|
||||
__global__ void transposeNaive(float *odata, float *idata, int width,
|
||||
int height) {
|
||||
int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;
|
||||
int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;
|
||||
__global__ void transposeNaive(float *odata, float *idata, int width, int height)
|
||||
{
|
||||
int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;
|
||||
int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;
|
||||
|
||||
int index_in = xIndex + width * yIndex;
|
||||
int index_out = yIndex + height * xIndex;
|
||||
int index_in = xIndex + width * yIndex;
|
||||
int index_out = yIndex + height * xIndex;
|
||||
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
odata[index_out + i] = idata[index_in + i * width];
|
||||
}
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
odata[index_out + i] = idata[index_in + i * width];
|
||||
}
|
||||
}
|
||||
|
||||
// coalesced transpose (with bank conflicts)
|
||||
|
||||
__global__ void transposeCoalesced(float *odata, float *idata, int width,
|
||||
int height) {
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ float tile[TILE_DIM][TILE_DIM];
|
||||
__global__ void transposeCoalesced(float *odata, float *idata, int width, int height)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ float tile[TILE_DIM][TILE_DIM];
|
||||
|
||||
int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;
|
||||
int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;
|
||||
int index_in = xIndex + (yIndex)*width;
|
||||
int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;
|
||||
int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;
|
||||
int index_in = xIndex + (yIndex)*width;
|
||||
|
||||
xIndex = blockIdx.y * TILE_DIM + threadIdx.x;
|
||||
yIndex = blockIdx.x * TILE_DIM + threadIdx.y;
|
||||
int index_out = xIndex + (yIndex)*height;
|
||||
xIndex = blockIdx.y * TILE_DIM + threadIdx.x;
|
||||
yIndex = blockIdx.x * TILE_DIM + threadIdx.y;
|
||||
int index_out = xIndex + (yIndex)*height;
|
||||
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
tile[threadIdx.y + i][threadIdx.x] = idata[index_in + i * width];
|
||||
}
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
tile[threadIdx.y + i][threadIdx.x] = idata[index_in + i * width];
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
cg::sync(cta);
|
||||
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
odata[index_out + i * height] = tile[threadIdx.x][threadIdx.y + i];
|
||||
}
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
odata[index_out + i * height] = tile[threadIdx.x][threadIdx.y + i];
|
||||
}
|
||||
}
|
||||
|
||||
// Coalesced transpose with no bank conflicts
|
||||
|
||||
__global__ void transposeNoBankConflicts(float *odata, float *idata, int width,
|
||||
int height) {
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ float tile[TILE_DIM][TILE_DIM + 1];
|
||||
__global__ void transposeNoBankConflicts(float *odata, float *idata, int width, int height)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ float tile[TILE_DIM][TILE_DIM + 1];
|
||||
|
||||
int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;
|
||||
int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;
|
||||
int index_in = xIndex + (yIndex)*width;
|
||||
int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;
|
||||
int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;
|
||||
int index_in = xIndex + (yIndex)*width;
|
||||
|
||||
xIndex = blockIdx.y * TILE_DIM + threadIdx.x;
|
||||
yIndex = blockIdx.x * TILE_DIM + threadIdx.y;
|
||||
int index_out = xIndex + (yIndex)*height;
|
||||
xIndex = blockIdx.y * TILE_DIM + threadIdx.x;
|
||||
yIndex = blockIdx.x * TILE_DIM + threadIdx.y;
|
||||
int index_out = xIndex + (yIndex)*height;
|
||||
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
tile[threadIdx.y + i][threadIdx.x] = idata[index_in + i * width];
|
||||
}
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
tile[threadIdx.y + i][threadIdx.x] = idata[index_in + i * width];
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
cg::sync(cta);
|
||||
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
odata[index_out + i * height] = tile[threadIdx.x][threadIdx.y + i];
|
||||
}
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
odata[index_out + i * height] = tile[threadIdx.x][threadIdx.y + i];
|
||||
}
|
||||
}
|
||||
|
||||
// Transpose that effectively reorders execution of thread blocks along
|
||||
@@ -200,44 +200,45 @@ __global__ void transposeNoBankConflicts(float *odata, float *idata, int width,
|
||||
// blockIdx_y and replacement of blockIdx.x and bloclIdx.y with the subscripted
|
||||
// versions in the remaining code
|
||||
|
||||
__global__ void transposeDiagonal(float *odata, float *idata, int width,
|
||||
int height) {
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ float tile[TILE_DIM][TILE_DIM + 1];
|
||||
__global__ void transposeDiagonal(float *odata, float *idata, int width, int height)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ float tile[TILE_DIM][TILE_DIM + 1];
|
||||
|
||||
int blockIdx_x, blockIdx_y;
|
||||
int blockIdx_x, blockIdx_y;
|
||||
|
||||
// do diagonal reordering
|
||||
if (width == height) {
|
||||
blockIdx_y = blockIdx.x;
|
||||
blockIdx_x = (blockIdx.x + blockIdx.y) % gridDim.x;
|
||||
} else {
|
||||
int bid = blockIdx.x + gridDim.x * blockIdx.y;
|
||||
blockIdx_y = bid % gridDim.y;
|
||||
blockIdx_x = ((bid / gridDim.y) + blockIdx_y) % gridDim.x;
|
||||
}
|
||||
// do diagonal reordering
|
||||
if (width == height) {
|
||||
blockIdx_y = blockIdx.x;
|
||||
blockIdx_x = (blockIdx.x + blockIdx.y) % gridDim.x;
|
||||
}
|
||||
else {
|
||||
int bid = blockIdx.x + gridDim.x * blockIdx.y;
|
||||
blockIdx_y = bid % gridDim.y;
|
||||
blockIdx_x = ((bid / gridDim.y) + blockIdx_y) % gridDim.x;
|
||||
}
|
||||
|
||||
// from here on the code is same as previous kernel except blockIdx_x replaces
|
||||
// blockIdx.x and similarly for y
|
||||
// from here on the code is same as previous kernel except blockIdx_x replaces
|
||||
// blockIdx.x and similarly for y
|
||||
|
||||
int xIndex = blockIdx_x * TILE_DIM + threadIdx.x;
|
||||
int yIndex = blockIdx_y * TILE_DIM + threadIdx.y;
|
||||
int index_in = xIndex + (yIndex)*width;
|
||||
int xIndex = blockIdx_x * TILE_DIM + threadIdx.x;
|
||||
int yIndex = blockIdx_y * TILE_DIM + threadIdx.y;
|
||||
int index_in = xIndex + (yIndex)*width;
|
||||
|
||||
xIndex = blockIdx_y * TILE_DIM + threadIdx.x;
|
||||
yIndex = blockIdx_x * TILE_DIM + threadIdx.y;
|
||||
int index_out = xIndex + (yIndex)*height;
|
||||
xIndex = blockIdx_y * TILE_DIM + threadIdx.x;
|
||||
yIndex = blockIdx_x * TILE_DIM + threadIdx.y;
|
||||
int index_out = xIndex + (yIndex)*height;
|
||||
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
tile[threadIdx.y + i][threadIdx.x] = idata[index_in + i * width];
|
||||
}
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
tile[threadIdx.y + i][threadIdx.x] = idata[index_in + i * width];
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
cg::sync(cta);
|
||||
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
odata[index_out + i * height] = tile[threadIdx.x][threadIdx.y + i];
|
||||
}
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
odata[index_out + i * height] = tile[threadIdx.x][threadIdx.y + i];
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -249,365 +250,369 @@ __global__ void transposeDiagonal(float *odata, float *idata, int width,
|
||||
// components of a full transpose
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
__global__ void transposeFineGrained(float *odata, float *idata, int width,
|
||||
int height) {
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ float block[TILE_DIM][TILE_DIM + 1];
|
||||
__global__ void transposeFineGrained(float *odata, float *idata, int width, int height)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ float block[TILE_DIM][TILE_DIM + 1];
|
||||
|
||||
int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;
|
||||
int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;
|
||||
int index = xIndex + (yIndex)*width;
|
||||
int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;
|
||||
int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;
|
||||
int index = xIndex + (yIndex)*width;
|
||||
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
block[threadIdx.y + i][threadIdx.x] = idata[index + i * width];
|
||||
}
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
block[threadIdx.y + i][threadIdx.x] = idata[index + i * width];
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
cg::sync(cta);
|
||||
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
odata[index + i * height] = block[threadIdx.x][threadIdx.y + i];
|
||||
}
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
odata[index + i * height] = block[threadIdx.x][threadIdx.y + i];
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void transposeCoarseGrained(float *odata, float *idata, int width,
|
||||
int height) {
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ float block[TILE_DIM][TILE_DIM + 1];
|
||||
__global__ void transposeCoarseGrained(float *odata, float *idata, int width, int height)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ float block[TILE_DIM][TILE_DIM + 1];
|
||||
|
||||
int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;
|
||||
int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;
|
||||
int index_in = xIndex + (yIndex)*width;
|
||||
int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;
|
||||
int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;
|
||||
int index_in = xIndex + (yIndex)*width;
|
||||
|
||||
xIndex = blockIdx.y * TILE_DIM + threadIdx.x;
|
||||
yIndex = blockIdx.x * TILE_DIM + threadIdx.y;
|
||||
int index_out = xIndex + (yIndex)*height;
|
||||
xIndex = blockIdx.y * TILE_DIM + threadIdx.x;
|
||||
yIndex = blockIdx.x * TILE_DIM + threadIdx.y;
|
||||
int index_out = xIndex + (yIndex)*height;
|
||||
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
block[threadIdx.y + i][threadIdx.x] = idata[index_in + i * width];
|
||||
}
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
block[threadIdx.y + i][threadIdx.x] = idata[index_in + i * width];
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
cg::sync(cta);
|
||||
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
odata[index_out + i * height] = block[threadIdx.y + i][threadIdx.x];
|
||||
}
|
||||
for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {
|
||||
odata[index_out + i * height] = block[threadIdx.y + i][threadIdx.x];
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------
|
||||
// host utility routines
|
||||
// ---------------------
|
||||
|
||||
void computeTransposeGold(float *gold, float *idata, const int size_x,
|
||||
const int size_y) {
|
||||
for (int y = 0; y < size_y; ++y) {
|
||||
for (int x = 0; x < size_x; ++x) {
|
||||
gold[(x * size_y) + y] = idata[(y * size_x) + x];
|
||||
void computeTransposeGold(float *gold, float *idata, const int size_x, const int size_y)
|
||||
{
|
||||
for (int y = 0; y < size_y; ++y) {
|
||||
for (int x = 0; x < size_x; ++x) {
|
||||
gold[(x * size_y) + y] = idata[(y * size_x) + x];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getParams(int argc, char **argv, cudaDeviceProp &deviceProp, int &size_x,
|
||||
int &size_y, int max_tile_dim) {
|
||||
// set matrix size (if (x,y) dim of matrix is not square, then this will have
|
||||
// to be modified
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "dimX")) {
|
||||
size_x = getCmdLineArgumentInt(argc, (const char **)argv, "dimX");
|
||||
void getParams(int argc, char **argv, cudaDeviceProp &deviceProp, int &size_x, int &size_y, int max_tile_dim)
|
||||
{
|
||||
// set matrix size (if (x,y) dim of matrix is not square, then this will have
|
||||
// to be modified
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "dimX")) {
|
||||
size_x = getCmdLineArgumentInt(argc, (const char **)argv, "dimX");
|
||||
|
||||
if (size_x > max_tile_dim) {
|
||||
printf("> MatrixSize X = %d is greater than the recommended size = %d\n",
|
||||
size_x, max_tile_dim);
|
||||
} else {
|
||||
printf("> MatrixSize X = %d\n", size_x);
|
||||
if (size_x > max_tile_dim) {
|
||||
printf("> MatrixSize X = %d is greater than the recommended size = %d\n", size_x, max_tile_dim);
|
||||
}
|
||||
else {
|
||||
printf("> MatrixSize X = %d\n", size_x);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
size_x = max_tile_dim;
|
||||
size_x = FLOOR(size_x, 512);
|
||||
}
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "dimY")) {
|
||||
size_y = getCmdLineArgumentInt(argc, (const char **)argv, "dimY");
|
||||
|
||||
if (size_y > max_tile_dim) {
|
||||
printf("> MatrixSize Y = %d is greater than the recommended size = %d\n",
|
||||
size_y, max_tile_dim);
|
||||
} else {
|
||||
printf("> MatrixSize Y = %d\n", size_y);
|
||||
else {
|
||||
size_x = max_tile_dim;
|
||||
size_x = FLOOR(size_x, 512);
|
||||
}
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "dimY")) {
|
||||
size_y = getCmdLineArgumentInt(argc, (const char **)argv, "dimY");
|
||||
|
||||
if (size_y > max_tile_dim) {
|
||||
printf("> MatrixSize Y = %d is greater than the recommended size = %d\n", size_y, max_tile_dim);
|
||||
}
|
||||
else {
|
||||
printf("> MatrixSize Y = %d\n", size_y);
|
||||
}
|
||||
}
|
||||
else {
|
||||
size_y = max_tile_dim;
|
||||
size_y = FLOOR(size_y, 512);
|
||||
}
|
||||
} else {
|
||||
size_y = max_tile_dim;
|
||||
size_y = FLOOR(size_y, 512);
|
||||
}
|
||||
}
|
||||
|
||||
void showHelp() {
|
||||
printf("\n%s : Command line options\n", sSDKsample);
|
||||
printf("\t-device=n (where n=0,1,2.... for the GPU device)\n\n");
|
||||
printf("> The default matrix size can be overridden with these parameters\n");
|
||||
printf("\t-dimX=row_dim_size (matrix row dimensions)\n");
|
||||
printf("\t-dimY=col_dim_size (matrix column dimensions)\n");
|
||||
void showHelp()
|
||||
{
|
||||
printf("\n%s : Command line options\n", sSDKsample);
|
||||
printf("\t-device=n (where n=0,1,2.... for the GPU device)\n\n");
|
||||
printf("> The default matrix size can be overridden with these parameters\n");
|
||||
printf("\t-dimX=row_dim_size (matrix row dimensions)\n");
|
||||
printf("\t-dimY=col_dim_size (matrix column dimensions)\n");
|
||||
}
|
||||
|
||||
// ----
|
||||
// main
|
||||
// ----
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Start logs
|
||||
printf("%s Starting...\n\n", sSDKsample);
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// Start logs
|
||||
printf("%s Starting...\n\n", sSDKsample);
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "help")) {
|
||||
showHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int devID = findCudaDevice(argc, (const char **)argv);
|
||||
cudaDeviceProp deviceProp;
|
||||
|
||||
// get number of SMs on this GPU
|
||||
checkCudaErrors(cudaGetDevice(&devID));
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));
|
||||
|
||||
// compute the scaling factor (for GPUs with fewer MPs)
|
||||
float scale_factor, total_tiles;
|
||||
scale_factor =
|
||||
max((192.0f / (_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) *
|
||||
(float)deviceProp.multiProcessorCount)),
|
||||
1.0f);
|
||||
|
||||
printf("> Device %d: \"%s\"\n", devID, deviceProp.name);
|
||||
printf("> SM Capability %d.%d detected:\n", deviceProp.major,
|
||||
deviceProp.minor);
|
||||
|
||||
// Calculate number of tiles we will run for the Matrix Transpose performance
|
||||
// tests
|
||||
int size_x, size_y, max_matrix_dim, matrix_size_test;
|
||||
|
||||
matrix_size_test = 512; // we round down max_matrix_dim for this perf test
|
||||
total_tiles = (float)MAX_TILES / scale_factor;
|
||||
|
||||
max_matrix_dim =
|
||||
FLOOR((int)(floor(sqrt(total_tiles)) * TILE_DIM), matrix_size_test);
|
||||
|
||||
// This is the minimum size allowed
|
||||
if (max_matrix_dim == 0) {
|
||||
max_matrix_dim = matrix_size_test;
|
||||
}
|
||||
|
||||
printf("> [%s] has %d MP(s) x %d (Cores/MP) = %d (Cores)\n", deviceProp.name,
|
||||
deviceProp.multiProcessorCount,
|
||||
_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor),
|
||||
_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) *
|
||||
deviceProp.multiProcessorCount);
|
||||
|
||||
printf("> Compute performance scaling factor = %4.2f\n", scale_factor);
|
||||
|
||||
// Extract parameters if there are any, command line -dimx and -dimy can
|
||||
// override any of these settings
|
||||
getParams(argc, argv, deviceProp, size_x, size_y, max_matrix_dim);
|
||||
|
||||
if (size_x != size_y) {
|
||||
printf(
|
||||
"\n[%s] does not support non-square matrices (row_dim_size(%d) != "
|
||||
"col_dim_size(%d))\nExiting...\n\n",
|
||||
sSDKsample, size_x, size_y);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (size_x % TILE_DIM != 0 || size_y % TILE_DIM != 0) {
|
||||
printf(
|
||||
"[%s] Matrix size must be integral multiple of tile "
|
||||
"size\nExiting...\n\n",
|
||||
sSDKsample);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// kernel pointer and descriptor
|
||||
void (*kernel)(float *, float *, int, int);
|
||||
const char *kernelName;
|
||||
|
||||
// execution configuration parameters
|
||||
dim3 grid(size_x / TILE_DIM, size_y / TILE_DIM),
|
||||
threads(TILE_DIM, BLOCK_ROWS);
|
||||
|
||||
if (grid.x < 1 || grid.y < 1) {
|
||||
printf("[%s] grid size computation incorrect in test \nExiting...\n\n",
|
||||
sSDKsample);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// CUDA events
|
||||
cudaEvent_t start, stop;
|
||||
|
||||
// size of memory required to store the matrix
|
||||
size_t mem_size = static_cast<size_t>(sizeof(float) * size_x * size_y);
|
||||
|
||||
if (2 * mem_size > deviceProp.totalGlobalMem) {
|
||||
printf("Input matrix size is larger than the available device memory!\n");
|
||||
printf("Please choose a smaller size matrix\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// allocate host memory
|
||||
float *h_idata = (float *)malloc(mem_size);
|
||||
float *h_odata = (float *)malloc(mem_size);
|
||||
float *transposeGold = (float *)malloc(mem_size);
|
||||
float *gold;
|
||||
|
||||
// allocate device memory
|
||||
float *d_idata, *d_odata;
|
||||
checkCudaErrors(cudaMalloc((void **)&d_idata, mem_size));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_odata, mem_size));
|
||||
|
||||
// initialize host data
|
||||
for (int i = 0; i < (size_x * size_y); ++i) {
|
||||
h_idata[i] = (float)i;
|
||||
}
|
||||
|
||||
// copy host data to device
|
||||
checkCudaErrors(
|
||||
cudaMemcpy(d_idata, h_idata, mem_size, cudaMemcpyHostToDevice));
|
||||
|
||||
// Compute reference transpose solution
|
||||
computeTransposeGold(transposeGold, h_idata, size_x, size_y);
|
||||
|
||||
// print out common data for all kernels
|
||||
printf(
|
||||
"\nMatrix size: %dx%d (%dx%d tiles), tile size: %dx%d, block size: "
|
||||
"%dx%d\n\n",
|
||||
size_x, size_y, size_x / TILE_DIM, size_y / TILE_DIM, TILE_DIM, TILE_DIM,
|
||||
TILE_DIM, BLOCK_ROWS);
|
||||
|
||||
// initialize events
|
||||
checkCudaErrors(cudaEventCreate(&start));
|
||||
checkCudaErrors(cudaEventCreate(&stop));
|
||||
|
||||
//
|
||||
// loop over different kernels
|
||||
//
|
||||
|
||||
bool success = true;
|
||||
|
||||
for (int k = 0; k < 8; k++) {
|
||||
// set kernel pointer
|
||||
switch (k) {
|
||||
case 0:
|
||||
kernel = ©
|
||||
kernelName = "simple copy ";
|
||||
break;
|
||||
|
||||
case 1:
|
||||
kernel = ©SharedMem;
|
||||
kernelName = "shared memory copy";
|
||||
break;
|
||||
|
||||
case 2:
|
||||
kernel = &transposeNaive;
|
||||
kernelName = "naive ";
|
||||
break;
|
||||
|
||||
case 3:
|
||||
kernel = &transposeCoalesced;
|
||||
kernelName = "coalesced ";
|
||||
break;
|
||||
|
||||
case 4:
|
||||
kernel = &transposeNoBankConflicts;
|
||||
kernelName = "optimized ";
|
||||
break;
|
||||
|
||||
case 5:
|
||||
kernel = &transposeCoarseGrained;
|
||||
kernelName = "coarse-grained ";
|
||||
break;
|
||||
|
||||
case 6:
|
||||
kernel = &transposeFineGrained;
|
||||
kernelName = "fine-grained ";
|
||||
break;
|
||||
|
||||
case 7:
|
||||
kernel = &transposeDiagonal;
|
||||
kernelName = "diagonal ";
|
||||
break;
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "help")) {
|
||||
showHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// set reference solution
|
||||
if (kernel == © || kernel == ©SharedMem) {
|
||||
gold = h_idata;
|
||||
} else if (kernel == &transposeCoarseGrained ||
|
||||
kernel == &transposeFineGrained) {
|
||||
gold = h_odata; // fine- and coarse-grained kernels are not full
|
||||
// transposes, so bypass check
|
||||
} else {
|
||||
gold = transposeGold;
|
||||
int devID = findCudaDevice(argc, (const char **)argv);
|
||||
cudaDeviceProp deviceProp;
|
||||
|
||||
// get number of SMs on this GPU
|
||||
checkCudaErrors(cudaGetDevice(&devID));
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));
|
||||
|
||||
// compute the scaling factor (for GPUs with fewer MPs)
|
||||
float scale_factor, total_tiles;
|
||||
scale_factor = max(
|
||||
(192.0f / (_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * (float)deviceProp.multiProcessorCount)),
|
||||
1.0f);
|
||||
|
||||
printf("> Device %d: \"%s\"\n", devID, deviceProp.name);
|
||||
printf("> SM Capability %d.%d detected:\n", deviceProp.major, deviceProp.minor);
|
||||
|
||||
// Calculate number of tiles we will run for the Matrix Transpose performance
|
||||
// tests
|
||||
int size_x, size_y, max_matrix_dim, matrix_size_test;
|
||||
|
||||
matrix_size_test = 512; // we round down max_matrix_dim for this perf test
|
||||
total_tiles = (float)MAX_TILES / scale_factor;
|
||||
|
||||
max_matrix_dim = FLOOR((int)(floor(sqrt(total_tiles)) * TILE_DIM), matrix_size_test);
|
||||
|
||||
// This is the minimum size allowed
|
||||
if (max_matrix_dim == 0) {
|
||||
max_matrix_dim = matrix_size_test;
|
||||
}
|
||||
|
||||
// Clear error status
|
||||
checkCudaErrors(cudaGetLastError());
|
||||
printf("> [%s] has %d MP(s) x %d (Cores/MP) = %d (Cores)\n",
|
||||
deviceProp.name,
|
||||
deviceProp.multiProcessorCount,
|
||||
_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor),
|
||||
_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * deviceProp.multiProcessorCount);
|
||||
|
||||
// warmup to avoid timing startup
|
||||
kernel<<<grid, threads>>>(d_odata, d_idata, size_x, size_y);
|
||||
printf("> Compute performance scaling factor = %4.2f\n", scale_factor);
|
||||
|
||||
// take measurements for loop over kernel launches
|
||||
checkCudaErrors(cudaEventRecord(start, 0));
|
||||
// Extract parameters if there are any, command line -dimx and -dimy can
|
||||
// override any of these settings
|
||||
getParams(argc, argv, deviceProp, size_x, size_y, max_matrix_dim);
|
||||
|
||||
for (int i = 0; i < NUM_REPS; i++) {
|
||||
kernel<<<grid, threads>>>(d_odata, d_idata, size_x, size_y);
|
||||
// Ensure no launch failure
|
||||
checkCudaErrors(cudaGetLastError());
|
||||
if (size_x != size_y) {
|
||||
printf("\n[%s] does not support non-square matrices (row_dim_size(%d) != "
|
||||
"col_dim_size(%d))\nExiting...\n\n",
|
||||
sSDKsample,
|
||||
size_x,
|
||||
size_y);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaEventRecord(stop, 0));
|
||||
checkCudaErrors(cudaEventSynchronize(stop));
|
||||
float kernelTime;
|
||||
checkCudaErrors(cudaEventElapsedTime(&kernelTime, start, stop));
|
||||
|
||||
checkCudaErrors(
|
||||
cudaMemcpy(h_odata, d_odata, mem_size, cudaMemcpyDeviceToHost));
|
||||
bool res = compareData(gold, h_odata, size_x * size_y, 0.01f, 0.0f);
|
||||
|
||||
if (res == false) {
|
||||
printf("*** %s kernel FAILED ***\n", kernelName);
|
||||
success = false;
|
||||
if (size_x % TILE_DIM != 0 || size_y % TILE_DIM != 0) {
|
||||
printf("[%s] Matrix size must be integral multiple of tile "
|
||||
"size\nExiting...\n\n",
|
||||
sSDKsample);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// take measurements for loop inside kernel
|
||||
checkCudaErrors(
|
||||
cudaMemcpy(h_odata, d_odata, mem_size, cudaMemcpyDeviceToHost));
|
||||
res = compareData(gold, h_odata, size_x * size_y, 0.01f, 0.0f);
|
||||
// kernel pointer and descriptor
|
||||
void (*kernel)(float *, float *, int, int);
|
||||
const char *kernelName;
|
||||
|
||||
if (res == false) {
|
||||
printf("*** %s kernel FAILED ***\n", kernelName);
|
||||
success = false;
|
||||
// execution configuration parameters
|
||||
dim3 grid(size_x / TILE_DIM, size_y / TILE_DIM), threads(TILE_DIM, BLOCK_ROWS);
|
||||
|
||||
if (grid.x < 1 || grid.y < 1) {
|
||||
printf("[%s] grid size computation incorrect in test \nExiting...\n\n", sSDKsample);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// report effective bandwidths
|
||||
float kernelBandwidth = 2.0f * 1000.0f * mem_size / (1024 * 1024 * 1024) /
|
||||
(kernelTime / NUM_REPS);
|
||||
printf(
|
||||
"transpose %s, Throughput = %.4f GB/s, Time = %.5f ms, Size = %u fp32 "
|
||||
"elements, NumDevsUsed = %u, Workgroup = %u\n",
|
||||
kernelName, kernelBandwidth, kernelTime / NUM_REPS, (size_x * size_y),
|
||||
1, TILE_DIM * BLOCK_ROWS);
|
||||
}
|
||||
// CUDA events
|
||||
cudaEvent_t start, stop;
|
||||
|
||||
// cleanup
|
||||
free(h_idata);
|
||||
free(h_odata);
|
||||
free(transposeGold);
|
||||
cudaFree(d_idata);
|
||||
cudaFree(d_odata);
|
||||
// size of memory required to store the matrix
|
||||
size_t mem_size = static_cast<size_t>(sizeof(float) * size_x * size_y);
|
||||
|
||||
checkCudaErrors(cudaEventDestroy(start));
|
||||
checkCudaErrors(cudaEventDestroy(stop));
|
||||
if (2 * mem_size > deviceProp.totalGlobalMem) {
|
||||
printf("Input matrix size is larger than the available device memory!\n");
|
||||
printf("Please choose a smaller size matrix\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
printf("Test failed!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
// allocate host memory
|
||||
float *h_idata = (float *)malloc(mem_size);
|
||||
float *h_odata = (float *)malloc(mem_size);
|
||||
float *transposeGold = (float *)malloc(mem_size);
|
||||
float *gold;
|
||||
|
||||
printf("Test passed\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
// allocate device memory
|
||||
float *d_idata, *d_odata;
|
||||
checkCudaErrors(cudaMalloc((void **)&d_idata, mem_size));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_odata, mem_size));
|
||||
|
||||
// initialize host data
|
||||
for (int i = 0; i < (size_x * size_y); ++i) {
|
||||
h_idata[i] = (float)i;
|
||||
}
|
||||
|
||||
// copy host data to device
|
||||
checkCudaErrors(cudaMemcpy(d_idata, h_idata, mem_size, cudaMemcpyHostToDevice));
|
||||
|
||||
// Compute reference transpose solution
|
||||
computeTransposeGold(transposeGold, h_idata, size_x, size_y);
|
||||
|
||||
// print out common data for all kernels
|
||||
printf("\nMatrix size: %dx%d (%dx%d tiles), tile size: %dx%d, block size: "
|
||||
"%dx%d\n\n",
|
||||
size_x,
|
||||
size_y,
|
||||
size_x / TILE_DIM,
|
||||
size_y / TILE_DIM,
|
||||
TILE_DIM,
|
||||
TILE_DIM,
|
||||
TILE_DIM,
|
||||
BLOCK_ROWS);
|
||||
|
||||
// initialize events
|
||||
checkCudaErrors(cudaEventCreate(&start));
|
||||
checkCudaErrors(cudaEventCreate(&stop));
|
||||
|
||||
//
|
||||
// loop over different kernels
|
||||
//
|
||||
|
||||
bool success = true;
|
||||
|
||||
for (int k = 0; k < 8; k++) {
|
||||
// set kernel pointer
|
||||
switch (k) {
|
||||
case 0:
|
||||
kernel = ©
|
||||
kernelName = "simple copy ";
|
||||
break;
|
||||
|
||||
case 1:
|
||||
kernel = ©SharedMem;
|
||||
kernelName = "shared memory copy";
|
||||
break;
|
||||
|
||||
case 2:
|
||||
kernel = &transposeNaive;
|
||||
kernelName = "naive ";
|
||||
break;
|
||||
|
||||
case 3:
|
||||
kernel = &transposeCoalesced;
|
||||
kernelName = "coalesced ";
|
||||
break;
|
||||
|
||||
case 4:
|
||||
kernel = &transposeNoBankConflicts;
|
||||
kernelName = "optimized ";
|
||||
break;
|
||||
|
||||
case 5:
|
||||
kernel = &transposeCoarseGrained;
|
||||
kernelName = "coarse-grained ";
|
||||
break;
|
||||
|
||||
case 6:
|
||||
kernel = &transposeFineGrained;
|
||||
kernelName = "fine-grained ";
|
||||
break;
|
||||
|
||||
case 7:
|
||||
kernel = &transposeDiagonal;
|
||||
kernelName = "diagonal ";
|
||||
break;
|
||||
}
|
||||
|
||||
// set reference solution
|
||||
if (kernel == © || kernel == ©SharedMem) {
|
||||
gold = h_idata;
|
||||
}
|
||||
else if (kernel == &transposeCoarseGrained || kernel == &transposeFineGrained) {
|
||||
gold = h_odata; // fine- and coarse-grained kernels are not full
|
||||
// transposes, so bypass check
|
||||
}
|
||||
else {
|
||||
gold = transposeGold;
|
||||
}
|
||||
|
||||
// Clear error status
|
||||
checkCudaErrors(cudaGetLastError());
|
||||
|
||||
// warmup to avoid timing startup
|
||||
kernel<<<grid, threads>>>(d_odata, d_idata, size_x, size_y);
|
||||
|
||||
// take measurements for loop over kernel launches
|
||||
checkCudaErrors(cudaEventRecord(start, 0));
|
||||
|
||||
for (int i = 0; i < NUM_REPS; i++) {
|
||||
kernel<<<grid, threads>>>(d_odata, d_idata, size_x, size_y);
|
||||
// Ensure no launch failure
|
||||
checkCudaErrors(cudaGetLastError());
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaEventRecord(stop, 0));
|
||||
checkCudaErrors(cudaEventSynchronize(stop));
|
||||
float kernelTime;
|
||||
checkCudaErrors(cudaEventElapsedTime(&kernelTime, start, stop));
|
||||
|
||||
checkCudaErrors(cudaMemcpy(h_odata, d_odata, mem_size, cudaMemcpyDeviceToHost));
|
||||
bool res = compareData(gold, h_odata, size_x * size_y, 0.01f, 0.0f);
|
||||
|
||||
if (res == false) {
|
||||
printf("*** %s kernel FAILED ***\n", kernelName);
|
||||
success = false;
|
||||
}
|
||||
|
||||
// take measurements for loop inside kernel
|
||||
checkCudaErrors(cudaMemcpy(h_odata, d_odata, mem_size, cudaMemcpyDeviceToHost));
|
||||
res = compareData(gold, h_odata, size_x * size_y, 0.01f, 0.0f);
|
||||
|
||||
if (res == false) {
|
||||
printf("*** %s kernel FAILED ***\n", kernelName);
|
||||
success = false;
|
||||
}
|
||||
|
||||
// report effective bandwidths
|
||||
float kernelBandwidth = 2.0f * 1000.0f * mem_size / (1024 * 1024 * 1024) / (kernelTime / NUM_REPS);
|
||||
printf("transpose %s, Throughput = %.4f GB/s, Time = %.5f ms, Size = %u fp32 "
|
||||
"elements, NumDevsUsed = %u, Workgroup = %u\n",
|
||||
kernelName,
|
||||
kernelBandwidth,
|
||||
kernelTime / NUM_REPS,
|
||||
(size_x * size_y),
|
||||
1,
|
||||
TILE_DIM * BLOCK_ROWS);
|
||||
}
|
||||
|
||||
// cleanup
|
||||
free(h_idata);
|
||||
free(h_odata);
|
||||
free(transposeGold);
|
||||
cudaFree(d_idata);
|
||||
cudaFree(d_odata);
|
||||
|
||||
checkCudaErrors(cudaEventDestroy(start));
|
||||
checkCudaErrors(cudaEventDestroy(stop));
|
||||
|
||||
if (!success) {
|
||||
printf("Test failed!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("Test passed\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user