Files
TheRock/compiler/amd-llvm/offload/test/xteamr/test_xteamr.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

775 lines
31 KiB
C++

//===----- test_xteamr.cpp - Test for Xteamr DeviceRTL functions ---C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// performance and functional tests for Xteamr reduction helper functions in
// libomptarget/DeviceRTL/Xteamr.cpp
//
// RUN: %libomptarget-compileoptxx-run-and-check-nvptx64-nvidia-cuda
// REQUIRES: nvptx64-nvidia-cuda
// CHECK: ALL TESTS PASSED
//
//===----------------------------------------------------------------------===//
#include <algorithm>
#include <chrono>
#include <complex>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <limits>
#include <numeric>
#include <omp.h>
#include <vector>
#include "test_xteamr.h"
#ifndef _ARRAY_SIZE
#define _ARRAY_SIZE 33554432
#endif
const uint64_t ARRAY_SIZE = _ARRAY_SIZE;
unsigned int repeat_num_times = 12;
unsigned int ignore_times =
2; // ignore this many timings first
// If we know at compile time that we have 0 index with 1 stride,
// then generate an optimized _BIG_JUMP_LOOP.
// This test case has index 0 and stride 1, so we set this here.
#define __OPTIMIZE_INDEX0_STRIDE1
// Extern Xteamr functions are designed for 1024, 512, and 256 thread blocks.
// The default here is 512.
#ifndef _XTEAM_NUM_THREADS
#define _XTEAM_NUM_THREADS 512
#endif
#ifndef _XTEAM_NUM_TEAMS
#define _XTEAM_NUM_TEAMS 80
#endif
#if _XTEAM_NUM_THREADS == 1024
#define _SUM_OVERLOAD_64_FCT _overload_to_extern_sum_16x64
#define _SUM_OVERLOAD_32_FCT _overload_to_extern_sum_32x32
#define _MAX_OVERLOAD_64_FCT _overload_to_extern_max_16x64
#define _MAX_OVERLOAD_32_FCT _overload_to_extern_max_32x32
#define _MIN_OVERLOAD_64_FCT _overload_to_extern_min_16x64
#define _MIN_OVERLOAD_32_FCT _overload_to_extern_min_32x32
#elif _XTEAM_NUM_THREADS == 512
#define _SUM_OVERLOAD_64_FCT _overload_to_extern_sum_8x64
#define _SUM_OVERLOAD_32_FCT _overload_to_extern_sum_16x32
#define _MAX_OVERLOAD_64_FCT _overload_to_extern_max_8x64
#define _MAX_OVERLOAD_32_FCT _overload_to_extern_max_16x32
#define _MIN_OVERLOAD_64_FCT _overload_to_extern_min_8x64
#define _MIN_OVERLOAD_32_FCT _overload_to_extern_min_16x32
#elif _XTEAM_NUM_THREADS == 256
#define _SUM_OVERLOAD_64_FCT _overload_to_extern_sum_4x64
#define _SUM_OVERLOAD_32_FCT _overload_to_extern_sum_8x32
#define _MAX_OVERLOAD_64_FCT _overload_to_extern_max_4x64
#define _MAX_OVERLOAD_32_FCT _overload_to_extern_max_8x32
#define _MIN_OVERLOAD_64_FCT _overload_to_extern_min_4x64
#define _MIN_OVERLOAD_32_FCT _overload_to_extern_min_8x32
#elif _XTEAM_NUM_THREADS == 128
#define _SUM_OVERLOAD_64_FCT _overload_to_extern_sum_2x64
#define _SUM_OVERLOAD_32_FCT _overload_to_extern_sum_4x32
#define _MAX_OVERLOAD_64_FCT _overload_to_extern_max_2x64
#define _MAX_OVERLOAD_32_FCT _overload_to_extern_max_4x32
#define _MIN_OVERLOAD_64_FCT _overload_to_extern_min_2x64
#define _MIN_OVERLOAD_32_FCT _overload_to_extern_min_4x32
#elif _XTEAM_NUM_THREADS == 64
#define _SUM_OVERLOAD_64_FCT _overload_to_extern_sum_1x64
#define _SUM_OVERLOAD_32_FCT _overload_to_extern_sum_2x32
#define _MAX_OVERLOAD_64_FCT _overload_to_extern_max_1x64
#define _MAX_OVERLOAD_32_FCT _overload_to_extern_max_2x32
#define _MIN_OVERLOAD_64_FCT _overload_to_extern_min_1x64
#define _MIN_OVERLOAD_32_FCT _overload_to_extern_min_2x32
#else
#error Invalid value for _XTEAM_NUM_THREADS. Must be 1024, 512, 256, 128, or 64
#endif
// Question to Dhruva, should the limiter include the stride?
#if defined(__NVPTX__) && _XTEAM_NUM_THREADS == 1024
// Cuda may restrict max threads when requesting 1024, so the bigjump
// on the inner loop depends on the actual limited number of threads
// determined by omp_get_num_threads(). It also requires we only call
// the helper reducer function when k is in this range. Lastly, the
// helper function must clear (set to rnv) unused xwave values
// before the optimized (unrolled) xwave reduction loop. See Xteamr.cpp.
// These three things kill performance on nvidia when requested thread=1024.
// So codegen max request of 512 threads (16x32) for nvidia GPUs.
#define _LIMIT_JUMP_TO_CUDA_REDUCED_THREADS(nteams) \
if (k < (nteams * omp_get_num_threads()))
#ifdef __OPTIMIZE_INDEX0_STRIDE1
#define _BIG_JUMP_LOOP(nteams, size, stride, offset) \
for (int64_t i = k; i < size; i += nteams * omp_get_num_threads())
#else
#define _BIG_JUMP_LOOP(nteams, size, stride, offset) \
for (int64_t i = ((k * stride) + offset); i < size; \
i += (nteams * omp_get_num_threads() * stride))
#endif
#else
// Assume AMDGPU or NVIDIA=512|256 always gets requested number of
// threads.
// So no conditional needed to limit reductions.
#define _LIMIT_JUMP_TO_CUDA_REDUCED_THREADS(nteams)
// Format of BIG_JUMP_LOOP depends on if we optimize for 0 index 1 stride
#if _XTEAM_NUM_THREADS == 1024
#ifdef __OPTIMIZE_INDEX0_STRIDE1
#define _BIG_JUMP_LOOP(nteams, size, stride, offset) \
for (int64_t i = k; i < size; i += nteams * 1024)
#else
#define _BIG_JUMP_LOOP(nteams, size, stride, offset) \
for (int64_t i = ((k * stride) + offset); i < size; \
i += (nteams * 1024 * stride))
#endif
#elif _XTEAM_NUM_THREADS == 512
#ifdef __OPTIMIZE_INDEX0_STRIDE1
#define _BIG_JUMP_LOOP(nteams, size, stride, offset) \
for (int64_t i = k; i < size; i += nteams * 512)
#else
#define _BIG_JUMP_LOOP(nteams, size, stride, offset) \
for (int64_t i = ((k * stride) + offset); i < size; \
i += (nteams * 512 * stride))
#endif
#elif _XTEAM_NUM_THREADS == 256
#ifdef __OPTIMIZE_INDEX0_STRIDE1
#define _BIG_JUMP_LOOP(nteams, size, stride, offset) \
for (int64_t i = k; i < size; i += nteams * 256)
#else
#define _BIG_JUMP_LOOP(nteams, size, stride, offset) \
for (int64_t i = ((k * stride) + offset); i < size; \
i += (nteams * 256* stride))
#endif
#elif _XTEAM_NUM_THREADS == 128
#ifdef __OPTIMIZE_INDEX0_STRIDE1
#define _BIG_JUMP_LOOP(nteams, size, stride, offset) \
for (int64_t i = k; i < size; i += nteams * 128)
#else
#define _BIG_JUMP_LOOP(nteams, size, stride, offset) \
for (int64_t i = ((k * stride) + offset); i < size; \
i += (nteams * 128* stride))
#endif
#else
#ifdef __OPTIMIZE_INDEX0_STRIDE1
#define _BIG_JUMP_LOOP(nteams, size, stride, offset) \
for (int64_t i = k; i < size; i += nteams * 64)
#else
#define _BIG_JUMP_LOOP(nteams, size, stride, offset) \
for (int64_t i = ((k * stride) + offset); i < size; \
i += (nteams * 64 * stride))
#endif
#endif // end if _XTEAM_NUM_THREADS == 1024, elif,elif .. else
#endif // if defined(__NVPTX__) && _XTEAM_NUM_THREADS == 1024 else
unsigned int test_run_rc = 0;
template <typename T, bool> void run_tests(const uint64_t);
template <typename TC, typename T> void run_tests_complex(const uint64_t);
int main(int argc, char *argv[]) {
std::cout << std::endl
<< "TEST DOUBLE " << _XTEAM_NUM_THREADS << " THREADS" << std::endl;
run_tests<double, false>(ARRAY_SIZE);
std::cout << std::endl
<< "TEST FLOAT " << _XTEAM_NUM_THREADS << " THREADS" << std::endl;
run_tests<float, false>(ARRAY_SIZE);
std::cout << std::endl
<< "TEST INT " << _XTEAM_NUM_THREADS << " THREADS" << std::endl;
run_tests<int, true>(ARRAY_SIZE);
std::cout << std::endl
<< "TEST UNSIGNED INT " << _XTEAM_NUM_THREADS << " THREADS"
<< std::endl;
run_tests<unsigned, true>(ARRAY_SIZE);
std::cout << std::endl
<< "TEST LONG " << _XTEAM_NUM_THREADS << " THREADS " << std::endl;
run_tests<long, true>(ARRAY_SIZE);
std::cout << std::endl
<< "TEST UNSIGNED LONG " << _XTEAM_NUM_THREADS << " THREADS"
<< std::endl;
run_tests<unsigned long, true>(ARRAY_SIZE);
std::cout << std::endl
<< "TEST DOUBLE COMPLEX " << _XTEAM_NUM_THREADS << " THREADS"
<< std::endl;
run_tests_complex<double _Complex, double>(ARRAY_SIZE);
std::cout << std::endl
<< "TEST FLOAT COMPLEX " << _XTEAM_NUM_THREADS << " THREADS"
<< std::endl;
run_tests_complex<float _Complex, float>(ARRAY_SIZE);
if (test_run_rc == 0)
printf("ALL TESTS PASSED\n");
return test_run_rc;
}
template <typename T> T omp_dot(T *a, T *b, uint64_t array_size) {
T sum = 0.0;
#pragma omp target teams distribute parallel for map(tofrom: sum) reduction(+:sum)
for (int64_t i = 0; i < array_size; i++)
sum += a[i] * b[i];
return sum;
}
template <typename T> T omp_max(T *c, uint64_t array_size) {
T maxval = std::numeric_limits<T>::lowest();
#pragma omp target teams distribute parallel for map(tofrom \
: maxval) \
reduction(max \
: maxval)
for (int64_t i = 0; i < array_size; i++)
maxval = (c[i] > maxval) ? c[i] : maxval;
return maxval;
}
template <typename T> T omp_min(T *c, uint64_t array_size) {
T minval = std::numeric_limits<T>::max();
#pragma omp target teams distribute parallel for map(tofrom \
: minval) \
reduction(min \
: minval)
for (int64_t i = 0; i < array_size; i++) {
minval = (c[i] < minval) ? c[i] : minval;
}
return minval;
}
template <typename T> T sim_dot(T *a, T *b, int warp_size) {
T sum = T(0);
int devid = 0;
struct loop_ctl_t {
uint32_t *td_ptr; // Atomic counter accessed on device
uint32_t reserved; // reserved
const int64_t stride = 1; // stride to process input vectors
const int64_t offset = 0; // Offset to initial index of input vectors
const int64_t size = _ARRAY_SIZE; // Size of input vector
const T rnv = T(0); // reduction null value
T *team_vals; // array of global team values
};
static uint32_t zero = 0;
static loop_ctl_t lc0;
static int64_t num_teams0 = 0;
if (!num_teams0) {
// num_teams0 = ompx_get_device_num_units(devid);
num_teams0 = _XTEAM_NUM_TEAMS;
lc0.td_ptr = (uint32_t *)omp_target_alloc(sizeof(uint32_t), devid);
lc0.team_vals = (T *)omp_target_alloc(sizeof(T) * num_teams0, devid);
omp_target_memcpy(lc0.td_ptr, &zero, sizeof(uint32_t), 0, 0, devid,
omp_get_initial_device());
}
if (warp_size == 64) {
#pragma omp target teams distribute parallel for num_teams(_XTEAM_NUM_TEAMS) \
num_threads(_XTEAM_NUM_THREADS) map(tofrom \
: sum) map(to \
: lc0)
for (uint64_t k = 0; k < (_XTEAM_NUM_TEAMS * _XTEAM_NUM_THREADS); k++) {
T val0 = lc0.rnv;
_BIG_JUMP_LOOP(_XTEAM_NUM_TEAMS, lc0.size, lc0.stride, lc0.offset)
val0 += a[i] * b[i];
_SUM_OVERLOAD_64_FCT(val0, &sum, lc0.team_vals, lc0.td_ptr, lc0.rnv, k,
_XTEAM_NUM_TEAMS);
}
} else {
#pragma omp target teams distribute parallel for num_teams(_XTEAM_NUM_TEAMS) \
num_threads(_XTEAM_NUM_THREADS) map(tofrom \
: sum) map(to \
: lc0)
for (uint64_t k = 0; k < (_XTEAM_NUM_TEAMS * _XTEAM_NUM_THREADS); k++) {
T val0 = lc0.rnv;
_BIG_JUMP_LOOP(_XTEAM_NUM_TEAMS, lc0.size, lc0.stride, lc0.offset)
val0 += a[i] * b[i];
_LIMIT_JUMP_TO_CUDA_REDUCED_THREADS(_XTEAM_NUM_TEAMS)
_SUM_OVERLOAD_32_FCT(val0, &sum, lc0.team_vals, lc0.td_ptr, lc0.rnv, k,
_XTEAM_NUM_TEAMS);
}
}
return sum;
}
template <typename T> T sim_max(T *c, int warp_size) {
T retval = std::numeric_limits<T>::lowest();
int devid = 0;
struct loop_ctl_t {
uint32_t *td_ptr; // Atomic counter accessed on device
uint32_t reserved; // reserved
const int64_t stride = 1; // stride to process input vectors
const int64_t offset = 0; // Offset to index of input vectors
const int64_t size = _ARRAY_SIZE; // Size of input vectors
T rnv; // reduction null value
T *team_vals; // array of global team values
};
static uint32_t zero = 0;
static loop_ctl_t lc1;
static int64_t num_teams1 = 0;
if (!num_teams1) {
// num_teams1 = ompx_get_device_num_units(devid);
num_teams1 = _XTEAM_NUM_TEAMS;
lc1.td_ptr = (uint32_t *)omp_target_alloc(sizeof(uint32_t), devid);
lc1.rnv = std::numeric_limits<T>::lowest();
lc1.team_vals = (T *)omp_target_alloc(sizeof(T) * num_teams1, devid);
omp_target_memcpy(lc1.td_ptr, &zero, sizeof(uint32_t), 0, 0, devid,
omp_get_initial_device());
}
if (warp_size == 64) {
#pragma omp target teams distribute parallel for num_teams(_XTEAM_NUM_TEAMS) \
num_threads(_XTEAM_NUM_THREADS) map(tofrom \
: retval) map(to \
: lc1)
for (uint64_t k = 0; k < (_XTEAM_NUM_TEAMS * _XTEAM_NUM_THREADS); k++) {
T val1 = lc1.rnv;
_BIG_JUMP_LOOP(_XTEAM_NUM_TEAMS, lc1.size, lc1.stride, lc1.offset)
val1 = (c[i] > val1) ? c[i] : val1;
_MAX_OVERLOAD_64_FCT(val1, &retval, lc1.team_vals, lc1.td_ptr, lc1.rnv, k,
_XTEAM_NUM_TEAMS);
}
} else {
#pragma omp target teams distribute parallel for num_teams(_XTEAM_NUM_TEAMS) \
num_threads(_XTEAM_NUM_THREADS) map(tofrom \
: retval) map(to \
: lc1)
for (uint64_t k = 0; k < (_XTEAM_NUM_TEAMS * _XTEAM_NUM_THREADS); k++) {
T val1 = lc1.rnv;
_BIG_JUMP_LOOP(_XTEAM_NUM_TEAMS, lc1.size, lc1.stride, lc1.offset)
val1 = (c[i] > val1) ? c[i] : val1;
_LIMIT_JUMP_TO_CUDA_REDUCED_THREADS(_XTEAM_NUM_TEAMS)
_MAX_OVERLOAD_32_FCT(val1, &retval, lc1.team_vals, lc1.td_ptr, lc1.rnv, k,
_XTEAM_NUM_TEAMS);
}
}
return retval;
}
template <typename T> T sim_min(T *c, int warp_size) {
T retval = std::numeric_limits<T>::max();
int devid = 0;
struct loop_ctl_t {
uint32_t *td_ptr; // Atomic counter accessed on device
uint32_t reserved; // reserved
const int64_t stride = 1; // stride to process input vectors
const int64_t offset = 0; // Offset to initial index of input vectors
const int64_t size = _ARRAY_SIZE; // Size of input vectors
T rnv; // reduction null value
T *team_vals; // array of global team values
};
static uint32_t zero = 0;
static loop_ctl_t lc2;
static int64_t num_teams2 = 0;
if (!num_teams2) {
// num_teams2 = ompx_get_device_num_units(devid);
num_teams2 = _XTEAM_NUM_TEAMS;
lc2.td_ptr = (uint32_t *)omp_target_alloc(sizeof(uint32_t), devid);
lc2.rnv = std::numeric_limits<T>::max();
lc2.team_vals = (T *)omp_target_alloc(sizeof(T) * num_teams2, devid);
omp_target_memcpy(lc2.td_ptr, &zero, sizeof(uint32_t), 0, 0, devid,
omp_get_initial_device());
}
if (warp_size == 64) {
#pragma omp target teams distribute parallel for num_teams(_XTEAM_NUM_TEAMS) \
num_threads(_XTEAM_NUM_THREADS) map(tofrom \
: retval) map(to \
: lc2)
for (uint64_t k = 0; k < (_XTEAM_NUM_TEAMS * _XTEAM_NUM_THREADS); k++) {
T val2 = lc2.rnv;
_BIG_JUMP_LOOP(_XTEAM_NUM_TEAMS, lc2.size, lc2.stride, lc2.offset)
val2 = (c[i] < val2) ? c[i] : val2;
_MIN_OVERLOAD_64_FCT(val2, &retval, lc2.team_vals, lc2.td_ptr, lc2.rnv, k,
_XTEAM_NUM_TEAMS);
}
} else {
#pragma omp target teams distribute parallel for num_teams(_XTEAM_NUM_TEAMS) \
num_threads(_XTEAM_NUM_THREADS) map(tofrom \
: retval) map(to \
: lc2)
for (uint64_t k = 0; k < (_XTEAM_NUM_TEAMS * _XTEAM_NUM_THREADS); k++) {
T val2 = lc2.rnv;
_BIG_JUMP_LOOP(_XTEAM_NUM_TEAMS, lc2.size, lc2.stride, lc2.offset)
val2 = (c[i] < val2) ? c[i] : val2;
_LIMIT_JUMP_TO_CUDA_REDUCED_THREADS(_XTEAM_NUM_TEAMS)
_MIN_OVERLOAD_32_FCT(val2, &retval, lc2.team_vals, lc2.td_ptr, lc2.rnv, k,
_XTEAM_NUM_TEAMS);
}
}
return retval;
}
template <typename T, bool DATA_TYPE_IS_INT>
void _check_val(T computed_val, T gold_val, const char *msg) {
double ETOL = 0.0000001;
if (DATA_TYPE_IS_INT) {
if (computed_val != gold_val) {
std::cerr << msg << " FAIL "
<< "Integar Value was " << computed_val << " but should be "
<< gold_val << std::endl;
test_run_rc = 1;
}
} else {
double dcomputed_val = (double)computed_val;
double dvalgold = (double)gold_val;
double ompErrSum = abs((dcomputed_val - dvalgold) / dvalgold);
if (ompErrSum > ETOL) {
std::cerr << msg << " FAIL " << ompErrSum << " tol:" << ETOL << std::endl
<< std::setprecision(15) << "Value was " << computed_val
<< " but should be " << gold_val << std::endl;
test_run_rc = 1;
}
}
}
#define ALIGNMENT (2 * 1024 * 1024)
template <typename T, bool DATA_TYPE_IS_INT>
void run_tests(uint64_t array_size) {
// FIXME: How do we get warpsize of a device from host?
int warp_size = 64;
#pragma omp target map(tofrom : warp_size)
warp_size = __kmpc_get_warp_size();
// Align on 2M boundaries
T *a = (T *)aligned_alloc(ALIGNMENT, sizeof(T) * array_size);
T *b = (T *)aligned_alloc(ALIGNMENT, sizeof(T) * array_size);
T *c = (T *)aligned_alloc(ALIGNMENT, sizeof(T) * array_size);
#pragma omp target enter data map(alloc \
: a [0:array_size], b [0:array_size], \
c [0:array_size])
#pragma omp target teams distribute parallel for
for (int64_t i = 0; i < array_size; i++) {
a[i] = 2;
b[i] = 3;
c[i] = (i + 1);
}
std::cout << "Running kernels " << repeat_num_times << " times" << std::endl;
std::cout << "Ignoring timing of first " << ignore_times << " runs "
<< std::endl;
double ETOL = 0.0000001;
if (DATA_TYPE_IS_INT) {
std::cout << "Integer Size: " << sizeof(T) << std::endl;
} else {
if (sizeof(T) == sizeof(float))
std::cout << "Precision: float" << std::endl;
else
std::cout << "Precision: double" << std::endl;
}
std::cout << "Warp size:" << warp_size << std::endl;
// int num_teams = ompx_get_device_num_units(omp_get_default_device());
int num_teams = _XTEAM_NUM_TEAMS;
std::cout << "Array elements: " << array_size << std::endl;
std::cout << "Array size: " << ((array_size * sizeof(T)) / (1024 * 1024))
<< " MB" << std::endl;
T goldDot = (T)6 * (T)array_size;
T goldMax = (T)array_size;
T goldMin = (T)1;
double goldDot_d = (double)goldDot;
double goldMax_d = (double)goldMax;
double goldMin_d = (double)goldMin;
// List of times
std::vector<std::vector<double>> timings(6);
// Declare timers
std::chrono::high_resolution_clock::time_point t1, t2;
// Timing loop
for (unsigned int k = 0; k < repeat_num_times; k++) {
t1 = std::chrono::high_resolution_clock::now();
T omp_sum = omp_dot<T>(a, b, array_size);
t2 = std::chrono::high_resolution_clock::now();
timings[0].push_back(
std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1)
.count());
_check_val<T, DATA_TYPE_IS_INT>(omp_sum, goldDot, "omp_dot");
t1 = std::chrono::high_resolution_clock::now();
T sim_sum = sim_dot<T>(a, b, warp_size);
t2 = std::chrono::high_resolution_clock::now();
timings[1].push_back(
std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1)
.count());
_check_val<T, DATA_TYPE_IS_INT>(sim_sum, goldDot, "sim_dot");
t1 = std::chrono::high_resolution_clock::now();
T omp_max_val = omp_max<T>(c, array_size);
t2 = std::chrono::high_resolution_clock::now();
timings[2].push_back(
std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1)
.count());
_check_val<T, DATA_TYPE_IS_INT>(omp_max_val, goldMax, "omp_max");
t1 = std::chrono::high_resolution_clock::now();
T sim_max_val = sim_max<T>(c, warp_size);
t2 = std::chrono::high_resolution_clock::now();
timings[3].push_back(
std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1)
.count());
_check_val<T, DATA_TYPE_IS_INT>(sim_max_val, goldMax, "sim_max");
t1 = std::chrono::high_resolution_clock::now();
T omp_min_val = omp_min<T>(c, array_size);
t2 = std::chrono::high_resolution_clock::now();
timings[4].push_back(
std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1)
.count());
_check_val<T, DATA_TYPE_IS_INT>(omp_min_val, goldMin, "omp_min");
t1 = std::chrono::high_resolution_clock::now();
T sim_min_val = sim_min<T>(c, warp_size);
t2 = std::chrono::high_resolution_clock::now();
timings[5].push_back(
std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1)
.count());
_check_val<T, DATA_TYPE_IS_INT>(sim_min_val, goldMin, "sim_min");
} // end Timing loop
// Display timing results
std::cout << std::left << std::setw(12) << "Function" << std::left
<< std::setw(12) << "Best-MB/sec" << std::left << std::setw(12)
<< " Min (sec)" << std::left << std::setw(12) << " Max"
<< std::left << std::setw(12) << "Average" << std::left
<< std::setw(12) << "Avg-MB/sec" << std::endl;
std::cout << std::fixed;
std::string labels[6] = {"ompdot", "simdot", "ompmax",
"simmax", "ompmin", "simmin"};
size_t sizes[6] = {2 * sizeof(T) * array_size, 2 * sizeof(T) * array_size,
1 * sizeof(T) * array_size, 1 * sizeof(T) * array_size,
1 * sizeof(T) * array_size, 1 * sizeof(T) * array_size};
for (int i = 0; i < 6; i++) {
// Get min/max; ignore the first couple results
auto minmax = std::minmax_element(timings[i].begin() + ignore_times,
timings[i].end());
// Calculate average; ignore ignore_times
double average = std::accumulate(timings[i].begin() + ignore_times,
timings[i].end(), 0.0) /
(double)(repeat_num_times - ignore_times);
printf(" %s %8.0f %8.6f %8.6f %8.6f %8.0f\n",
labels[i].c_str(), 1.0E-6 * sizes[i] / (*minmax.first),
(double)*minmax.first, (double)*minmax.second, (double)average,
1.0E-6 * sizes[i] / (average));
}
#pragma omp target exit data map(release \
: a [0:array_size], b [0:array_size], \
c [0:array_size])
free(a);
free(b);
free(c);
}
template <typename TC, typename T>
void _check_val_complex(TC computed_val_complex, TC gold_val_complex,
const char *msg) {
T gold_val_r = __real__(gold_val_complex);
T computed_val_r = __real__(computed_val_complex);
T gold_val_i = __imag__(gold_val_complex);
T computed_val_i = __imag__(computed_val_complex);
double ETOL = 0.0000001;
double computed_val_r_d = (double)computed_val_r;
double valgold_r_d = (double)gold_val_r;
double ompErrSum_r = abs((computed_val_r_d - valgold_r_d) / valgold_r_d);
double computed_val_i_d = (double)computed_val_i;
double valgold_i_d = (double)gold_val_i;
double ompErrSum_i = abs((computed_val_i_d - valgold_i_d) / valgold_i_d);
if ((ompErrSum_r > ETOL) || (ompErrSum_i > ETOL)) {
std::cerr << msg << " FAIL " << ompErrSum_r << " tol:" << ETOL << std::endl
<< std::setprecision(15) << "Value was (" << computed_val_r
<< " + " << computed_val_i << " i )" << std::endl
<< " but should be (" << gold_val_r << " + " << gold_val_i
<< "i) " << std::endl;
test_run_rc = 1;
}
}
template <typename TC> TC omp_dot_complex(TC *a, TC *b, uint64_t array_size) {
TC dot;
__real__(dot) = 0.0;
__imag__(dot) = 0.0;
#pragma omp target teams distribute parallel for map(tofrom: dot) reduction(+:dot)
for (int64_t i = 0; i < array_size; i++)
dot += a[i] * b[i];
return dot;
}
template <typename T> T sim_dot_complex(T *a, T *b, int warp_size) {
int devid = 0;
T zero_c;
__real__(zero_c) = 0.0;
__imag__(zero_c) = 0.0;
struct loop_ctl_t {
uint32_t *td_ptr; // Atomic counter accessed on device
uint32_t reserved; // reserved
const int64_t stride = 1; // stride to process input vectors
const int64_t offset = 0; // Offset to initial index of input vectors
const int64_t size = _ARRAY_SIZE; // Size of input vectors
T rnv; // reduction null value
T *team_vals; // array of global team values
};
T sum = zero_c;
uint32_t zero = 0;
static loop_ctl_t lc3;
static int64_t num_teams3 = 0;
if (!num_teams3) {
// num_teams3 = ompx_get_device_num_units(devid);
num_teams3 = _XTEAM_NUM_TEAMS;
lc3.td_ptr = (uint32_t *)omp_target_alloc(sizeof(uint32_t), devid);
lc3.team_vals = (T *)omp_target_alloc(sizeof(T) * num_teams3, devid);
lc3.rnv = zero_c;
omp_target_memcpy(lc3.td_ptr, &zero, sizeof(uint32_t), 0, 0, devid,
omp_get_initial_device());
}
if (warp_size == 64) {
#pragma omp target teams distribute parallel for num_teams(_XTEAM_NUM_TEAMS) \
num_threads(_XTEAM_NUM_THREADS) map(tofrom \
: sum) map(to \
: lc3)
for (uint64_t k = 0; k < (_XTEAM_NUM_TEAMS * _XTEAM_NUM_THREADS); k++) {
T val3 = lc3.rnv;
_BIG_JUMP_LOOP(_XTEAM_NUM_TEAMS, lc3.size, lc3.stride, lc3.offset)
val3 += a[i] * b[i];
_SUM_OVERLOAD_64_FCT(val3, &sum, lc3.team_vals, lc3.td_ptr, lc3.rnv, k,
_XTEAM_NUM_TEAMS);
}
} else {
#pragma omp target teams distribute parallel for num_teams(_XTEAM_NUM_TEAMS) \
num_threads(_XTEAM_NUM_THREADS) map(tofrom \
: sum) map(to \
: lc3)
for (uint64_t k = 0; k < (_XTEAM_NUM_TEAMS * _XTEAM_NUM_THREADS); k++) {
T val3 = lc3.rnv;
_BIG_JUMP_LOOP(_XTEAM_NUM_TEAMS, lc3.size, lc3.stride, lc3.offset)
val3 += a[i] * b[i];
_LIMIT_JUMP_TO_CUDA_REDUCED_THREADS(_XTEAM_NUM_TEAMS)
_SUM_OVERLOAD_32_FCT(val3, &sum, lc3.team_vals, lc3.td_ptr, lc3.rnv, k,
_XTEAM_NUM_TEAMS);
}
}
return sum;
}
template <typename TC, typename T>
void run_tests_complex(const uint64_t array_size) {
// FIXME: How do we get warpsize of a device from host?
int warp_size = 64;
#pragma omp target map(tofrom : warp_size)
warp_size = __kmpc_get_warp_size();
TC *a = (TC *)aligned_alloc(ALIGNMENT, sizeof(TC) * array_size);
TC *b = (TC *)aligned_alloc(ALIGNMENT, sizeof(TC) * array_size);
#pragma omp target enter data map(alloc : a [0:array_size], b [0:array_size])
TC startA;
__real__(startA) = 1.0;
__imag__(startA) = 1.0;
TC startB;
__real__(startB) = 1.0;
__imag__(startB) = -1.0;
#pragma omp target teams distribute parallel for
for (int64_t i = 0; i < array_size; i++) {
a[i] = startA;
b[i] = startB;
// a[i] * b[i] = 2 + 0i
}
std::cout << "Running kernels " << repeat_num_times << " times" << std::endl;
std::cout << "Ignoring timing of first " << ignore_times << " runs "
<< std::endl;
double ETOL = 0.0000001;
if (sizeof(TC) == sizeof(float _Complex))
std::cout << "Precision: float _Complex" << std::endl;
else
std::cout << "Precision: double _Complex" << std::endl;
std::cout << "Warp size:" << warp_size << std::endl;
std::cout << "Array elements: " << array_size << std::endl;
std::cout << "Array size: " << ((array_size * sizeof(TC)) / (1024 * 1024))
<< " MB" << std::endl;
T goldDotr = T(2) * (T)array_size;
T goldDoti = T(0);
TC goldDot;
__real__(goldDot) = goldDotr;
__imag__(goldDot) = goldDoti;
// List of times
std::vector<std::vector<double>> timings(2);
// Declare timers
std::chrono::high_resolution_clock::time_point t1, t2;
// timing loop
for (unsigned int k = 0; k < repeat_num_times; k++) {
t1 = std::chrono::high_resolution_clock::now();
TC omp_sum = omp_dot_complex<TC>(a, b, array_size);
t2 = std::chrono::high_resolution_clock::now();
timings[0].push_back(
std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1)
.count());
_check_val_complex<TC, T>(omp_sum, goldDot, "omp_dot");
t1 = std::chrono::high_resolution_clock::now();
TC sim_sum = sim_dot_complex<TC>(a, b, warp_size);
t2 = std::chrono::high_resolution_clock::now();
timings[1].push_back(
std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1)
.count());
_check_val_complex<TC, T>(sim_sum, goldDot, "sim_dot");
} // end timing loop
// Display timing results
std::cout << std::left << std::setw(12) << "Function" << std::left
<< std::setw(12) << "Best-MB/sec" << std::left << std::setw(12)
<< " Min (sec)" << std::left << std::setw(12) << " Max"
<< std::left << std::setw(12) << "Average" << std::left
<< std::setw(12) << "Avg-MB/sec" << std::endl;
std::cout << std::fixed;
std::string labels[2] = {"ompdot", "simdot"};
size_t sizes[2] = {2 * sizeof(TC) * array_size, 2 * sizeof(TC) * array_size};
for (int i = 0; i < 2; i++) {
// Get min/max; ignore the first couple results
auto minmax = std::minmax_element(timings[i].begin() + ignore_times,
timings[i].end());
// Calculate average; ignore ignore_times
double average = std::accumulate(timings[i].begin() + ignore_times,
timings[i].end(), 0.0) /
(double)(repeat_num_times - ignore_times);
printf(" %s %8.0f %8.6f %8.6f %8.6f %8.0f\n",
labels[i].c_str(), 1.0E-6 * sizes[i] / (*minmax.first),
(double)*minmax.first, (double)*minmax.second, (double)average,
1.0E-6 * sizes[i] / (average));
}
#pragma omp target exit data map(release : a [0:array_size], b [0:array_size])
free(a);
free(b);
}