Live attach/detach and its unit tests (#53)
This commit is contained in:
کامیت شده توسط
GitHub
والد
9278770b89
کامیت
872f0aed0c
@@ -0,0 +1,174 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "example_utils.hpp"
|
||||
|
||||
#include <hip/hip_runtime.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <thread>
|
||||
|
||||
/// \brief A simple matrix transpose kernel that using dynamic shared memory.
|
||||
/// - The number of rows in the input and output matrices is equal, and given by the \p width parameter.
|
||||
/// - Each thread in the grid is responsible for one element of the input and output matrices.
|
||||
/// - Because the transposition is computed in shared memory, which cannot be accessed between different
|
||||
/// blocks, the matrix has to be processed by a single block.
|
||||
__global__ void matrix_transpose_kernel(float* out, const float* in, const unsigned int width)
|
||||
{
|
||||
// Declare that this kernel is using dynamic shared memory to store a number of floats.
|
||||
// The unsized array type indicates that the total amount of memory that is going
|
||||
// to be used here is not known ahead of time, and will be computed at runtime and
|
||||
// passed to the kernel launch function.
|
||||
extern __shared__ float shared_matrix_memory[];
|
||||
|
||||
// Compute the row and column index of the element this thread is going to process.
|
||||
const unsigned int x = blockDim.x * blockIdx.x + threadIdx.x;
|
||||
const unsigned int y = blockDim.y * blockIdx.y + threadIdx.y;
|
||||
|
||||
// Perform the transpose by reading an element of the input matrix from global memory and
|
||||
// by storing it in the tranposed index in shared memory.
|
||||
shared_matrix_memory[y * width + x] = in[x * width + y];
|
||||
|
||||
// Synchronization is required to make sure that all threads have written
|
||||
// their part of the input matrix to the shared memory, before the values
|
||||
// are read by another thread.
|
||||
__syncthreads();
|
||||
|
||||
// Copy the transposed matrix from shared memory to the output array, which
|
||||
// is in global memory.
|
||||
out[y * width + x] = shared_matrix_memory[y * width + x];
|
||||
}
|
||||
|
||||
// CPU implementation of matrix transpose
|
||||
std::vector<float> matrix_transpose_reference(const std::vector<float>& input,
|
||||
const unsigned int width)
|
||||
{
|
||||
std::vector<float> output(width * width);
|
||||
for(unsigned int j = 0; j < width; j++)
|
||||
{
|
||||
for(unsigned int i = 0; i < width; i++)
|
||||
{
|
||||
output[i * width + j] = input[j * width + i];
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Number of rows and columns in the transposed square matrix.
|
||||
constexpr unsigned int width = 4;
|
||||
|
||||
// Number of threads in each kernel block along the X dimension.
|
||||
// Because each thread will process exactly one element, this value
|
||||
// is equal to the width of the matrix.
|
||||
constexpr unsigned int threads_per_block_x = width;
|
||||
|
||||
// Number of threads in each kernel block along the Y dimension.
|
||||
// Because each thread will process exactly one element, this value
|
||||
// is equal to the width of the matrix.
|
||||
constexpr unsigned int threads_per_block_y = width;
|
||||
|
||||
// Total element count of the transposed matrix.
|
||||
constexpr unsigned int size = width * width;
|
||||
|
||||
// Total size (in bytes) of the transposed matrix.
|
||||
constexpr size_t size_bytes = sizeof(float) * size;
|
||||
|
||||
// Total amount of shared memory that each block is going to use.
|
||||
// Exactly one matrix will be stored in shared memory.
|
||||
constexpr size_t shared_memory_bytes = size_bytes;
|
||||
std::cout << "Run transpose continuously" << std::endl;
|
||||
|
||||
// Set a timer to 30 seconds for rocprofv3 preparation
|
||||
std::this_thread::sleep_for(std::chrono::seconds(30));
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
// Allocate host vectors.
|
||||
std::vector<float> h_matrix(size);
|
||||
std::vector<float> h_transposed_matrix(size);
|
||||
|
||||
// Set up input data.
|
||||
for(unsigned int i = 0; i < size; i++)
|
||||
{
|
||||
h_matrix[i] = i * 10.0f;
|
||||
}
|
||||
|
||||
// Allocate device memory for the input and output matrices.
|
||||
float* d_matrix{};
|
||||
float* d_transposed_matrix{};
|
||||
HIP_CHECK(hipMalloc(&d_matrix, size_bytes));
|
||||
HIP_CHECK(hipMalloc(&d_transposed_matrix, size_bytes));
|
||||
|
||||
// Transfer the input matrix to the device memory.
|
||||
HIP_CHECK(hipMemcpy(d_matrix, h_matrix.data(), size_bytes, hipMemcpyHostToDevice));
|
||||
|
||||
// Lauching kernel from host.
|
||||
matrix_transpose_kernel<<<dim3(width / threads_per_block_x, width / threads_per_block_y),
|
||||
dim3(threads_per_block_x, threads_per_block_y),
|
||||
shared_memory_bytes,
|
||||
hipStreamDefault>>>(d_transposed_matrix, d_matrix, width);
|
||||
|
||||
// Check if the kernel launch was successful.
|
||||
HIP_CHECK(hipGetLastError());
|
||||
|
||||
// Transfer the result back to the host.
|
||||
HIP_CHECK(hipMemcpy(h_transposed_matrix.data(),
|
||||
d_transposed_matrix,
|
||||
size_bytes,
|
||||
hipMemcpyDeviceToHost));
|
||||
|
||||
// Free the resources on the device.
|
||||
HIP_CHECK(hipFree(d_matrix));
|
||||
HIP_CHECK(hipFree(d_transposed_matrix));
|
||||
|
||||
// Perform the reference (CPU) calculation.
|
||||
std::vector<float> ref_transposed_matrix = matrix_transpose_reference(h_matrix, width);
|
||||
|
||||
// Check the results' validity.
|
||||
constexpr float eps = 1.0E-6f;
|
||||
unsigned int errors{};
|
||||
for(unsigned int i = 0; i < size; i++)
|
||||
{
|
||||
if(std::fabs(h_transposed_matrix[i] - ref_transposed_matrix[i]) > eps)
|
||||
{
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
if(errors != 0)
|
||||
{
|
||||
std::cout << "Validation failed. Errors: " << errors << std::endl;
|
||||
return error_exit_code;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Validation passed." << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef COMMON_EXAMPLE_UTILS_HPP
|
||||
#define COMMON_EXAMPLE_UTILS_HPP
|
||||
|
||||
// Compiling HIP on Windows includes windows.h, and this triggers many silly warnings.
|
||||
#include <cstdint>
|
||||
#if defined(_WIN32) && defined(__NVCC__)
|
||||
#pragma nv_diag_suppress 108 // signed bit field of length 1
|
||||
#pragma nv_diag_suppress 174 // expression has no effect
|
||||
#pragma nv_diag_suppress 1835 // attribute "dllimport" does not apply here
|
||||
#endif
|
||||
|
||||
// rocPRIM adds a #warning about printf on NAVI.
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-W#warnings"
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <hip/hip_runtime.h>
|
||||
|
||||
constexpr int error_exit_code = -1;
|
||||
|
||||
/// \brief Checks if the provided error code is \p hipSuccess and if not,
|
||||
/// prints an error message to the standard error output and terminates the program
|
||||
/// with an error code.
|
||||
#define HIP_CHECK(condition) \
|
||||
{ \
|
||||
const hipError_t error = condition; \
|
||||
if(error != hipSuccess) \
|
||||
{ \
|
||||
std::cerr << "An error encountered: \"" << hipGetErrorString(error) << "\" at " \
|
||||
<< __FILE__ << ':' << __LINE__ << std::endl; \
|
||||
std::exit(error_exit_code); \
|
||||
} \
|
||||
}
|
||||
|
||||
/// \brief Formats a range of elements to a pretty string.
|
||||
/// \tparam BidirectionalIterator - must implement the BidirectionalIterator concept and
|
||||
/// must be dereferencable in host code. Its value type must be formattable to
|
||||
/// \p std::ostream.
|
||||
template<class BidirectionalIterator>
|
||||
inline std::string format_range(const BidirectionalIterator begin, const BidirectionalIterator end)
|
||||
{
|
||||
std::stringstream sstream;
|
||||
sstream << "[ ";
|
||||
for(auto it = begin; it != end; ++it)
|
||||
{
|
||||
sstream << *it;
|
||||
if(it != std::prev(end))
|
||||
{
|
||||
sstream << ", ";
|
||||
}
|
||||
}
|
||||
sstream << " ]";
|
||||
return sstream.str();
|
||||
}
|
||||
|
||||
/// \brief Formats a range of pairs to a pretty string. The length of the two ranges must match.
|
||||
/// \tparam BidirectionalIteratorT - must implement the BidirectionalIterator concept and
|
||||
/// must be dereferencable in host code. Its value type must be formattable to \p std::ostream.
|
||||
/// \tparam BidirectionalIteratorU - must implement the BidirectionalIterator concept and
|
||||
/// must be dereferencable in host code. Its value type must be formattable to \p std::ostream.
|
||||
template<class BidirectionalIteratorT, typename BidirectionalIteratorU>
|
||||
inline std::string format_pairs(const BidirectionalIteratorT begin_a,
|
||||
const BidirectionalIteratorT end_a,
|
||||
const BidirectionalIteratorU begin_b,
|
||||
const BidirectionalIteratorU end_b)
|
||||
{
|
||||
(void)end_b;
|
||||
assert(std::distance(begin_a, end_a) == std::distance(begin_b, end_b));
|
||||
|
||||
std::stringstream sstream;
|
||||
sstream << "[ ";
|
||||
auto it_a = begin_a;
|
||||
auto it_b = begin_b;
|
||||
for(; it_a < end_a; ++it_a, ++it_b)
|
||||
{
|
||||
sstream << "(" << *it_a << ", " << *it_b << ")";
|
||||
|
||||
if(it_a != std::prev(end_a))
|
||||
{
|
||||
sstream << ", ";
|
||||
}
|
||||
}
|
||||
sstream << " ]";
|
||||
return sstream.str();
|
||||
}
|
||||
|
||||
/// \brief A function to parse a string for an int. If the string is a valid integer then return true
|
||||
/// else if it has non-numeric character then return false.
|
||||
inline bool parse_int_string(const std::string& str, int& out)
|
||||
{
|
||||
try
|
||||
{
|
||||
size_t end;
|
||||
int value = std::stoi(str, &end);
|
||||
if(end == str.size())
|
||||
{
|
||||
out = value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch(const std::exception&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief A class to measures time between intervals
|
||||
class HostClock
|
||||
{
|
||||
private:
|
||||
std::chrono::steady_clock::time_point start_time;
|
||||
std::chrono::steady_clock::duration elapsed_time;
|
||||
|
||||
public:
|
||||
HostClock()
|
||||
{
|
||||
this->reset_timer();
|
||||
}
|
||||
|
||||
inline void reset_timer()
|
||||
{
|
||||
this->elapsed_time = std::chrono::steady_clock::duration(0);
|
||||
}
|
||||
|
||||
inline void start_timer()
|
||||
{
|
||||
this->start_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
inline void stop_timer()
|
||||
{
|
||||
const auto end_time = std::chrono::steady_clock::now();
|
||||
this->elapsed_time += end_time - this->start_time;
|
||||
}
|
||||
|
||||
/// @brief Returns time elapsed in Seconds
|
||||
/// @return type double that contains the elapsed time in Seconds
|
||||
inline double get_elapsed_time() const
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::duration<double>>(this->elapsed_time)
|
||||
.count();
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Returns <tt>ceil(dividend / divisor)</tt>, where \p dividend is an integer and
|
||||
/// \p divisor is an unsigned integer.
|
||||
template<typename T,
|
||||
typename U,
|
||||
std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<U>::value, int> = 0>
|
||||
__host__ __device__ constexpr auto ceiling_div(const T& dividend, const U& divisor)
|
||||
{
|
||||
return (dividend + divisor - 1) / divisor;
|
||||
}
|
||||
|
||||
/// \brief Report validation results.
|
||||
inline int report_validation_result(int errors)
|
||||
{
|
||||
if(errors)
|
||||
{
|
||||
std::cout << "Validation failed. Errors: " << errors << std::endl;
|
||||
return error_exit_code;
|
||||
}
|
||||
|
||||
std::cout << "Validation passed." << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \brief Generate an identity matrix.
|
||||
/// The identity matrix is a $m \times n$ matrix with ones in the main diagonal and zeros elsewhere.
|
||||
template<typename T>
|
||||
void generate_identity_matrix(T* A, int m, int n, size_t lda)
|
||||
{
|
||||
for(int i = 0; i < m; ++i)
|
||||
{
|
||||
for(int j = 0; j < n; ++j)
|
||||
{
|
||||
A[i + j * lda] = T(i == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Multiply an $A$ matrix ($m \times k$) with a $B$ matrix ($k \times n$) as:
|
||||
/// $C := \alpha \cdot A \cdot B + \beta \cdot C$
|
||||
template<typename T>
|
||||
void multiply_matrices(T alpha,
|
||||
T beta,
|
||||
int m,
|
||||
int n,
|
||||
int k,
|
||||
const T* A,
|
||||
int stride1_a,
|
||||
int stride2_a,
|
||||
const T* B,
|
||||
int stride1_b,
|
||||
int stride2_b,
|
||||
T* C,
|
||||
int stride_c)
|
||||
{
|
||||
for(int i1 = 0; i1 < m; ++i1)
|
||||
{
|
||||
for(int i2 = 0; i2 < n; ++i2)
|
||||
{
|
||||
T t = T(0.0);
|
||||
for(int i3 = 0; i3 < k; ++i3)
|
||||
{
|
||||
t += A[i1 * stride1_a + i3 * stride2_a] * B[i3 * stride1_b + i2 * stride2_b];
|
||||
}
|
||||
C[i1 + i2 * stride_c] = beta * C[i1 + i2 * stride_c] + alpha * t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Prints an {1,2,3}-dimensional array. The last dimension (fastest-index) specified in
|
||||
/// \p n will be printed horizontally.
|
||||
///
|
||||
/// By default a row-major layout of the data is assumed. When printing data in column-major
|
||||
/// layout, the \p column_major parameter must be set to \p true for a correct interpretation
|
||||
/// of the dimensions' sizes.
|
||||
template<class Tdata, class Tsize>
|
||||
void print_nd_data(const std::vector<Tdata>& data,
|
||||
std::vector<Tsize> np,
|
||||
const int column_width = 4,
|
||||
const bool column_major = false)
|
||||
{
|
||||
if(column_major)
|
||||
{
|
||||
std::reverse(np.begin(), np.end());
|
||||
}
|
||||
const std::vector<Tsize> n(np);
|
||||
// Note: we want to print the last dimension horizontally (on the x-axis)!
|
||||
int size_x = n[n.size() - 1];
|
||||
int size_y = n.size() > 1 ? n[n.size() - 2] : 1;
|
||||
int size_z = n.size() > 2 ? n[n.size() - 3] : 1;
|
||||
for(int z = 0; z < size_z; ++z)
|
||||
{
|
||||
for(int y = 0; y < size_y; ++y)
|
||||
{
|
||||
for(int x = 0; x < size_x; ++x)
|
||||
{
|
||||
auto index = (z * size_y + y) * size_x + x;
|
||||
std::cout << std::setfill(' ') << std::setw(column_width) << data[index] << " ";
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
if(z != size_z - 1)
|
||||
{
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
std::cout << std::flush;
|
||||
}
|
||||
|
||||
/// \brief Returns a string from the double \p value with specified \p precision .
|
||||
inline std::string
|
||||
double_precision(const double value, const int precision, const bool fixed = false)
|
||||
{
|
||||
std::stringstream ss;
|
||||
if(fixed)
|
||||
{
|
||||
ss << std::fixed;
|
||||
}
|
||||
ss << std::setprecision(precision) << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#endif // COMMON_EXAMPLE_UTILS_HPP
|
||||
مرجع در شماره جدید
Block a user