From 8c72b52f16a21cd77181ebeaac32f794075377c0 Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Tue, 12 Jan 2021 11:00:54 +0000 Subject: [PATCH 01/78] [SYCL 2020] Use unnamed lamdas --- SYCLStream.cpp | 12 ++++++------ SYCLStream.h | 18 ------------------ 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index 8ab642f..dfaf13c 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -91,7 +91,7 @@ void SYCLStream::copy() { auto ka = d_a->template get_access(cgh); auto kc = d_c->template get_access(cgh); - cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) + cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) { kc[idx] = ka[idx]; }); @@ -107,7 +107,7 @@ void SYCLStream::mul() { auto kb = d_b->template get_access(cgh); auto kc = d_c->template get_access(cgh); - cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) + cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) { kb[idx] = scalar * kc[idx]; }); @@ -123,7 +123,7 @@ void SYCLStream::add() auto ka = d_a->template get_access(cgh); auto kb = d_b->template get_access(cgh); auto kc = d_c->template get_access(cgh); - cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) + cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) { kc[idx] = ka[idx] + kb[idx]; }); @@ -140,7 +140,7 @@ void SYCLStream::triad() auto ka = d_a->template get_access(cgh); auto kb = d_b->template get_access(cgh); auto kc = d_c->template get_access(cgh); - cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) + cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) { ka[idx] = kb[idx] + scalar * kc[idx]; }); @@ -160,7 +160,7 @@ T SYCLStream::dot() auto wg_sum = accessor(range<1>(dot_wgsize), cgh); size_t N = array_size; - cgh.parallel_for(nd_range<1>(dot_num_groups*dot_wgsize, dot_wgsize), [=](nd_item<1> item) + cgh.parallel_for(nd_range<1>(dot_num_groups*dot_wgsize, dot_wgsize), [=](nd_item<1> item) { size_t i = item.get_global_id(0); size_t li = item.get_local_id(0); @@ -201,7 +201,7 @@ void SYCLStream::init_arrays(T initA, T initB, T initC) auto ka = d_a->template get_access(cgh); auto kb = d_b->template get_access(cgh); auto kc = d_c->template get_access(cgh); - cgh.parallel_for(range<1>{array_size}, [=](item<1> item) + cgh.parallel_for(range<1>{array_size}, [=](item<1> item) { auto id = item.get_id(0); ka[id] = initA; diff --git a/SYCLStream.h b/SYCLStream.h index df10946..cb1a45a 100644 --- a/SYCLStream.h +++ b/SYCLStream.h @@ -15,16 +15,6 @@ #define IMPLEMENTATION_STRING "SYCL" -namespace sycl_kernels -{ - template class init; - template class copy; - template class mul; - template class add; - template class triad; - template class dot; -} - template class SYCLStream : public Stream { @@ -39,14 +29,6 @@ class SYCLStream : public Stream cl::sycl::buffer *d_c; cl::sycl::buffer *d_sum; - // SYCL kernel names - typedef sycl_kernels::init init_kernel; - typedef sycl_kernels::copy copy_kernel; - typedef sycl_kernels::mul mul_kernel; - typedef sycl_kernels::add add_kernel; - typedef sycl_kernels::triad triad_kernel; - typedef sycl_kernels::dot dot_kernel; - // NDRange configuration for the dot kernel size_t dot_num_groups; size_t dot_wgsize; From e8faf6843d498daff1d37cba235decc2a9fa8366 Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Tue, 12 Jan 2021 11:01:11 +0000 Subject: [PATCH 02/78] Remove old comment --- SYCLStream.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index dfaf13c..8f47304 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -292,6 +292,5 @@ std::string getDeviceDriver(const int device) return driver; } -// TODO: Fix kernel names to allow multiple template specializations template class SYCLStream; template class SYCLStream; From 501c61cfbde1fc30d21c5fd05f666860b4fe5485 Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Tue, 12 Jan 2021 11:14:43 +0000 Subject: [PATCH 03/78] [SYCL 2020] update namespace from cl::sycl to sycl:: Also remove the use namespace to make it clear what comes from SYCL --- SYCLStream.cpp | 101 ++++++++++++++++++++++++------------------------- SYCLStream.h | 10 ++--- 2 files changed, 55 insertions(+), 56 deletions(-) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index 8f47304..984d8d2 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -9,11 +9,9 @@ #include -using namespace cl::sycl; - // Cache list of devices bool cached = false; -std::vector devices; +std::vector devices; void getDeviceList(void); template @@ -26,18 +24,19 @@ SYCLStream::SYCLStream(const int ARRAY_SIZE, const int device_index) if (device_index >= devices.size()) throw std::runtime_error("Invalid device index"); - device dev = devices[device_index]; + + sycl::device dev = devices[device_index]; // Determine sensible dot kernel NDRange configuration if (dev.is_cpu()) { - dot_num_groups = dev.get_info(); - dot_wgsize = dev.get_info() * 2; + dot_num_groups = dev.get_info(); + dot_wgsize = dev.get_info() * 2; } else { - dot_num_groups = dev.get_info() * 4; - dot_wgsize = dev.get_info(); + dot_num_groups = dev.get_info() * 4; + dot_wgsize = dev.get_info(); } // Print out device information @@ -45,7 +44,7 @@ SYCLStream::SYCLStream(const int ARRAY_SIZE, const int device_index) std::cout << "Driver: " << getDeviceDriver(device_index) << std::endl; std::cout << "Reduction kernel config: " << dot_num_groups << " groups of size " << dot_wgsize << std::endl; - queue = new cl::sycl::queue(dev, cl::sycl::async_handler{[&](cl::sycl::exception_list l) + queue = new sycl::queue(dev, sycl::async_handler{[&](sycl::exception_list l) { bool error = false; for(auto e: l) @@ -54,7 +53,7 @@ SYCLStream::SYCLStream(const int ARRAY_SIZE, const int device_index) { std::rethrow_exception(e); } - catch (cl::sycl::exception e) + catch (sycl::exception e) { std::cout << e.what(); error = true; @@ -67,10 +66,10 @@ SYCLStream::SYCLStream(const int ARRAY_SIZE, const int device_index) }}); // Create buffers - d_a = new buffer(array_size); - d_b = new buffer(array_size); - d_c = new buffer(array_size); - d_sum = new buffer(dot_num_groups); + d_a = new sycl::buffer(array_size); + d_b = new sycl::buffer(array_size); + d_c = new sycl::buffer(array_size); + d_sum = new sycl::buffer(dot_num_groups); } template @@ -87,11 +86,11 @@ SYCLStream::~SYCLStream() template void SYCLStream::copy() { - queue->submit([&](handler &cgh) + queue->submit([&](sycl::handler &cgh) { - auto ka = d_a->template get_access(cgh); - auto kc = d_c->template get_access(cgh); - cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) + auto ka = d_a->template get_access(cgh); + auto kc = d_c->template get_access(cgh); + cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { kc[idx] = ka[idx]; }); @@ -103,11 +102,11 @@ template void SYCLStream::mul() { const T scalar = startScalar; - queue->submit([&](handler &cgh) + queue->submit([&](sycl::handler &cgh) { - auto kb = d_b->template get_access(cgh); - auto kc = d_c->template get_access(cgh); - cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) + auto kb = d_b->template get_access(cgh); + auto kc = d_c->template get_access(cgh); + cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { kb[idx] = scalar * kc[idx]; }); @@ -118,12 +117,12 @@ void SYCLStream::mul() template void SYCLStream::add() { - queue->submit([&](handler &cgh) + queue->submit([&](sycl::handler &cgh) { - auto ka = d_a->template get_access(cgh); - auto kb = d_b->template get_access(cgh); - auto kc = d_c->template get_access(cgh); - cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) + auto ka = d_a->template get_access(cgh); + auto kb = d_b->template get_access(cgh); + auto kc = d_c->template get_access(cgh); + cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { kc[idx] = ka[idx] + kb[idx]; }); @@ -135,12 +134,12 @@ template void SYCLStream::triad() { const T scalar = startScalar; - queue->submit([&](handler &cgh) + queue->submit([&](sycl::handler &cgh) { - auto ka = d_a->template get_access(cgh); - auto kb = d_b->template get_access(cgh); - auto kc = d_c->template get_access(cgh); - cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) + auto ka = d_a->template get_access(cgh); + auto kb = d_b->template get_access(cgh); + auto kc = d_c->template get_access(cgh); + cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { ka[idx] = kb[idx] + scalar * kc[idx]; }); @@ -151,16 +150,16 @@ void SYCLStream::triad() template T SYCLStream::dot() { - queue->submit([&](handler &cgh) + queue->submit([&](sycl::handler &cgh) { - auto ka = d_a->template get_access(cgh); - auto kb = d_b->template get_access(cgh); - auto ksum = d_sum->template get_access(cgh); + auto ka = d_a->template get_access(cgh); + auto kb = d_b->template get_access(cgh); + auto ksum = d_sum->template get_access(cgh); - auto wg_sum = accessor(range<1>(dot_wgsize), cgh); + auto wg_sum = sycl::accessor(sycl::range<1>(dot_wgsize), cgh); size_t N = array_size; - cgh.parallel_for(nd_range<1>(dot_num_groups*dot_wgsize, dot_wgsize), [=](nd_item<1> item) + cgh.parallel_for(sycl::nd_range<1>(dot_num_groups*dot_wgsize, dot_wgsize), [=](sycl::nd_item<1> item) { size_t i = item.get_global_id(0); size_t li = item.get_local_id(0); @@ -173,7 +172,7 @@ T SYCLStream::dot() size_t local_size = item.get_local_range()[0]; for (int offset = local_size / 2; offset > 0; offset /= 2) { - item.barrier(cl::sycl::access::fence_space::local_space); + item.barrier(sycl::access::fence_space::local_space); if (li < offset) wg_sum[li] += wg_sum[li + offset]; } @@ -184,7 +183,7 @@ T SYCLStream::dot() }); T sum = 0.0; - auto h_sum = d_sum->template get_access(); + auto h_sum = d_sum->template get_access(); for (int i = 0; i < dot_num_groups; i++) { sum += h_sum[i]; @@ -196,12 +195,12 @@ T SYCLStream::dot() template void SYCLStream::init_arrays(T initA, T initB, T initC) { - queue->submit([&](handler &cgh) + queue->submit([&](sycl::handler &cgh) { - auto ka = d_a->template get_access(cgh); - auto kb = d_b->template get_access(cgh); - auto kc = d_c->template get_access(cgh); - cgh.parallel_for(range<1>{array_size}, [=](item<1> item) + auto ka = d_a->template get_access(cgh); + auto kb = d_b->template get_access(cgh); + auto kc = d_c->template get_access(cgh); + cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::item<1> item) { auto id = item.get_id(0); ka[id] = initA; @@ -215,9 +214,9 @@ void SYCLStream::init_arrays(T initA, T initB, T initC) template void SYCLStream::read_arrays(std::vector& a, std::vector& b, std::vector& c) { - auto _a = d_a->template get_access(); - auto _b = d_b->template get_access(); - auto _c = d_c->template get_access(); + auto _a = d_a->template get_access(); + auto _b = d_b->template get_access(); + auto _c = d_c->template get_access(); for (int i = 0; i < array_size; i++) { a[i] = _a[i]; @@ -229,7 +228,7 @@ void SYCLStream::read_arrays(std::vector& a, std::vector& b, std::vecto void getDeviceList(void) { // Ask SYCL runtime for all devices in system - devices = cl::sycl::device::get_devices(); + devices = sycl::device::get_devices(); cached = true; } @@ -263,7 +262,7 @@ std::string getDeviceName(const int device) if (device < devices.size()) { - name = devices[device].get_info(); + name = devices[device].get_info(); } else { @@ -282,7 +281,7 @@ std::string getDeviceDriver(const int device) if (device < devices.size()) { - driver = devices[device].get_info(); + driver = devices[device].get_info(); } else { diff --git a/SYCLStream.h b/SYCLStream.h index cb1a45a..7b79ad2 100644 --- a/SYCLStream.h +++ b/SYCLStream.h @@ -23,11 +23,11 @@ class SYCLStream : public Stream int array_size; // SYCL objects - cl::sycl::queue *queue; - cl::sycl::buffer *d_a; - cl::sycl::buffer *d_b; - cl::sycl::buffer *d_c; - cl::sycl::buffer *d_sum; + sycl::queue *queue; + sycl::buffer *d_a; + sycl::buffer *d_b; + sycl::buffer *d_c; + sycl::buffer *d_sum; // NDRange configuration for the dot kernel size_t dot_num_groups; From 8f5357011a0b64f4ffb435466d67743acdd7016e Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Tue, 12 Jan 2021 11:16:46 +0000 Subject: [PATCH 04/78] [SYCL 2020] Use sycl::id for init kernel --- SYCLStream.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index 984d8d2..5b9d78d 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -200,12 +200,11 @@ void SYCLStream::init_arrays(T initA, T initB, T initC) auto ka = d_a->template get_access(cgh); auto kb = d_b->template get_access(cgh); auto kc = d_c->template get_access(cgh); - cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::item<1> item) + cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { - auto id = item.get_id(0); - ka[id] = initA; - kb[id] = initB; - kc[id] = initC; + ka[idx] = initA; + kb[idx] = initB; + kc[idx] = initC; }); }); queue->wait(); From 282fb1e5e35092ae4f9c0b37c2f701730ad92f1b Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Tue, 12 Jan 2021 11:54:39 +0000 Subject: [PATCH 05/78] [SYCL 2020] Use accessor constructurs using CTAD and Tags instead of get_access --- SYCLStream.cpp | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index 5b9d78d..e0fce5d 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -88,8 +88,8 @@ void SYCLStream::copy() { queue->submit([&](sycl::handler &cgh) { - auto ka = d_a->template get_access(cgh); - auto kc = d_c->template get_access(cgh); + sycl::accessor ka {*d_a, cgh, sycl::read_only}; + sycl::accessor kc {*d_c, cgh, sycl::write_only}; cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { kc[idx] = ka[idx]; @@ -104,8 +104,8 @@ void SYCLStream::mul() const T scalar = startScalar; queue->submit([&](sycl::handler &cgh) { - auto kb = d_b->template get_access(cgh); - auto kc = d_c->template get_access(cgh); + sycl::accessor kb {*d_b, cgh, sycl::write_only}; + sycl::accessor kc {*d_c, cgh, sycl::read_only}; cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { kb[idx] = scalar * kc[idx]; @@ -119,9 +119,9 @@ void SYCLStream::add() { queue->submit([&](sycl::handler &cgh) { - auto ka = d_a->template get_access(cgh); - auto kb = d_b->template get_access(cgh); - auto kc = d_c->template get_access(cgh); + sycl::accessor ka {*d_a, cgh, sycl::read_only}; + sycl::accessor kb {*d_b, cgh, sycl::read_only}; + sycl::accessor kc {*d_c, cgh, sycl::write_only}; cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { kc[idx] = ka[idx] + kb[idx]; @@ -136,9 +136,9 @@ void SYCLStream::triad() const T scalar = startScalar; queue->submit([&](sycl::handler &cgh) { - auto ka = d_a->template get_access(cgh); - auto kb = d_b->template get_access(cgh); - auto kc = d_c->template get_access(cgh); + sycl::accessor ka {*d_a, cgh, sycl::write_only}; + sycl::accessor kb {*d_b, cgh, sycl::read_only}; + sycl::accessor kc {*d_c, cgh, sycl::read_only}; cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { ka[idx] = kb[idx] + scalar * kc[idx]; @@ -152,10 +152,11 @@ T SYCLStream::dot() { queue->submit([&](sycl::handler &cgh) { - auto ka = d_a->template get_access(cgh); - auto kb = d_b->template get_access(cgh); - auto ksum = d_sum->template get_access(cgh); + sycl::accessor ka {*d_a, cgh, sycl::read_only}; + sycl::accessor kb {*d_b, cgh, sycl::read_only}; + sycl::accessor ksum {*d_sum, cgh, sycl::write_only}; + //sycl::local_accessor wg_sum {sycl::range<1>(dot_wgsize), cgh}; auto wg_sum = sycl::accessor(sycl::range<1>(dot_wgsize), cgh); size_t N = array_size; @@ -197,9 +198,10 @@ void SYCLStream::init_arrays(T initA, T initB, T initC) { queue->submit([&](sycl::handler &cgh) { - auto ka = d_a->template get_access(cgh); - auto kb = d_b->template get_access(cgh); - auto kc = d_c->template get_access(cgh); + // TODO: could add the sycl::no_init property + sycl::accessor ka {*d_a, cgh, sycl::write_only}; + sycl::accessor kb {*d_b, cgh, sycl::write_only}; + sycl::accessor kc {*d_c, cgh, sycl::write_only}; cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { ka[idx] = initA; From b611db8cabe7bc08528df77baace8948825e28d5 Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Tue, 12 Jan 2021 11:58:14 +0000 Subject: [PATCH 06/78] [SYCL 2020] Use host accessor constructors --- SYCLStream.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index e0fce5d..049c26e 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -215,9 +215,9 @@ void SYCLStream::init_arrays(T initA, T initB, T initC) template void SYCLStream::read_arrays(std::vector& a, std::vector& b, std::vector& c) { - auto _a = d_a->template get_access(); - auto _b = d_b->template get_access(); - auto _c = d_c->template get_access(); + sycl::host_accessor _a {*d_a, sycl::read_only}; + sycl::host_accessor _b {*d_b, sycl::read_only}; + sycl::host_accessor _c {*d_c, sycl::read_only}; for (int i = 0; i < array_size; i++) { a[i] = _a[i]; From aa0ab6a8e3a1814d7838de58aa6617a12a5fc3aa Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Thu, 21 Jan 2021 10:37:11 +0000 Subject: [PATCH 07/78] use new header path --- SYCLStream.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SYCLStream.h b/SYCLStream.h index 7b79ad2..124ab28 100644 --- a/SYCLStream.h +++ b/SYCLStream.h @@ -11,7 +11,7 @@ #include "Stream.h" -#include "CL/sycl.hpp" +#include #define IMPLEMENTATION_STRING "SYCL" From 42c89547896b403d5b62046bbe0cdccdea883934 Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Thu, 21 Jan 2021 10:37:56 +0000 Subject: [PATCH 08/78] [SYCL 2020] use new reduction for dot kernel --- SYCLStream.cpp | 43 ++++++++++++------------------------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index 049c26e..109b883 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -69,7 +69,7 @@ SYCLStream::SYCLStream(const int ARRAY_SIZE, const int device_index) d_a = new sycl::buffer(array_size); d_b = new sycl::buffer(array_size); d_c = new sycl::buffer(array_size); - d_sum = new sycl::buffer(dot_num_groups); + d_sum = new sycl::buffer(1); } template @@ -150,47 +150,28 @@ void SYCLStream::triad() template T SYCLStream::dot() { + queue->submit([&](sycl::handler &cgh) { sycl::accessor ka {*d_a, cgh, sycl::read_only}; sycl::accessor kb {*d_b, cgh, sycl::read_only}; - sycl::accessor ksum {*d_sum, cgh, sycl::write_only}; - //sycl::local_accessor wg_sum {sycl::range<1>(dot_wgsize), cgh}; - auto wg_sum = sycl::accessor(sycl::range<1>(dot_wgsize), cgh); + // Reduction object, to perform summation + // Initialises the result to zero + auto sumReducer = sycl::reduction(*d_sum, cgh, std::plus(), sycl::property::reduction::initialize_to_identity); - size_t N = array_size; - cgh.parallel_for(sycl::nd_range<1>(dot_num_groups*dot_wgsize, dot_wgsize), [=](sycl::nd_item<1> item) + cgh.parallel_for(sycl::range<1>{array_size}, sumReducer, [=](sycl::id<1> idx, auto& sum) { - size_t i = item.get_global_id(0); - size_t li = item.get_local_id(0); - size_t global_size = item.get_global_range()[0]; - - wg_sum[li] = 0.0; - for (; i < N; i += global_size) - wg_sum[li] += ka[i] * kb[i]; - - size_t local_size = item.get_local_range()[0]; - for (int offset = local_size / 2; offset > 0; offset /= 2) - { - item.barrier(sycl::access::fence_space::local_space); - if (li < offset) - wg_sum[li] += wg_sum[li + offset]; - } - - if (li == 0) - ksum[item.get_group(0)] = wg_sum[0]; + sum += ka[idx] * kb[idx]; }); + }); - T sum = 0.0; - auto h_sum = d_sum->template get_access(); - for (int i = 0; i < dot_num_groups; i++) - { - sum += h_sum[i]; - } + // Get access on the host, and return a copy of the data (single number) + // This will block until the result is available, so no need to wait on the queue. + sycl::host_accessor result {*d_sum, sycl::read_only}; + return result[0]; - return sum; } template From 4726f3f0f1bf2f4ab8a17032e1431fff36e6c9de Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Thu, 21 Jan 2021 10:39:13 +0000 Subject: [PATCH 09/78] [SYCL 2020] Specify no_init property when initalising buffers --- SYCLStream.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index 109b883..1c182cb 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -179,10 +179,10 @@ void SYCLStream::init_arrays(T initA, T initB, T initC) { queue->submit([&](sycl::handler &cgh) { - // TODO: could add the sycl::no_init property - sycl::accessor ka {*d_a, cgh, sycl::write_only}; - sycl::accessor kb {*d_b, cgh, sycl::write_only}; - sycl::accessor kc {*d_c, cgh, sycl::write_only}; + sycl::accessor ka {*d_a, cgh, sycl::write_only, sycl::no_init}; + sycl::accessor kb {*d_b, cgh, sycl::write_only, sycl::no_init}; + sycl::accessor kc {*d_c, cgh, sycl::write_only, sycl::no_init}; + cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { ka[idx] = initA; @@ -190,6 +190,7 @@ void SYCLStream::init_arrays(T initA, T initB, T initC) kc[idx] = initC; }); }); + queue->wait(); } From b825df00746a13854cfdfd7265cb797fb57395b2 Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Thu, 21 Jan 2021 18:18:35 +0000 Subject: [PATCH 10/78] [SYCL 2020] Declare reduction inline to reduce one variable name --- SYCLStream.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index 1c182cb..d879c4e 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -156,14 +156,13 @@ T SYCLStream::dot() sycl::accessor ka {*d_a, cgh, sycl::read_only}; sycl::accessor kb {*d_b, cgh, sycl::read_only}; - // Reduction object, to perform summation - // Initialises the result to zero - auto sumReducer = sycl::reduction(*d_sum, cgh, std::plus(), sycl::property::reduction::initialize_to_identity); - - cgh.parallel_for(sycl::range<1>{array_size}, sumReducer, [=](sycl::id<1> idx, auto& sum) - { - sum += ka[idx] * kb[idx]; - }); + cgh.parallel_for(sycl::range<1>{array_size}, + // Reduction object, to perform summation - initialises the result to zero + sycl::reduction(*d_sum, cgh, std::plus(), sycl::property::reduction::initialize_to_identity); + [=](sycl::id<1> idx, auto& sum) + { + sum += ka[idx] * kb[idx]; + }); }); From 1517101ceb08b13f85b731c26262acc1ee742bae Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Mon, 8 Feb 2021 11:07:38 +0000 Subject: [PATCH 11/78] [SYCL 2020] Remove work-group heuristic for reduction as unused --- SYCLStream.cpp | 13 ------------- SYCLStream.h | 4 ---- 2 files changed, 17 deletions(-) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index d879c4e..8a1010e 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -27,22 +27,9 @@ SYCLStream::SYCLStream(const int ARRAY_SIZE, const int device_index) sycl::device dev = devices[device_index]; - // Determine sensible dot kernel NDRange configuration - if (dev.is_cpu()) - { - dot_num_groups = dev.get_info(); - dot_wgsize = dev.get_info() * 2; - } - else - { - dot_num_groups = dev.get_info() * 4; - dot_wgsize = dev.get_info(); - } - // Print out device information std::cout << "Using SYCL device " << getDeviceName(device_index) << std::endl; std::cout << "Driver: " << getDeviceDriver(device_index) << std::endl; - std::cout << "Reduction kernel config: " << dot_num_groups << " groups of size " << dot_wgsize << std::endl; queue = new sycl::queue(dev, sycl::async_handler{[&](sycl::exception_list l) { diff --git a/SYCLStream.h b/SYCLStream.h index 124ab28..3ebea6c 100644 --- a/SYCLStream.h +++ b/SYCLStream.h @@ -29,10 +29,6 @@ class SYCLStream : public Stream sycl::buffer *d_c; sycl::buffer *d_sum; - // NDRange configuration for the dot kernel - size_t dot_num_groups; - size_t dot_wgsize; - public: SYCLStream(const int, const int); From 1db9a6b64848e114794c31b0c5bede911f99be7c Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Mon, 8 Feb 2021 11:33:03 +0000 Subject: [PATCH 12/78] [SYCL 2020] Use smart pointers instead of raw pointers --- SYCLStream.cpp | 24 ++++++++++-------------- SYCLStream.h | 13 +++++++------ 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index 8a1010e..0f65560 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -51,24 +51,20 @@ SYCLStream::SYCLStream(const int ARRAY_SIZE, const int device_index) throw std::runtime_error("SYCL errors detected"); } }}); + + // No longer need list of devices + devices.clear(); + cached = true; // Create buffers - d_a = new sycl::buffer(array_size); - d_b = new sycl::buffer(array_size); - d_c = new sycl::buffer(array_size); - d_sum = new sycl::buffer(1); + // Only in the constructor at runtime do we know the size, so need to use (smart) pointers + d_a = std::make_unique>(array_size); + d_b = std::make_unique>(array_size); + d_c = std::make_unique>(array_size); + d_sum = std::make_unique>(1); + } -template -SYCLStream::~SYCLStream() -{ - delete d_a; - delete d_b; - delete d_c; - delete d_sum; - delete queue; - devices.clear(); -} template void SYCLStream::copy() diff --git a/SYCLStream.h b/SYCLStream.h index 3ebea6c..bfe9ae5 100644 --- a/SYCLStream.h +++ b/SYCLStream.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include "Stream.h" @@ -23,16 +24,16 @@ class SYCLStream : public Stream int array_size; // SYCL objects - sycl::queue *queue; - sycl::buffer *d_a; - sycl::buffer *d_b; - sycl::buffer *d_c; - sycl::buffer *d_sum; + std::unique_ptr queue; + std::unique_ptr> d_a; + std::unique_ptr> d_b; + std::unique_ptr> d_c; + std::unique_ptr> d_sum; public: SYCLStream(const int, const int); - ~SYCLStream(); + ~SYCLStream() = default; virtual void copy() override; virtual void add() override; From 1336400311a7633080d3973c79151686c614937a Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Mon, 8 Feb 2021 11:34:37 +0000 Subject: [PATCH 13/78] [SYCL 2020] Use unique pointer for queue constructor --- SYCLStream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index 0f65560..7765352 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -31,7 +31,7 @@ SYCLStream::SYCLStream(const int ARRAY_SIZE, const int device_index) std::cout << "Using SYCL device " << getDeviceName(device_index) << std::endl; std::cout << "Driver: " << getDeviceDriver(device_index) << std::endl; - queue = new sycl::queue(dev, sycl::async_handler{[&](sycl::exception_list l) + queue = std::make_unique(dev, sycl::async_handler{[&](sycl::exception_list l) { bool error = false; for(auto e: l) From ae8bd6081be7d8c116e8f345e7f833bd0317b22c Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Mon, 8 Feb 2021 13:43:35 +0000 Subject: [PATCH 14/78] [SYCL 2020] Use constructor initaliser list to allocate buffers - no need to use pointers --- SYCLStream.cpp | 55 ++++++++++++++++++++++++-------------------------- SYCLStream.h | 11 ++++++---- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index 7765352..d766996 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -16,12 +16,15 @@ void getDeviceList(void); template SYCLStream::SYCLStream(const int ARRAY_SIZE, const int device_index) +: array_size {ARRAY_SIZE}, + d_a {ARRAY_SIZE}, + d_b {ARRAY_SIZE}, + d_c {ARRAY_SIZE}, + d_sum {1} { if (!cached) getDeviceList(); - array_size = ARRAY_SIZE; - if (device_index >= devices.size()) throw std::runtime_error("Invalid device index"); @@ -55,13 +58,7 @@ SYCLStream::SYCLStream(const int ARRAY_SIZE, const int device_index) // No longer need list of devices devices.clear(); cached = true; - - // Create buffers - // Only in the constructor at runtime do we know the size, so need to use (smart) pointers - d_a = std::make_unique>(array_size); - d_b = std::make_unique>(array_size); - d_c = std::make_unique>(array_size); - d_sum = std::make_unique>(1); + } @@ -71,8 +68,8 @@ void SYCLStream::copy() { queue->submit([&](sycl::handler &cgh) { - sycl::accessor ka {*d_a, cgh, sycl::read_only}; - sycl::accessor kc {*d_c, cgh, sycl::write_only}; + sycl::accessor ka {d_a, cgh, sycl::read_only}; + sycl::accessor kc {d_c, cgh, sycl::write_only}; cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { kc[idx] = ka[idx]; @@ -87,8 +84,8 @@ void SYCLStream::mul() const T scalar = startScalar; queue->submit([&](sycl::handler &cgh) { - sycl::accessor kb {*d_b, cgh, sycl::write_only}; - sycl::accessor kc {*d_c, cgh, sycl::read_only}; + sycl::accessor kb {d_b, cgh, sycl::write_only}; + sycl::accessor kc {d_c, cgh, sycl::read_only}; cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { kb[idx] = scalar * kc[idx]; @@ -102,9 +99,9 @@ void SYCLStream::add() { queue->submit([&](sycl::handler &cgh) { - sycl::accessor ka {*d_a, cgh, sycl::read_only}; - sycl::accessor kb {*d_b, cgh, sycl::read_only}; - sycl::accessor kc {*d_c, cgh, sycl::write_only}; + sycl::accessor ka {d_a, cgh, sycl::read_only}; + sycl::accessor kb {d_b, cgh, sycl::read_only}; + sycl::accessor kc {d_c, cgh, sycl::write_only}; cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { kc[idx] = ka[idx] + kb[idx]; @@ -119,9 +116,9 @@ void SYCLStream::triad() const T scalar = startScalar; queue->submit([&](sycl::handler &cgh) { - sycl::accessor ka {*d_a, cgh, sycl::write_only}; - sycl::accessor kb {*d_b, cgh, sycl::read_only}; - sycl::accessor kc {*d_c, cgh, sycl::read_only}; + sycl::accessor ka {d_a, cgh, sycl::write_only}; + sycl::accessor kb {d_b, cgh, sycl::read_only}; + sycl::accessor kc {d_c, cgh, sycl::read_only}; cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { ka[idx] = kb[idx] + scalar * kc[idx]; @@ -136,12 +133,12 @@ T SYCLStream::dot() queue->submit([&](sycl::handler &cgh) { - sycl::accessor ka {*d_a, cgh, sycl::read_only}; - sycl::accessor kb {*d_b, cgh, sycl::read_only}; + sycl::accessor ka {d_a, cgh, sycl::read_only}; + sycl::accessor kb {d_b, cgh, sycl::read_only}; cgh.parallel_for(sycl::range<1>{array_size}, // Reduction object, to perform summation - initialises the result to zero - sycl::reduction(*d_sum, cgh, std::plus(), sycl::property::reduction::initialize_to_identity); + sycl::reduction(d_sum, cgh, std::plus(), sycl::property::reduction::initialize_to_identity); [=](sycl::id<1> idx, auto& sum) { sum += ka[idx] * kb[idx]; @@ -151,7 +148,7 @@ T SYCLStream::dot() // Get access on the host, and return a copy of the data (single number) // This will block until the result is available, so no need to wait on the queue. - sycl::host_accessor result {*d_sum, sycl::read_only}; + sycl::host_accessor result {d_sum, sycl::read_only}; return result[0]; } @@ -161,9 +158,9 @@ void SYCLStream::init_arrays(T initA, T initB, T initC) { queue->submit([&](sycl::handler &cgh) { - sycl::accessor ka {*d_a, cgh, sycl::write_only, sycl::no_init}; - sycl::accessor kb {*d_b, cgh, sycl::write_only, sycl::no_init}; - sycl::accessor kc {*d_c, cgh, sycl::write_only, sycl::no_init}; + sycl::accessor ka {d_a, cgh, sycl::write_only, sycl::no_init}; + sycl::accessor kb {d_b, cgh, sycl::write_only, sycl::no_init}; + sycl::accessor kc {d_c, cgh, sycl::write_only, sycl::no_init}; cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { @@ -179,9 +176,9 @@ void SYCLStream::init_arrays(T initA, T initB, T initC) template void SYCLStream::read_arrays(std::vector& a, std::vector& b, std::vector& c) { - sycl::host_accessor _a {*d_a, sycl::read_only}; - sycl::host_accessor _b {*d_b, sycl::read_only}; - sycl::host_accessor _c {*d_c, sycl::read_only}; + sycl::host_accessor _a {d_a, sycl::read_only}; + sycl::host_accessor _b {d_b, sycl::read_only}; + sycl::host_accessor _c {d_c, sycl::read_only}; for (int i = 0; i < array_size; i++) { a[i] = _a[i]; diff --git a/SYCLStream.h b/SYCLStream.h index bfe9ae5..0c73594 100644 --- a/SYCLStream.h +++ b/SYCLStream.h @@ -24,11 +24,14 @@ class SYCLStream : public Stream int array_size; // SYCL objects + // Queue is a pointer because we allow device selection std::unique_ptr queue; - std::unique_ptr> d_a; - std::unique_ptr> d_b; - std::unique_ptr> d_c; - std::unique_ptr> d_sum; + + // Buffers + sycl::buffer d_a; + sycl::buffer d_b; + sycl::buffer d_c; + sycl::buffer d_sum; public: From 707bc5d0bfcc4ee94b5600b1b4c36db8d0268d68 Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Tue, 9 Feb 2021 10:31:18 +0000 Subject: [PATCH 15/78] Update SYCL version to SYCL 2020 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1cfe5e..7d45f82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ All notable changes to this project will be documented in this file. - Cray compiler OpenMP flags updated. - Clang compiler OpenMP flags corrected for NVIDIA target. - Reorder OpenCL objects in class so destructors are called in safe order. +- Update SYCL version to SYCL 2020. ### Removed - Pre-building of kernels in SYCL version to ensure compatibility with SYCL 1.2.1. From c9247cd27f489683151b351dfb9b5669ccd43fbf Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Wed, 17 Feb 2021 16:35:41 +0000 Subject: [PATCH 16/78] [SYCL-2020] fix semicolon typo --- SYCLStream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index d766996..028a3df 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -138,7 +138,7 @@ T SYCLStream::dot() cgh.parallel_for(sycl::range<1>{array_size}, // Reduction object, to perform summation - initialises the result to zero - sycl::reduction(d_sum, cgh, std::plus(), sycl::property::reduction::initialize_to_identity); + sycl::reduction(d_sum, cgh, std::plus(), sycl::property::reduction::initialize_to_identity), [=](sycl::id<1> idx, auto& sum) { sum += ka[idx] * kb[idx]; From 98d2140a88f1862b5ba3d93cf6a96a8b915b00b6 Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Wed, 17 Feb 2021 16:48:42 +0000 Subject: [PATCH 17/78] [SYCL 2020] Add note about glibc --- SYCL.make | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SYCL.make b/SYCL.make index 4326da5..b251f2e 100644 --- a/SYCL.make +++ b/SYCL.make @@ -7,6 +7,9 @@ Available compilers are: For HIPSYCL and COMPUTECPP, SYCL_SDK_DIR must be specified, the directory should contain [/lib, /bin, ...] For DPCPP, the compiler must be on path + + You may need to use the following if running old glibc: + EXTRA_FLAGS="--gcc-toolchain=$(realpath "$(dirname "$(which gcc)")"/..)" endef $(info $(compiler_help)) COMPILER=HIPSYCL From 67da8f6a8eeb98f62bb7fe5641913b82d909d3d8 Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Mon, 22 Feb 2021 15:19:56 +0000 Subject: [PATCH 18/78] [SYCL 2020] Add nstream kernel --- SYCLStream.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index 3eab481..f3dd55f 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -131,12 +131,13 @@ template void SYCLStream::nstream() { const T scalar = startScalar; - queue->submit([&](handler &cgh) + + queue->submit([&](sycl::handler &cgh) { - auto ka = d_a->template get_access(cgh); - auto kb = d_b->template get_access(cgh); - auto kc = d_c->template get_access(cgh); - cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) + sycl::accessor ka {d_a, cgh}; + sycl::accessor kb {d_b, cgh, sycl::read_only}; + sycl::accessor kc {d_c, cgh, sycl::read_only}; + cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) { ka[idx] += kb[idx] + scalar * kc[idx]; }); From 9b23f6e54d375ce99461e907cb19456a992fa562 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 25 Mar 2021 15:40:16 +0000 Subject: [PATCH 19/78] Initial Rust implementation --- README.md | 8 + rust-stream/.gitignore | 2 + rust-stream/Cargo.lock | 357 ++++++++++++++++++++++++++++++++++ rust-stream/Cargo.toml | 13 ++ rust-stream/src/main.rs | 413 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 793 insertions(+) create mode 100644 rust-stream/.gitignore create mode 100644 rust-stream/Cargo.lock create mode 100644 rust-stream/Cargo.toml create mode 100644 rust-stream/src/main.rs diff --git a/README.md b/README.md index 25ba9ae..cd8ab86 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,14 @@ Pass in extra flags via the `EXTRA_FLAGS` option. The binaries are named in the form `-stream`. +This project also contains implementations in alternative languages with different build systems: +* Rust - Install the [Rust toolchain](https://www.rust-lang.org/tools/install) and proceed to compile a binary via: + ```shell + > cd rust-stream/ + > cargo build --release + > ./target/release/rust-stream + ``` + Building Kokkos --------------- diff --git a/rust-stream/.gitignore b/rust-stream/.gitignore new file mode 100644 index 0000000..3a8cabc --- /dev/null +++ b/rust-stream/.gitignore @@ -0,0 +1,2 @@ +/target +.idea diff --git a/rust-stream/Cargo.lock b/rust-stream/Cargo.lock new file mode 100644 index 0000000..66addaa --- /dev/null +++ b/rust-stream/Cargo.lock @@ -0,0 +1,357 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "heck" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" + +[[package]] +name = "memoffset" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "rust-stream" +version = "3.4.0" +dependencies = [ + "num-traits", + "rayon", + "structopt", + "tabular", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tabular" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e35bee02dcefe64a74065b6b869d241eab1a02fea0d65e6074ce4e51894c3b" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "unicode-segmentation" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/rust-stream/Cargo.toml b/rust-stream/Cargo.toml new file mode 100644 index 0000000..35a2880 --- /dev/null +++ b/rust-stream/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rust-stream" +version = "3.4.0" +authors = ["Wei-Chen Lin "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +num-traits = "0.2.14" +structopt = "0.3.13" +tabular = "0.1.4" +rayon = "1.5" diff --git a/rust-stream/src/main.rs b/rust-stream/src/main.rs new file mode 100644 index 0000000..3e31fd9 --- /dev/null +++ b/rust-stream/src/main.rs @@ -0,0 +1,413 @@ +use std::fmt::{Debug, Display}; +use std::iter::Sum; +use std::mem::size_of; +use std::time::{Duration, Instant}; + +use num_traits::{abs, NumAssign, Signed}; +use num_traits::real::Real; +use rayon::prelude::*; +use structopt::StructOpt; +use tabular::{Row, Table}; + +#[derive(Debug, StructOpt)] +struct Options { + /// List available devices + #[structopt(long)] list: bool, + /// Select device at + #[structopt(long, default_value = "0")] device: usize, + /// Run the test times (NUM >= 2) + #[structopt(long, default_value = "100")] numtimes: usize, + /// Use elements in the array + #[structopt(long, default_value = "33554432")] arraysize: usize, + /// Use floats (rather than doubles) + #[structopt(long)] float: bool, + /// Only run triad + #[structopt(long)] triad_only: bool, + /// Only run nstream + #[structopt(long)] nstream_only: bool, + /// Output as csv table + #[structopt(long)] csv: bool, + /// Use MiB=2^20 for bandwidth calculation (default MB=10^6) + #[structopt(long)] mibibytes: bool, +} + +#[derive(PartialEq)] +enum Benchmark { All, Triad, NStream } + +struct StreamData { + size: usize, + scalar: T, + a: Vec, + b: Vec, + c: Vec, +} + +impl StreamData { + pub fn new(size: usize, scalar: T) -> StreamData { + StreamData { + size, + scalar, + a: vec![T::default(); size], + b: vec![T::default(); size], + c: vec![T::default(); size], + } + } +} + +struct PlainFor; + +struct RayonPar; + +#[inline(always)] +fn timed(f: F) -> Duration { + let start = Instant::now(); + f(); + start.elapsed() +} + +#[inline(always)] +fn timed_mut T>(f: &mut F) -> (Duration, T) { + let start = Instant::now(); + let x = f(); + (start.elapsed(), x) +} + +struct AllTiming { copy: T, mul: T, add: T, triad: T, dot: T } + +trait RustStream { + fn init_arrays(&mut self, init: (T, T, T)); + fn copy(&mut self); + fn mul(&mut self); + fn add(&mut self); + fn triad(&mut self); + fn nstream(&mut self); + fn dot(&mut self) -> T; + + fn run_all(&mut self, n: usize) -> (AllTiming>, T) { + let mut timings: AllTiming> = AllTiming { + copy: vec![Duration::default(); n], + mul: vec![Duration::default(); n], + add: vec![Duration::default(); n], + triad: vec![Duration::default(); n], + dot: vec![Duration::default(); n], + }; + let mut last_sum = T::default(); + for i in 0..n { + timings.copy[i] = timed(|| self.copy()); + timings.mul[i] = timed(|| self.mul()); + timings.add[i] = timed(|| self.add()); + timings.triad[i] = timed(|| self.triad()); + let (dot, sum) = timed_mut(&mut || self.dot()); + timings.dot[i] = dot; + last_sum = sum; + } + (timings, last_sum) + } + + fn run_triad(&mut self, n: usize) -> Duration { + timed(|| for _ in 0..n { self.triad(); }) + } + + fn run_nstream(&mut self, n: usize) -> Vec { + (0..n).map(|_| timed(|| self.nstream())).collect::>() + } +} + +trait ArrayType: Real + NumAssign + Signed + Default {} + +impl ArrayType for T {} + +// single threaded version +impl RustStream for StreamData { + fn init_arrays(&mut self, init: (T, T, T)) { + self.a.fill(init.0); + self.b.fill(init.1); + self.c.fill(init.2); + } + + fn copy(&mut self) { + for i in 0..self.size { + self.c[i] = self.a[i]; + } + } + + fn mul(&mut self) { + for i in 0..self.size { + self.b[i] = self.scalar * self.c[i]; + } + } + + fn add(&mut self) { + for i in 0..self.size { + self.c[i] = self.a[i] + self.b[i]; + } + } + + fn triad(&mut self) { + for i in 0..self.size { + self.a[i] = self.b[i] + self.scalar * self.c[i]; + } + } + + fn nstream(&mut self) { + for i in 0..self.size { + self.a[i] += self.b[i] * self.scalar * self.c[i]; + } + } + + fn dot(&mut self) -> T { + let mut sum: T = T::default(); + for i in 0..self.size { + sum += self.a[i] * self.b[i]; + } + sum + } +} + +// Rayon version, it should be semantically equal to the single threaded version +impl RustStream for StreamData { + fn init_arrays(&mut self, init: (T, T, T)) { + self.a.fill(init.0); + self.b.fill(init.1); + self.c.fill(init.2); + } + + fn copy(&mut self) { + let a = &self.a; + self.c.par_iter_mut().enumerate().for_each(|(i, c)| *c = a[i]) + } + + fn mul(&mut self) { + let c = &self.c; + let scalar = &self.scalar; + self.b.par_iter_mut().enumerate().for_each(|(i, b)| *b = *scalar * c[i]) + } + + fn add(&mut self) { + let a = &self.a; + let b = &self.b; + self.c.par_iter_mut().enumerate().for_each(|(i, c)| *c = a[i] + b[i]) + } + + fn triad(&mut self) { + let scalar = &self.scalar; + let b = &self.b; + let c = &self.c; + self.a.par_iter_mut().enumerate().for_each(|(i, a)| *a = b[i] + *scalar * c[i]) + } + + fn nstream(&mut self) { + let scalar = &self.scalar; + let b = &self.b; + let c = &self.c; + self.a.par_iter_mut().enumerate().for_each(|(i, a)| *a += b[i] + *scalar * c[i]) + } + + fn dot(&mut self) -> T { + let a = &self.a; + let b = &self.b; + (0..self.size).into_par_iter().fold(|| T::default(), |acc, i| acc + a[i] * b[i]).sum::() + } +} + +fn validate>( + benchmark: Benchmark, + numtimes: usize, + vec: &StreamData, + dot_sum: Option, + scalar: T, init: (T, T, T)) { + let (mut gold_a, mut gold_b, mut gold_c) = init; + for _ in 0..numtimes { + match benchmark { + Benchmark::All => { + gold_c = gold_a; + gold_b = scalar * gold_c; + gold_c = gold_a + gold_b; + gold_a = gold_b + scalar * gold_c; + } + Benchmark::Triad => { + gold_a = gold_b + scalar * gold_c; + } + Benchmark::NStream => { + gold_a += gold_b + scalar * gold_c; + } + }; + } + let tolerance = T::epsilon().into() * 100.0f64; + let validate_xs = |name: &str, xs: &Vec, from: T| { + let error = (xs.iter().map(|x| abs(*x - from)).sum::()).into() / xs.len() as f64; + if error > tolerance { + eprintln!("Validation failed on {}[]. Average error {} ", name, error) + } + }; + validate_xs("a", &vec.a, gold_a); + validate_xs("b", &vec.b, gold_b); + validate_xs("c", &vec.c, gold_c); + + if let Some(sum) = dot_sum { + let gold_sum = (gold_a * gold_b).into() * vec.size as f64; + let error = abs((sum.into() - gold_sum) / gold_sum); + if error > 1.0e-8 { + eprintln!("Validation failed on sum. Error {} \nSum was {} but should be {}", error, sum, gold_sum); + } + } +} + +fn run_cpu + Display>(option: Options, scalar: T, init: (T, T, T)) { + let benchmark = match (option.nstream_only, option.triad_only) { + (true, false) => Benchmark::NStream, + (false, true) => Benchmark::Triad, + (false, false) => Benchmark::All, + (true, true) => panic!("Both triad and nstream are enabled, pick one or omit both to run all benchmarks"), + }; + + let array_bytes = option.arraysize * size_of::(); + let total_bytes = array_bytes * 3; + let (mega_scale, mega_suffix, giga_scale, giga_suffix) = + if !option.mibibytes { (1.0e-6, "MB", 1.0e-9, "GB") } else { (2f64.powi(-20), "MiB", 2f64.powi(-30), "GiB") }; + + if !option.csv { + println!("Running {} {} times", match benchmark { + Benchmark::All => "kernels", + Benchmark::Triad => "triad", + Benchmark::NStream => "nstream", + }, option.numtimes); + + if benchmark == Benchmark::Triad { + println!("Number of elements: {}", option.arraysize); + } + + println!("Precision: {}", if option.float { "float" } else { "double" }); + println!("Array size: {:.1} {}(={:.1} {})", + mega_scale * array_bytes as f64, mega_suffix, giga_scale * array_bytes as f64, giga_suffix); + println!("Total size: {:.1} {}(={:.1} {})", + mega_scale * total_bytes as f64, mega_suffix, giga_scale * total_bytes as f64, giga_suffix); + } + + + let mut vec: StreamData = StreamData::::new(option.arraysize, scalar); + let stream = &mut vec as &mut dyn RustStream; + stream.init_arrays(init); + + let tabulate = |xs: &Vec, name: &str, t_size: usize| -> Vec<(&str, String)> { + let tail = &xs[1..]; // tail only + // do stats + let max = tail.iter().max().map(|d| d.as_secs_f64()); + let min = tail.iter().min().map(|d| d.as_secs_f64()); + match (min, max) { + (Some(min), Some(max)) => { + let avg: f64 = tail.iter().map(|d| d.as_secs_f64()).sum::() / tail.len() as f64; + let mbps = mega_scale * (t_size as f64) / min; + if option.csv { + vec![ + ("function", name.to_string()), + ("num_times", option.numtimes.to_string()), + ("n_elements", option.arraysize.to_string()), + ("sizeof", t_size.to_string()), + (if option.mibibytes { "max_mibytes_per_sec" } else { "max_mbytes_per_sec" }, mbps.to_string()), + ("min_runtime", min.to_string()), + ("max_runtime", max.to_string()), + ("avg_runtime", avg.to_string()), + ] + } else { + vec![ + ("Function", name.to_string()), + (if option.mibibytes { "MiBytes/sec" } else { "MBytes/sec" }, format!("{:.3}", mbps)), + ("Min (sec)", format!("{:.5}", min)), + ("Max", format!("{:.5}", max)), + ("Average", format!("{:.5}", avg)), + ] + } + } + (_, _) => panic!("No min/max element for {}(size={})", name, t_size) + } + }; + + let tabulate_all = |xs: Vec>| { + match xs.as_slice() { + [head, .. ] => { + if option.csv { + println!("{}", head.iter().map(|(col, _)| *col).collect::>().join(",")); + for kvs in xs { + println!("{}", kvs.iter().map(|(_, val)| val.clone()).collect::>().join(",")); + } + } else { + let mut table = Table::new(&vec!["{:<}"; head.len()].join(" ")); + table.add_row(head.iter().fold(Row::new(), |row, (col, _)| row.with_cell(col))); + for kvs in xs { + table.add_row(kvs.iter().fold(Row::new(), |row, (_, val)| row.with_cell(val))); + } + println!("{}", table); + } + } + _ => panic!("Empty tabulation") + }; + }; + + match benchmark { + Benchmark::All => { + let (results, sum) = stream.run_all(option.numtimes); + validate(benchmark, option.numtimes, &vec, Some(sum), scalar, init); + tabulate_all(vec![ + tabulate(&results.copy, "Copy", 2 * array_bytes), + tabulate(&results.mul, "Mul", 2 * array_bytes), + tabulate(&results.add, "Add", 3 * array_bytes), + tabulate(&results.triad, "Triad", 3 * array_bytes), + tabulate(&results.dot, "Dot", 2 * array_bytes), + ]) + } + Benchmark::NStream => { + let results = stream.run_nstream(option.numtimes); + validate(benchmark, option.numtimes, &vec, None, scalar, init); + tabulate_all(vec![ + tabulate(&results, "Nstream", 4 * array_bytes) + ]); + } + Benchmark::Triad => { + let results = stream.run_triad(option.numtimes); + let total_bytes = 3 * array_bytes * option.numtimes; + let bandwidth = mega_scale * (total_bytes as f64 / results.as_secs_f64()); + + println!("Runtime (seconds): {:.5}", results.as_secs_f64()); + println!("Bandwidth ({}/s): {:.3} ", giga_suffix, bandwidth); + } + }; +} + +const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); + +static START_A: f32 = 0.1; +static START_B: f32 = 0.2; +static START_C: f32 = 0.0; +static START_SCALAR: f32 = 0.4; + +fn main() { + let options: Options = Options::from_args(); + + // only CPU via Rayon for now + let devices = vec![("CPU (Rayon)", |opt: Options| { + if opt.float { + run_cpu::(opt, START_SCALAR, (START_A, START_B, START_C)); + } else { + run_cpu::(opt, START_SCALAR.into(), (START_A.into(), START_B.into(), START_C.into())); + } + })]; + + if options.list { + devices.iter().enumerate().for_each(|(i, (name, _))| { + println!("{}: {}", i, name); + }) + } else { + match devices.get(options.device) { + Some((_, run)) => { + if !&options.csv { + println!("BabelStream\n\ + Version: {}\n\ + Implementation: Rust+Rayon", VERSION.unwrap_or("unknown")) + } + run(options); + } + None => eprintln!("Device index({}) not available", options.device) + } + } +} From 9f38177e1b8b5fdf49cc2cdd6eb0ef45e51d057c Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Mon, 17 May 2021 15:32:42 +0100 Subject: [PATCH 20/78] [SYCL 2020] Add check for FP64 support using device aspects. This will resolve #98 in the future SYCL 2020 version. --- SYCLStream.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index b4fa514..9e23175 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -34,6 +34,15 @@ SYCLStream::SYCLStream(const int ARRAY_SIZE, const int device_index) std::cout << "Using SYCL device " << getDeviceName(device_index) << std::endl; std::cout << "Driver: " << getDeviceDriver(device_index) << std::endl; + // Check device can support FP64 if needed + if (sizeof(T) == sizeof(double)) + { + if (!dev.has(sycl::aspect::fp64)) + { + throw std::runtime_error("Device does not support double precision, please use --float"); + } + } + queue = std::make_unique(dev, sycl::async_handler{[&](sycl::exception_list l) { bool error = false; From 717cc40c2c05f38b50c541742593bd7b66f42215 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Sun, 23 May 2021 07:21:21 +0100 Subject: [PATCH 21/78] Move all C++ impl. to ./cpp --- .github/workflows/main.yaml | 54 +++++++++---------- .gitignore | 24 +-------- README.md | 4 +- cpp/.gitignore | 30 +++++++++++ ACC.cmake => cpp/ACC.cmake | 0 ACCStream.cpp => cpp/ACCStream.cpp | 0 ACCStream.h => cpp/ACCStream.h | 0 {CL => cpp/CL}/cl.h | 0 {CL => cpp/CL}/cl2.hpp | 0 {CL => cpp/CL}/cl_d3d10.h | 0 {CL => cpp/CL}/cl_d3d11.h | 0 {CL => cpp/CL}/cl_dx9_media_sharing.h | 0 {CL => cpp/CL}/cl_dx9_media_sharing_intel.h | 0 {CL => cpp/CL}/cl_egl.h | 0 {CL => cpp/CL}/cl_ext.h | 0 {CL => cpp/CL}/cl_ext_intel.h | 0 {CL => cpp/CL}/cl_gl.h | 0 {CL => cpp/CL}/cl_gl_ext.h | 0 {CL => cpp/CL}/cl_half.h | 0 {CL => cpp/CL}/cl_icd.h | 0 {CL => cpp/CL}/cl_platform.h | 0 .../CL}/cl_va_api_media_sharing_intel.h | 0 {CL => cpp/CL}/cl_version.h | 0 {CL => cpp/CL}/opencl.h | 0 CMakeLists.txt => cpp/CMakeLists.txt | 0 CUDA.cmake => cpp/CUDA.cmake | 0 CUDA.make => cpp/CUDA.make | 0 CUDAStream.cu => cpp/CUDAStream.cu | 0 CUDAStream.h => cpp/CUDAStream.h | 0 HIP.cmake => cpp/HIP.cmake | 0 HIP.make => cpp/HIP.make | 0 HIPStream.cpp => cpp/HIPStream.cpp | 0 HIPStream.h => cpp/HIPStream.h | 0 KOKKOS.cmake => cpp/KOKKOS.cmake | 0 Kokkos.make => cpp/Kokkos.make | 0 KokkosStream.cpp => cpp/KokkosStream.cpp | 0 KokkosStream.hpp => cpp/KokkosStream.hpp | 0 LICENSE => cpp/LICENSE | 0 OCL.cmake => cpp/OCL.cmake | 0 OCLStream.cpp => cpp/OCLStream.cpp | 0 OCLStream.h => cpp/OCLStream.h | 0 OMP.cmake => cpp/OMP.cmake | 0 OMPStream.cpp => cpp/OMPStream.cpp | 0 OMPStream.h => cpp/OMPStream.h | 0 OpenACC.make => cpp/OpenACC.make | 0 OpenCL.make => cpp/OpenCL.make | 0 OpenMP.make => cpp/OpenMP.make | 0 RAJA.cmake => cpp/RAJA.cmake | 0 RAJA.make => cpp/RAJA.make | 0 RAJAStream.cpp => cpp/RAJAStream.cpp | 0 RAJAStream.hpp => cpp/RAJAStream.hpp | 0 STD.cmake => cpp/STD.cmake | 0 STD.make => cpp/STD.make | 0 STD20.cmake => cpp/STD20.cmake | 0 STD20.make => cpp/STD20.make | 0 STD20Stream.cpp => cpp/STD20Stream.cpp | 0 STD20Stream.hpp => cpp/STD20Stream.hpp | 0 STDStream.cpp => cpp/STDStream.cpp | 0 STDStream.h => cpp/STDStream.h | 0 SYCL.cmake => cpp/SYCL.cmake | 0 SYCL.make => cpp/SYCL.make | 0 SYCLStream.cpp => cpp/SYCLStream.cpp | 0 SYCLStream.h => cpp/SYCLStream.h | 0 Stream.h => cpp/Stream.h | 0 .../ci-prepare-bionic.sh | 0 ci-test-compile.sh => cpp/ci-test-compile.sh | 0 .../Modules/ComputeCppCompilerChecks.cmake | 0 .../cmake}/Modules/ComputeCppIRMap.cmake | 0 .../cmake}/Modules/FindComputeCpp.cmake | 0 .../cmake}/toolchains/arm-gcc-poky.cmake | 0 .../cmake}/toolchains/gcc-generic.cmake | 0 {legacy => cpp/legacy}/HC.make | 0 {legacy => cpp/legacy}/HCStream.cpp | 0 {legacy => cpp/legacy}/HCStream.h | 0 main.cpp => cpp/main.cpp | 0 .../register_models.cmake | 0 76 files changed, 60 insertions(+), 52 deletions(-) create mode 100644 cpp/.gitignore rename ACC.cmake => cpp/ACC.cmake (100%) rename ACCStream.cpp => cpp/ACCStream.cpp (100%) rename ACCStream.h => cpp/ACCStream.h (100%) rename {CL => cpp/CL}/cl.h (100%) rename {CL => cpp/CL}/cl2.hpp (100%) rename {CL => cpp/CL}/cl_d3d10.h (100%) rename {CL => cpp/CL}/cl_d3d11.h (100%) rename {CL => cpp/CL}/cl_dx9_media_sharing.h (100%) rename {CL => cpp/CL}/cl_dx9_media_sharing_intel.h (100%) rename {CL => cpp/CL}/cl_egl.h (100%) rename {CL => cpp/CL}/cl_ext.h (100%) rename {CL => cpp/CL}/cl_ext_intel.h (100%) rename {CL => cpp/CL}/cl_gl.h (100%) rename {CL => cpp/CL}/cl_gl_ext.h (100%) rename {CL => cpp/CL}/cl_half.h (100%) rename {CL => cpp/CL}/cl_icd.h (100%) rename {CL => cpp/CL}/cl_platform.h (100%) rename {CL => cpp/CL}/cl_va_api_media_sharing_intel.h (100%) rename {CL => cpp/CL}/cl_version.h (100%) rename {CL => cpp/CL}/opencl.h (100%) rename CMakeLists.txt => cpp/CMakeLists.txt (100%) rename CUDA.cmake => cpp/CUDA.cmake (100%) rename CUDA.make => cpp/CUDA.make (100%) rename CUDAStream.cu => cpp/CUDAStream.cu (100%) rename CUDAStream.h => cpp/CUDAStream.h (100%) rename HIP.cmake => cpp/HIP.cmake (100%) rename HIP.make => cpp/HIP.make (100%) rename HIPStream.cpp => cpp/HIPStream.cpp (100%) rename HIPStream.h => cpp/HIPStream.h (100%) rename KOKKOS.cmake => cpp/KOKKOS.cmake (100%) rename Kokkos.make => cpp/Kokkos.make (100%) rename KokkosStream.cpp => cpp/KokkosStream.cpp (100%) rename KokkosStream.hpp => cpp/KokkosStream.hpp (100%) rename LICENSE => cpp/LICENSE (100%) rename OCL.cmake => cpp/OCL.cmake (100%) rename OCLStream.cpp => cpp/OCLStream.cpp (100%) rename OCLStream.h => cpp/OCLStream.h (100%) rename OMP.cmake => cpp/OMP.cmake (100%) rename OMPStream.cpp => cpp/OMPStream.cpp (100%) rename OMPStream.h => cpp/OMPStream.h (100%) rename OpenACC.make => cpp/OpenACC.make (100%) rename OpenCL.make => cpp/OpenCL.make (100%) rename OpenMP.make => cpp/OpenMP.make (100%) rename RAJA.cmake => cpp/RAJA.cmake (100%) rename RAJA.make => cpp/RAJA.make (100%) rename RAJAStream.cpp => cpp/RAJAStream.cpp (100%) rename RAJAStream.hpp => cpp/RAJAStream.hpp (100%) rename STD.cmake => cpp/STD.cmake (100%) rename STD.make => cpp/STD.make (100%) rename STD20.cmake => cpp/STD20.cmake (100%) rename STD20.make => cpp/STD20.make (100%) rename STD20Stream.cpp => cpp/STD20Stream.cpp (100%) rename STD20Stream.hpp => cpp/STD20Stream.hpp (100%) rename STDStream.cpp => cpp/STDStream.cpp (100%) rename STDStream.h => cpp/STDStream.h (100%) rename SYCL.cmake => cpp/SYCL.cmake (100%) rename SYCL.make => cpp/SYCL.make (100%) rename SYCLStream.cpp => cpp/SYCLStream.cpp (100%) rename SYCLStream.h => cpp/SYCLStream.h (100%) rename Stream.h => cpp/Stream.h (100%) rename ci-prepare-bionic.sh => cpp/ci-prepare-bionic.sh (100%) rename ci-test-compile.sh => cpp/ci-test-compile.sh (100%) rename {cmake => cpp/cmake}/Modules/ComputeCppCompilerChecks.cmake (100%) rename {cmake => cpp/cmake}/Modules/ComputeCppIRMap.cmake (100%) rename {cmake => cpp/cmake}/Modules/FindComputeCpp.cmake (100%) rename {cmake => cpp/cmake}/toolchains/arm-gcc-poky.cmake (100%) rename {cmake => cpp/cmake}/toolchains/gcc-generic.cmake (100%) rename {legacy => cpp/legacy}/HC.make (100%) rename {legacy => cpp/legacy}/HCStream.cpp (100%) rename {legacy => cpp/legacy}/HCStream.h (100%) rename main.cpp => cpp/main.cpp (100%) rename register_models.cmake => cpp/register_models.cmake (100%) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 20e1034..33f6573 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -13,86 +13,86 @@ jobs: uses: actions/cache@v2 with: path: compilers - key: ${{ runner.os }}-${{ hashFiles('ci-prepare-bionic.sh') }} + key: ${{ runner.os }}-${{ hashFiles('./cpp/ci-prepare-bionic.sh') }} - name: Prepare compilers if: steps.prepare-compilers.outputs.cache-hit != 'true' - run: source ./ci-prepare-bionic.sh ./compilers SETUP true || true + run: source ./cpp/ci-prepare-bionic.sh ./compilers SETUP true || true - name: Setup test environment - run: source ./ci-prepare-bionic.sh ./compilers VARS false || true + run: source ./cpp/ci-prepare-bionic.sh ./compilers VARS false || true - name: Test compile gcc @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_13_BIN }} + run: ./cpp/ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_13_BIN }} - name: Test compile clang @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_13_BIN }} + run: ./cpp/ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_13_BIN }} - name: Test compile nvhpc @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_13_BIN }} + run: ./cpp/ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_13_BIN }} - name: Test compile aocc @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_13_BIN }} + run: ./cpp/ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_13_BIN }} - name: Test compile aomp @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_13_BIN }} + run: ./cpp/ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_13_BIN }} - name: Test compile hip @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_13_BIN }} + run: ./cpp/ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_13_BIN }} - name: Test compile dpcpp @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_13_BIN }} + run: ./cpp/ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_13_BIN }} - name: Test compile hipsycl @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_13_BIN }} + run: ./cpp/ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_13_BIN }} - name: Test compile gcc @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_15_BIN }} + run: ./cpp/ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_15_BIN }} - name: Test compile clang @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_15_BIN }} + run: ./cpp/ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_15_BIN }} - name: Test compile nvhpc @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_15_BIN }} + run: ./cpp/ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_15_BIN }} - name: Test compile aocc @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_15_BIN }} + run: ./cpp/ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_15_BIN }} - name: Test compile aomp @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_15_BIN }} + run: ./cpp/ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_15_BIN }} - name: Test compile hip @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_15_BIN }} + run: ./cpp/ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_15_BIN }} - name: Test compile dpcpp @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_15_BIN }} + run: ./cpp/ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_15_BIN }} - name: Test compile hipsycl @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_15_BIN }} + run: ./cpp/ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_15_BIN }} - name: Test compile gcc @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_18_BIN }} + run: ./cpp/ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_18_BIN }} - name: Test compile clang @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_18_BIN }} + run: ./cpp/ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_18_BIN }} - name: Test compile nvhpc @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_18_BIN }} + run: ./cpp/ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_18_BIN }} - name: Test compile aocc @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_18_BIN }} + run: ./cpp/ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_18_BIN }} - name: Test compile aomp @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_18_BIN }} + run: ./cpp/ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_18_BIN }} - name: Test compile hip @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_18_BIN }} + run: ./cpp/ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_18_BIN }} - name: Test compile dpcpp @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_18_BIN }} + run: ./cpp/ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_18_BIN }} - name: Test compile hipsycl @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_18_BIN }} \ No newline at end of file + run: ./cpp/ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_18_BIN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index c3ea1da..614eb0f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,30 +1,8 @@ -cuda-stream -ocl-stream -omp-stream -acc-stream -raja-stream -kokkos-stream -std-stream -sycl-stream -hip-stream - -*.o -*.bc -*.sycl -*.tar -*.gz -*.a - -KokkosCore_config.* - -.DS_Store - -Makefile build/ cmake-build-*/ CMakeFiles/ .idea/ .vscode/ -.directory \ No newline at end of file +.directory diff --git a/README.md b/README.md index 8ca7398..66ea661 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ The project supports building with CMake >= 3.13.0, it can be installed without As with any CMake project, first configure the project: ```shell -> cd babelstream +> cd babelstream/cpp > cmake -Bbuild -H. -DMODEL= # configure the build, build type defaults to Release > cmake --build build # compile it > ./build/babelstream # executable available at ./build/ @@ -75,7 +75,7 @@ There are assigned those to `RELEASE_FLAGS`, and you can override them if requir To find out what flag each model supports or requires, simply configure while only specifying the model. For example: ```shell -> cd babelstream +> cd babelstream/cpp > cmake -Bbuild -H. -DMODEL=OCL ... - Common Release flags are `-O3`, set RELEASE_FLAGS to override diff --git a/cpp/.gitignore b/cpp/.gitignore new file mode 100644 index 0000000..c3ea1da --- /dev/null +++ b/cpp/.gitignore @@ -0,0 +1,30 @@ + +cuda-stream +ocl-stream +omp-stream +acc-stream +raja-stream +kokkos-stream +std-stream +sycl-stream +hip-stream + +*.o +*.bc +*.sycl +*.tar +*.gz +*.a + +KokkosCore_config.* + +.DS_Store + +Makefile + +build/ +cmake-build-*/ +CMakeFiles/ +.idea/ +.vscode/ +.directory \ No newline at end of file diff --git a/ACC.cmake b/cpp/ACC.cmake similarity index 100% rename from ACC.cmake rename to cpp/ACC.cmake diff --git a/ACCStream.cpp b/cpp/ACCStream.cpp similarity index 100% rename from ACCStream.cpp rename to cpp/ACCStream.cpp diff --git a/ACCStream.h b/cpp/ACCStream.h similarity index 100% rename from ACCStream.h rename to cpp/ACCStream.h diff --git a/CL/cl.h b/cpp/CL/cl.h similarity index 100% rename from CL/cl.h rename to cpp/CL/cl.h diff --git a/CL/cl2.hpp b/cpp/CL/cl2.hpp similarity index 100% rename from CL/cl2.hpp rename to cpp/CL/cl2.hpp diff --git a/CL/cl_d3d10.h b/cpp/CL/cl_d3d10.h similarity index 100% rename from CL/cl_d3d10.h rename to cpp/CL/cl_d3d10.h diff --git a/CL/cl_d3d11.h b/cpp/CL/cl_d3d11.h similarity index 100% rename from CL/cl_d3d11.h rename to cpp/CL/cl_d3d11.h diff --git a/CL/cl_dx9_media_sharing.h b/cpp/CL/cl_dx9_media_sharing.h similarity index 100% rename from CL/cl_dx9_media_sharing.h rename to cpp/CL/cl_dx9_media_sharing.h diff --git a/CL/cl_dx9_media_sharing_intel.h b/cpp/CL/cl_dx9_media_sharing_intel.h similarity index 100% rename from CL/cl_dx9_media_sharing_intel.h rename to cpp/CL/cl_dx9_media_sharing_intel.h diff --git a/CL/cl_egl.h b/cpp/CL/cl_egl.h similarity index 100% rename from CL/cl_egl.h rename to cpp/CL/cl_egl.h diff --git a/CL/cl_ext.h b/cpp/CL/cl_ext.h similarity index 100% rename from CL/cl_ext.h rename to cpp/CL/cl_ext.h diff --git a/CL/cl_ext_intel.h b/cpp/CL/cl_ext_intel.h similarity index 100% rename from CL/cl_ext_intel.h rename to cpp/CL/cl_ext_intel.h diff --git a/CL/cl_gl.h b/cpp/CL/cl_gl.h similarity index 100% rename from CL/cl_gl.h rename to cpp/CL/cl_gl.h diff --git a/CL/cl_gl_ext.h b/cpp/CL/cl_gl_ext.h similarity index 100% rename from CL/cl_gl_ext.h rename to cpp/CL/cl_gl_ext.h diff --git a/CL/cl_half.h b/cpp/CL/cl_half.h similarity index 100% rename from CL/cl_half.h rename to cpp/CL/cl_half.h diff --git a/CL/cl_icd.h b/cpp/CL/cl_icd.h similarity index 100% rename from CL/cl_icd.h rename to cpp/CL/cl_icd.h diff --git a/CL/cl_platform.h b/cpp/CL/cl_platform.h similarity index 100% rename from CL/cl_platform.h rename to cpp/CL/cl_platform.h diff --git a/CL/cl_va_api_media_sharing_intel.h b/cpp/CL/cl_va_api_media_sharing_intel.h similarity index 100% rename from CL/cl_va_api_media_sharing_intel.h rename to cpp/CL/cl_va_api_media_sharing_intel.h diff --git a/CL/cl_version.h b/cpp/CL/cl_version.h similarity index 100% rename from CL/cl_version.h rename to cpp/CL/cl_version.h diff --git a/CL/opencl.h b/cpp/CL/opencl.h similarity index 100% rename from CL/opencl.h rename to cpp/CL/opencl.h diff --git a/CMakeLists.txt b/cpp/CMakeLists.txt similarity index 100% rename from CMakeLists.txt rename to cpp/CMakeLists.txt diff --git a/CUDA.cmake b/cpp/CUDA.cmake similarity index 100% rename from CUDA.cmake rename to cpp/CUDA.cmake diff --git a/CUDA.make b/cpp/CUDA.make similarity index 100% rename from CUDA.make rename to cpp/CUDA.make diff --git a/CUDAStream.cu b/cpp/CUDAStream.cu similarity index 100% rename from CUDAStream.cu rename to cpp/CUDAStream.cu diff --git a/CUDAStream.h b/cpp/CUDAStream.h similarity index 100% rename from CUDAStream.h rename to cpp/CUDAStream.h diff --git a/HIP.cmake b/cpp/HIP.cmake similarity index 100% rename from HIP.cmake rename to cpp/HIP.cmake diff --git a/HIP.make b/cpp/HIP.make similarity index 100% rename from HIP.make rename to cpp/HIP.make diff --git a/HIPStream.cpp b/cpp/HIPStream.cpp similarity index 100% rename from HIPStream.cpp rename to cpp/HIPStream.cpp diff --git a/HIPStream.h b/cpp/HIPStream.h similarity index 100% rename from HIPStream.h rename to cpp/HIPStream.h diff --git a/KOKKOS.cmake b/cpp/KOKKOS.cmake similarity index 100% rename from KOKKOS.cmake rename to cpp/KOKKOS.cmake diff --git a/Kokkos.make b/cpp/Kokkos.make similarity index 100% rename from Kokkos.make rename to cpp/Kokkos.make diff --git a/KokkosStream.cpp b/cpp/KokkosStream.cpp similarity index 100% rename from KokkosStream.cpp rename to cpp/KokkosStream.cpp diff --git a/KokkosStream.hpp b/cpp/KokkosStream.hpp similarity index 100% rename from KokkosStream.hpp rename to cpp/KokkosStream.hpp diff --git a/LICENSE b/cpp/LICENSE similarity index 100% rename from LICENSE rename to cpp/LICENSE diff --git a/OCL.cmake b/cpp/OCL.cmake similarity index 100% rename from OCL.cmake rename to cpp/OCL.cmake diff --git a/OCLStream.cpp b/cpp/OCLStream.cpp similarity index 100% rename from OCLStream.cpp rename to cpp/OCLStream.cpp diff --git a/OCLStream.h b/cpp/OCLStream.h similarity index 100% rename from OCLStream.h rename to cpp/OCLStream.h diff --git a/OMP.cmake b/cpp/OMP.cmake similarity index 100% rename from OMP.cmake rename to cpp/OMP.cmake diff --git a/OMPStream.cpp b/cpp/OMPStream.cpp similarity index 100% rename from OMPStream.cpp rename to cpp/OMPStream.cpp diff --git a/OMPStream.h b/cpp/OMPStream.h similarity index 100% rename from OMPStream.h rename to cpp/OMPStream.h diff --git a/OpenACC.make b/cpp/OpenACC.make similarity index 100% rename from OpenACC.make rename to cpp/OpenACC.make diff --git a/OpenCL.make b/cpp/OpenCL.make similarity index 100% rename from OpenCL.make rename to cpp/OpenCL.make diff --git a/OpenMP.make b/cpp/OpenMP.make similarity index 100% rename from OpenMP.make rename to cpp/OpenMP.make diff --git a/RAJA.cmake b/cpp/RAJA.cmake similarity index 100% rename from RAJA.cmake rename to cpp/RAJA.cmake diff --git a/RAJA.make b/cpp/RAJA.make similarity index 100% rename from RAJA.make rename to cpp/RAJA.make diff --git a/RAJAStream.cpp b/cpp/RAJAStream.cpp similarity index 100% rename from RAJAStream.cpp rename to cpp/RAJAStream.cpp diff --git a/RAJAStream.hpp b/cpp/RAJAStream.hpp similarity index 100% rename from RAJAStream.hpp rename to cpp/RAJAStream.hpp diff --git a/STD.cmake b/cpp/STD.cmake similarity index 100% rename from STD.cmake rename to cpp/STD.cmake diff --git a/STD.make b/cpp/STD.make similarity index 100% rename from STD.make rename to cpp/STD.make diff --git a/STD20.cmake b/cpp/STD20.cmake similarity index 100% rename from STD20.cmake rename to cpp/STD20.cmake diff --git a/STD20.make b/cpp/STD20.make similarity index 100% rename from STD20.make rename to cpp/STD20.make diff --git a/STD20Stream.cpp b/cpp/STD20Stream.cpp similarity index 100% rename from STD20Stream.cpp rename to cpp/STD20Stream.cpp diff --git a/STD20Stream.hpp b/cpp/STD20Stream.hpp similarity index 100% rename from STD20Stream.hpp rename to cpp/STD20Stream.hpp diff --git a/STDStream.cpp b/cpp/STDStream.cpp similarity index 100% rename from STDStream.cpp rename to cpp/STDStream.cpp diff --git a/STDStream.h b/cpp/STDStream.h similarity index 100% rename from STDStream.h rename to cpp/STDStream.h diff --git a/SYCL.cmake b/cpp/SYCL.cmake similarity index 100% rename from SYCL.cmake rename to cpp/SYCL.cmake diff --git a/SYCL.make b/cpp/SYCL.make similarity index 100% rename from SYCL.make rename to cpp/SYCL.make diff --git a/SYCLStream.cpp b/cpp/SYCLStream.cpp similarity index 100% rename from SYCLStream.cpp rename to cpp/SYCLStream.cpp diff --git a/SYCLStream.h b/cpp/SYCLStream.h similarity index 100% rename from SYCLStream.h rename to cpp/SYCLStream.h diff --git a/Stream.h b/cpp/Stream.h similarity index 100% rename from Stream.h rename to cpp/Stream.h diff --git a/ci-prepare-bionic.sh b/cpp/ci-prepare-bionic.sh similarity index 100% rename from ci-prepare-bionic.sh rename to cpp/ci-prepare-bionic.sh diff --git a/ci-test-compile.sh b/cpp/ci-test-compile.sh similarity index 100% rename from ci-test-compile.sh rename to cpp/ci-test-compile.sh diff --git a/cmake/Modules/ComputeCppCompilerChecks.cmake b/cpp/cmake/Modules/ComputeCppCompilerChecks.cmake similarity index 100% rename from cmake/Modules/ComputeCppCompilerChecks.cmake rename to cpp/cmake/Modules/ComputeCppCompilerChecks.cmake diff --git a/cmake/Modules/ComputeCppIRMap.cmake b/cpp/cmake/Modules/ComputeCppIRMap.cmake similarity index 100% rename from cmake/Modules/ComputeCppIRMap.cmake rename to cpp/cmake/Modules/ComputeCppIRMap.cmake diff --git a/cmake/Modules/FindComputeCpp.cmake b/cpp/cmake/Modules/FindComputeCpp.cmake similarity index 100% rename from cmake/Modules/FindComputeCpp.cmake rename to cpp/cmake/Modules/FindComputeCpp.cmake diff --git a/cmake/toolchains/arm-gcc-poky.cmake b/cpp/cmake/toolchains/arm-gcc-poky.cmake similarity index 100% rename from cmake/toolchains/arm-gcc-poky.cmake rename to cpp/cmake/toolchains/arm-gcc-poky.cmake diff --git a/cmake/toolchains/gcc-generic.cmake b/cpp/cmake/toolchains/gcc-generic.cmake similarity index 100% rename from cmake/toolchains/gcc-generic.cmake rename to cpp/cmake/toolchains/gcc-generic.cmake diff --git a/legacy/HC.make b/cpp/legacy/HC.make similarity index 100% rename from legacy/HC.make rename to cpp/legacy/HC.make diff --git a/legacy/HCStream.cpp b/cpp/legacy/HCStream.cpp similarity index 100% rename from legacy/HCStream.cpp rename to cpp/legacy/HCStream.cpp diff --git a/legacy/HCStream.h b/cpp/legacy/HCStream.h similarity index 100% rename from legacy/HCStream.h rename to cpp/legacy/HCStream.h diff --git a/main.cpp b/cpp/main.cpp similarity index 100% rename from main.cpp rename to cpp/main.cpp diff --git a/register_models.cmake b/cpp/register_models.cmake similarity index 100% rename from register_models.cmake rename to cpp/register_models.cmake From 67a7447924b9fec5b1f34b1648dae6ade25ec13a Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Sun, 23 May 2021 07:36:08 +0100 Subject: [PATCH 22/78] Enter ./cpp first before CI tasks --- .github/workflows/main.yaml | 57 +++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 33f6573..173afed 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -3,7 +3,7 @@ on: [push, pull_request] jobs: - test: + "test_compile_cpp": runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 @@ -15,84 +15,87 @@ jobs: path: compilers key: ${{ runner.os }}-${{ hashFiles('./cpp/ci-prepare-bionic.sh') }} + - name: Prepare compilers + run: cd ./cpp + - name: Prepare compilers if: steps.prepare-compilers.outputs.cache-hit != 'true' - run: source ./cpp/ci-prepare-bionic.sh ./compilers SETUP true || true + run: source ./ci-prepare-bionic.sh ./compilers SETUP true || true - name: Setup test environment - run: source ./cpp/ci-prepare-bionic.sh ./compilers VARS false || true + run: source ./ci-prepare-bionic.sh ./compilers VARS false || true - name: Test compile gcc @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_13_BIN }} + run: ./ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_13_BIN }} - name: Test compile clang @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_13_BIN }} + run: ./ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_13_BIN }} - name: Test compile nvhpc @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_13_BIN }} + run: ./ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_13_BIN }} - name: Test compile aocc @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_13_BIN }} + run: ./ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_13_BIN }} - name: Test compile aomp @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_13_BIN }} + run: ./ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_13_BIN }} - name: Test compile hip @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_13_BIN }} + run: ./ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_13_BIN }} - name: Test compile dpcpp @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_13_BIN }} + run: ./ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_13_BIN }} - name: Test compile hipsycl @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_13_BIN }} + run: ./ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_13_BIN }} - name: Test compile gcc @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_15_BIN }} + run: ./ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_15_BIN }} - name: Test compile clang @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_15_BIN }} + run: ./ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_15_BIN }} - name: Test compile nvhpc @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_15_BIN }} + run: ./ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_15_BIN }} - name: Test compile aocc @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_15_BIN }} + run: ./ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_15_BIN }} - name: Test compile aomp @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_15_BIN }} + run: ./ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_15_BIN }} - name: Test compile hip @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_15_BIN }} + run: ./ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_15_BIN }} - name: Test compile dpcpp @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_15_BIN }} + run: ./ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_15_BIN }} - name: Test compile hipsycl @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_15_BIN }} + run: ./ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_15_BIN }} - name: Test compile gcc @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_18_BIN }} + run: ./ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_18_BIN }} - name: Test compile clang @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_18_BIN }} + run: ./ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_18_BIN }} - name: Test compile nvhpc @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_18_BIN }} + run: ./ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_18_BIN }} - name: Test compile aocc @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_18_BIN }} + run: ./ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_18_BIN }} - name: Test compile aomp @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_18_BIN }} + run: ./ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_18_BIN }} - name: Test compile hip @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_18_BIN }} + run: ./ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_18_BIN }} - name: Test compile dpcpp @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_18_BIN }} + run: ./ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_18_BIN }} - name: Test compile hipsycl @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./cpp/ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_18_BIN }} \ No newline at end of file + run: ./ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_18_BIN }} \ No newline at end of file From 60d4cb8c8763e772087309c19bf439f4f42fcdd1 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Sun, 23 May 2021 07:39:04 +0100 Subject: [PATCH 23/78] Enter ./cpp first before CI tasks, take 2 --- .github/workflows/main.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 173afed..43a036f 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -5,6 +5,9 @@ on: [push, pull_request] jobs: "test_compile_cpp": runs-on: ubuntu-18.04 + defaults: + run: + working-directory: ./cpp steps: - uses: actions/checkout@v2 @@ -15,9 +18,6 @@ jobs: path: compilers key: ${{ runner.os }}-${{ hashFiles('./cpp/ci-prepare-bionic.sh') }} - - name: Prepare compilers - run: cd ./cpp - - name: Prepare compilers if: steps.prepare-compilers.outputs.cache-hit != 'true' run: source ./ci-prepare-bionic.sh ./compilers SETUP true || true From cc86998f50f9c8b5e58dcab91e670961ebd2aa96 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Sun, 23 May 2021 08:03:30 +0100 Subject: [PATCH 24/78] Initial Scala implementation --- README.md | 2 + scala-stream/.bsp/sbt.json | 1 + scala-stream/.gitignore | 1 + scala-stream/.jvmopts | 2 + scala-stream/.scalafmt.conf | 34 ++ scala-stream/README.md | 102 +++++ scala-stream/build.sbt | 29 ++ scala-stream/project/build.properties | 1 + scala-stream/project/plugins.sbt | 6 + scala-stream/reflect-config.json | 11 + scala-stream/sbt | 3 + scala-stream/sbt-dist/bin/java9-rt-export.jar | Bin 0 -> 4658 bytes scala-stream/sbt-dist/bin/sbt | 177 +++++++++ scala-stream/sbt-dist/bin/sbt-launch-lib.bash | 363 +++++++++++++++++ scala-stream/sbt-dist/bin/sbt-launch.jar | Bin 0 -> 1273821 bytes scala-stream/sbt-dist/bin/sbt.bat | 212 ++++++++++ scala-stream/sbt-dist/conf/sbtconfig.txt | 14 + scala-stream/sbt-dist/conf/sbtopts | 49 +++ .../main/scala/scalastream/J8SStream.scala | 44 +++ .../main/scala/scalastream/ParStream.scala | 36 ++ .../main/scala/scalastream/PlainStream.scala | 31 ++ .../main/scala/scalastream/ScalaStream.scala | 369 ++++++++++++++++++ .../main/scala/scalastream/ThreadStream.scala | 68 ++++ 23 files changed, 1555 insertions(+) create mode 100644 scala-stream/.bsp/sbt.json create mode 100644 scala-stream/.gitignore create mode 100644 scala-stream/.jvmopts create mode 100644 scala-stream/.scalafmt.conf create mode 100644 scala-stream/README.md create mode 100644 scala-stream/build.sbt create mode 100644 scala-stream/project/build.properties create mode 100644 scala-stream/project/plugins.sbt create mode 100644 scala-stream/reflect-config.json create mode 100755 scala-stream/sbt create mode 100644 scala-stream/sbt-dist/bin/java9-rt-export.jar create mode 100755 scala-stream/sbt-dist/bin/sbt create mode 100644 scala-stream/sbt-dist/bin/sbt-launch-lib.bash create mode 100644 scala-stream/sbt-dist/bin/sbt-launch.jar create mode 100644 scala-stream/sbt-dist/bin/sbt.bat create mode 100644 scala-stream/sbt-dist/conf/sbtconfig.txt create mode 100644 scala-stream/sbt-dist/conf/sbtopts create mode 100644 scala-stream/src/main/scala/scalastream/J8SStream.scala create mode 100644 scala-stream/src/main/scala/scalastream/ParStream.scala create mode 100644 scala-stream/src/main/scala/scalastream/PlainStream.scala create mode 100644 scala-stream/src/main/scala/scalastream/ScalaStream.scala create mode 100644 scala-stream/src/main/scala/scalastream/ThreadStream.scala diff --git a/README.md b/README.md index 8ca7398..aa41a60 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ Currently implemented are: This code was previously called GPU-STREAM. +This project also contains implementations in alternative languages with different build systems: +* Scala - [scala-stream](./scala-stream) How is this different to STREAM? -------------------------------- diff --git a/scala-stream/.bsp/sbt.json b/scala-stream/.bsp/sbt.json new file mode 100644 index 0000000..2e1edb1 --- /dev/null +++ b/scala-stream/.bsp/sbt.json @@ -0,0 +1 @@ +{"name":"sbt","version":"1.5.2","bspVersion":"2.0.0-M5","languages":["scala"],"argv":["/usr/lib/jvm/java-11-openjdk-11.0.11.0.9-2.fc33.x86_64/bin/java","-Xms100m","-Xmx100m","-classpath","/home/tom/.local/share/JetBrains/Toolbox/apps/IDEA-U/ch-0/211.7142.45.plugins/Scala/launcher/sbt-launch.jar","xsbt.boot.Boot","-bsp","--sbt-launch-jar=/home/tom/.local/share/JetBrains/Toolbox/apps/IDEA-U/ch-0/211.7142.45.plugins/Scala/launcher/sbt-launch.jar"]} \ No newline at end of file diff --git a/scala-stream/.gitignore b/scala-stream/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/scala-stream/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/scala-stream/.jvmopts b/scala-stream/.jvmopts new file mode 100644 index 0000000..c1ef295 --- /dev/null +++ b/scala-stream/.jvmopts @@ -0,0 +1,2 @@ +-Xmx4096m +-Xss4m \ No newline at end of file diff --git a/scala-stream/.scalafmt.conf b/scala-stream/.scalafmt.conf new file mode 100644 index 0000000..8c7d0c8 --- /dev/null +++ b/scala-stream/.scalafmt.conf @@ -0,0 +1,34 @@ +version = "3.0.0-RC2" +runner.dialect = scala3 + +style = defaultWithAlign + +maxColumn = 100 + +align.preset = more + +rewrite.rules = [ + AvoidInfix + RedundantBraces + RedundantParens + AsciiSortImports + PreferCurlyFors +] + +rewrite.neverInfix.excludeFilters = [until + to + by + eq + ne + "should.*" + "contain.*" + "must.*" + in + be + taggedAs + thrownBy + synchronized + have + when + size + theSameElementsAs] \ No newline at end of file diff --git a/scala-stream/README.md b/scala-stream/README.md new file mode 100644 index 0000000..bf0e3f4 --- /dev/null +++ b/scala-stream/README.md @@ -0,0 +1,102 @@ +ScalaStream +=========== + +This is an implementation of BabelStream +in [Scala 3](https://docs.scala-lang.org/scala3/new-in-scala3.html) on the JVM. In theory, this +implementation also covers Java. Scala and Java, like any other programming language, has its own +ecosystem of library supported parallel programming frameworks, we currently implement the +following: + +* Parallel streams (introduced in Java 8) - `src/main/scala/scalastream/J8SStream.scala` +* [Scala Parallel Collections](https://github.com/scala/scala-parallel-collections) + - `src/main/scala/scalastream/ParStream.scala` + +As the benchmark is relatively simple, we also implement some baselines: + +* Single threaded Scala `for` (i.e `foreach` sugar) - `src/main/scala/scalastream/PlainStream.scala` +* Manually parallelism with Java executors - `src/main/scala/scalastream/ThreadedStream.scala` + +### Performance considerations + +As Scala 3 defaults to Scala 2.13's standard library, we roll our own `Fractional` typeclass with +liberal use of inlining and specialisation. This is motivated by 2.13 stdlib's lack of +specialisation for primitives types on the default `Fractional` and `Numeric` typeclasses. + +The use of [Spire](https://github.com/typelevel/spire) to mitigate this was attempted, however, due +to its use of Scala 2 macros, it currently doesn't compile with Scala 3. + +### Build & Run + +Prerequisites + +* JDK >= 8 on any of its supported platform; known working implementations: + - OpenJDK + distributions ([Amazon Corretto](https://docs.aws.amazon.com/corretto/latest/corretto-11-ug/downloads-list.html) + , [Azul](https://www.azul.com/downloads/?version=java-11-lts&package=jdk) + , [AdoptOpenJDK](https://adoptopenjdk.net/), etc) + - Oracle Graal CE/EE 8+ + +To run the benchmark, first create a binary: + +```shell +> ./sbt assembly +``` + +The binary will be located at `./target/scala-3.0.0/scala-stream.jar`. Run it with: + +```shell +> java -version +openjdk version "11.0.11" 2021-04-20 +OpenJDK Runtime Environment 18.9 (build 11.0.11+9) +OpenJDK 64-Bit Server VM 18.9 (build 11.0.11+9, mixed mode, sharing) +> java -jar target/scala-3.0.0/scala-stream.jar --help + +``` + +For best results, benchmark with the following JVM flags: + +``` +-XX:-UseOnStackReplacement # disable OSR, not useful for this benchmark as we are measuring peak performance +-XX:-TieredCompilation # disable C1, go straight to C2 +-XX:ReservedCodeCacheSize=512m # don't flush compiled code out of cache at any point +``` + +Worked example: + +```shell +> java -XX:-UseOnStackReplacement -XX:-TieredCompilation -XX:ReservedCodeCacheSize=512m -jar target/scala-3.0.0/scala-stream.jar + +BabelStream +Version: 3.4.0 +Implementation: Scala Parallel Collections; Scala (Java 11.0.11; Red Hat, Inc.; home=/usr/lib/jvm/java-11-openjdk-11.0.11.0.9-2.fc33.x86_64) +Running kernels 100 times +Precision: double +Array size: 268.4 MB (=0.3 GB) +Total size: 805.3 MB (=0.8 GB) +Function MBytes/sec Min (sec) Max Average +Copy 4087.077 0.13136 0.24896 0.15480 +Mul 2934.709 0.18294 0.28706 0.21627 +Add 3016.342 0.26698 0.39835 0.31119 +Triad 3016.496 0.26697 0.37612 0.31040 +Dot 2216.096 0.24226 0.41235 0.28264 + +``` + +### Graal Native Image + +The port has partial support for Graal Native Image, to generate one, run: + +```shell +> ./sbt nativeImage +``` + +The ELF binary will be located at `./target/native-image/scala-stream`, relocation should work on +the same architecture the binary is built on. + +There's an ongoing bug with Scala 3 's use of `lazy val`s where the program crashes at declaration +site. Currently, Scala Parallel Collections uses this feature internally, so selecting this device +will crash at runtime. + +The bug originates from the use of `Unsafe` in `lazy val` for thready safety guarantees. It seems +that Graal only supports limited uses of this JVM implementation detail and Scala 3 happens to be on +the unsupported side. \ No newline at end of file diff --git a/scala-stream/build.sbt b/scala-stream/build.sbt new file mode 100644 index 0000000..4194acb --- /dev/null +++ b/scala-stream/build.sbt @@ -0,0 +1,29 @@ +lazy val mainCls = Some("scalastream.App") + +lazy val root = (project in file(".")) + .enablePlugins(NativeImagePlugin) + .settings( + scalaVersion := "3.0.0", + version := "3.4.0", + organization := "uk.ac.bristol.uob-hpc", + organizationName := "University of Bristol", + Compile / mainClass := mainCls, + assembly / mainClass := mainCls, + scalacOptions ~= filterConsoleScalacOptions, + assembly / assemblyJarName := "scala-stream.jar", + nativeImageOptions := Seq( + "--no-fallback", + "-H:ReflectionConfigurationFiles=../../reflect-config.json" + ), + nativeImageVersion := "21.1.0", + (Global / excludeLintKeys) += nativeImageVersion, + name := "scala-stream", + libraryDependencies ++= Seq( + // Lazy val implementation in Scala 3 triggers an exception in nativeImage, use 2_13 for arg parsing for now otherwise we can't get to the benchmarking part + ("com.github.scopt" %% "scopt" % "4.0.1").cross(CrossVersion.for3Use2_13), + // par also uses lazy val at some point, so it doesn't work in nativeImage + "org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.3", + "net.openhft" % "affinity" % "3.21ea1", + "org.slf4j" % "slf4j-simple" % "1.7.30" // for affinity + ) + ) diff --git a/scala-stream/project/build.properties b/scala-stream/project/build.properties new file mode 100644 index 0000000..19479ba --- /dev/null +++ b/scala-stream/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.5.2 diff --git a/scala-stream/project/plugins.sbt b/scala-stream/project/plugins.sbt new file mode 100644 index 0000000..2c82902 --- /dev/null +++ b/scala-stream/project/plugins.sbt @@ -0,0 +1,6 @@ +addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.5.3") +addSbtPlugin("io.github.davidgregory084" % "sbt-tpolecat" % "0.1.17") +addSbtPlugin("org.scalameta" % "sbt-native-image" % "0.3.0") +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.27") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.2") diff --git a/scala-stream/reflect-config.json b/scala-stream/reflect-config.json new file mode 100644 index 0000000..9e8b089 --- /dev/null +++ b/scala-stream/reflect-config.json @@ -0,0 +1,11 @@ +[ + { + "name": "sun.misc.Unsafe", + "fields": [ + { + "name": "theUnsafe", + "allowUnsafeAccess": true + } + ] + } +] \ No newline at end of file diff --git a/scala-stream/sbt b/scala-stream/sbt new file mode 100755 index 0000000..efdfda6 --- /dev/null +++ b/scala-stream/sbt @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +./sbt-dist/bin/sbt "$@" \ No newline at end of file diff --git a/scala-stream/sbt-dist/bin/java9-rt-export.jar b/scala-stream/sbt-dist/bin/java9-rt-export.jar new file mode 100644 index 0000000000000000000000000000000000000000..cbabfb076ec041acffacfec32a96923cc4b21f29 GIT binary patch literal 4658 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 Q#NQv)3jn|%)%n~10sqa|VgLXD literal 0 HcmV?d00001 diff --git a/scala-stream/sbt-dist/bin/sbt b/scala-stream/sbt-dist/bin/sbt new file mode 100755 index 0000000..cca77be --- /dev/null +++ b/scala-stream/sbt-dist/bin/sbt @@ -0,0 +1,177 @@ +#!/usr/bin/env bash + + +### ------------------------------- ### +### Helper methods for BASH scripts ### +### ------------------------------- ### + +realpath () { +( + TARGET_FILE="$1" + FIX_CYGPATH="$2" + + cd "$(dirname "$TARGET_FILE")" + TARGET_FILE=$(basename "$TARGET_FILE") + + COUNT=0 + while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ] + do + TARGET_FILE=$(readlink "$TARGET_FILE") + cd "$(dirname "$TARGET_FILE")" + TARGET_FILE=$(basename "$TARGET_FILE") + COUNT=$(($COUNT + 1)) + done + + # make sure we grab the actual windows path, instead of cygwin's path. + if [[ "x$FIX_CYGPATH" != "x" ]]; then + echo "$(cygwinpath "$(pwd -P)/$TARGET_FILE")" + else + echo "$(pwd -P)/$TARGET_FILE" + fi +) +} + + +# Uses uname to detect if we're in the odd cygwin environment. +is_cygwin() { + local os=$(uname -s) + case "$os" in + CYGWIN*) return 0 ;; + MINGW*) return 0 ;; + MSYS*) return 0 ;; + *) return 1 ;; + esac +} + +# TODO - Use nicer bash-isms here. +CYGWIN_FLAG=$(if is_cygwin; then echo true; else echo false; fi) + + +# This can fix cygwin style /cygdrive paths so we get the +# windows style paths. +cygwinpath() { + local file="$1" + if [[ "$CYGWIN_FLAG" == "true" ]]; then + echo $(cygpath -w $file) + else + echo $file + fi +} + +. "$(dirname "$(realpath "$0")")/sbt-launch-lib.bash" + + +declare -r noshare_opts="-Dsbt.global.base=project/.sbtboot -Dsbt.boot.directory=project/.boot -Dsbt.ivy.home=project/.ivy" +declare -r sbt_opts_file=".sbtopts" +declare -r etc_sbt_opts_file="/etc/sbt/sbtopts" +declare -r dist_sbt_opts_file="${sbt_home}/conf/sbtopts" +declare -r win_sbt_opts_file="${sbt_home}/conf/sbtconfig.txt" + +usage() { + cat < path to global settings/plugins directory (default: ~/.sbt) + -sbt-boot path to shared boot directory (default: ~/.sbt/boot in 0.11 series) + -ivy path to local Ivy repository (default: ~/.ivy2) + -mem set memory options (default: $sbt_default_mem, which is $(get_mem_opts)) + -no-share use all local caches; no sharing + -no-global uses global caches, but does not use global ~/.sbt directory. + -jvm-debug Turn on JVM debugging, open at the given port. + -batch Disable interactive mode + + # sbt version (default: from project/build.properties if present, else latest release) + -sbt-version use the specified version of sbt + -sbt-jar use the specified jar as the sbt launcher + -sbt-rc use an RC version of sbt + -sbt-snapshot use a snapshot version of sbt + + # java version (default: java from PATH, currently $(java -version 2>&1 | grep version)) + -java-home alternate JAVA_HOME + + # jvm options and output control + JAVA_OPTS environment variable, if unset uses "$java_opts" + .jvmopts if this file exists in the current directory, its contents + are appended to JAVA_OPTS + SBT_OPTS environment variable, if unset uses "$default_sbt_opts" + .sbtopts if this file exists in the current directory, its contents + are prepended to the runner args + /etc/sbt/sbtopts if this file exists, it is prepended to the runner args + -Dkey=val pass -Dkey=val directly to the java runtime + -J-X pass option -X directly to the java runtime + (-J is stripped) + -S-X add -X to sbt's scalacOptions (-S is stripped) + +In the case of duplicated or conflicting options, the order above +shows precedence: JAVA_OPTS lowest, command line options highest. +EOM +} + + + +process_my_args () { + while [[ $# -gt 0 ]]; do + case "$1" in + -no-colors) addJava "-Dsbt.log.noformat=true" && shift ;; + -no-share) addJava "$noshare_opts" && shift ;; + -no-global) addJava "-Dsbt.global.base=$(pwd)/project/.sbtboot" && shift ;; + -sbt-boot) require_arg path "$1" "$2" && addJava "-Dsbt.boot.directory=$2" && shift 2 ;; + -sbt-dir) require_arg path "$1" "$2" && addJava "-Dsbt.global.base=$2" && shift 2 ;; + -debug-inc) addJava "-Dxsbt.inc.debug=true" && shift ;; + -batch) exec + link=$(expr "$ls" : '.*-> \(.*\)$') + if expr "$link" : '/.*' > /dev/null; then + SCRIPT="$link" + else + SCRIPT=$(dirname "$SCRIPT")/"$link" + fi +done +declare -r sbt_bin_dir="$(dirname "$SCRIPT")" +declare -r sbt_home="$(dirname "$sbt_bin_dir")" + +echoerr () { + echo 1>&2 "$@" +} +vlog () { + [[ $verbose || $debug ]] && echoerr "$@" +} +dlog () { + [[ $debug ]] && echoerr "$@" +} + +jar_file () { + echo "$(cygwinpath "${sbt_home}/bin/sbt-launch.jar")" +} + +acquire_sbt_jar () { + sbt_jar="$(jar_file)" + + if [[ ! -f "$sbt_jar" ]]; then + echoerr "Could not find launcher jar: $sbt_jar" + exit 2 + fi +} + +rt_export_file () { + echo "${sbt_bin_dir}/java9-rt-export.jar" +} + +execRunner () { + # print the arguments one to a line, quoting any containing spaces + [[ $verbose || $debug ]] && echo "# Executing command line:" && { + for arg; do + if printf "%s\n" "$arg" | grep -q ' '; then + printf "\"%s\"\n" "$arg" + else + printf "%s\n" "$arg" + fi + done + echo "" + } + + # THis used to be exec, but we loose the ability to re-hook stty then + # for cygwin... Maybe we should flag the feature here... + "$@" +} + +addJava () { + dlog "[addJava] arg = '$1'" + java_args=( "${java_args[@]}" "$1" ) +} +addSbt () { + dlog "[addSbt] arg = '$1'" + sbt_commands=( "${sbt_commands[@]}" "$1" ) +} +addResidual () { + dlog "[residual] arg = '$1'" + residual_args=( "${residual_args[@]}" "$1" ) +} +addDebugger () { + addJava "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$1" +} + +get_mem_opts () { + # if we detect any of these settings in ${JAVA_OPTS} or ${JAVA_TOOL_OPTIONS} we need to NOT output our settings. + # The reason is the Xms/Xmx, if they don't line up, cause errors. + if [[ "${JAVA_OPTS}" == *-Xmx* ]] || [[ "${JAVA_OPTS}" == *-Xms* ]] || [[ "${JAVA_OPTS}" == *-XX:MaxPermSize* ]] || [[ "${JAVA_OPTS}" == *-XX:MaxMetaspaceSize* ]] || [[ "${JAVA_OPTS}" == *-XX:ReservedCodeCacheSize* ]]; then + echo "" + elif [[ "${JAVA_TOOL_OPTIONS}" == *-Xmx* ]] || [[ "${JAVA_TOOL_OPTIONS}" == *-Xms* ]] || [[ "${JAVA_TOOL_OPTIONS}" == *-XX:MaxPermSize* ]] || [[ "${JAVA_TOOL_OPTIONS}" == *-XX:MaxMetaspaceSize* ]] || [[ "${JAVA_TOOL_OPTIONS}" == *-XX:ReservedCodeCacheSize* ]]; then + echo "" + elif [[ "${SBT_OPTS}" == *-Xmx* ]] || [[ "${SBT_OPTS}" == *-Xms* ]] || [[ "${SBT_OPTS}" == *-XX:MaxPermSize* ]] || [[ "${SBT_OPTS}" == *-XX:MaxMetaspaceSize* ]] || [[ "${SBT_OPTS}" == *-XX:ReservedCodeCacheSize* ]]; then + echo "" + else + # a ham-fisted attempt to move some memory settings in concert + # so they need not be messed around with individually. + local mem=${1:-$sbt_default_mem} + local codecache=$(( $mem / 8 )) + (( $codecache > 128 )) || codecache=128 + (( $codecache < 512 )) || codecache=512 + local class_metadata_size=$(( $codecache * 2 )) + if [[ -z $java_version ]]; then + java_version=$(jdk_version) + fi + local class_metadata_opt=$((( $java_version < 8 )) && echo "MaxPermSize" || echo "MaxMetaspaceSize") + + local arg_xms=$([[ "${java_args[@]}" == *-Xms* ]] && echo "" || echo "-Xms${mem}m") + local arg_xmx=$([[ "${java_args[@]}" == *-Xmx* ]] && echo "" || echo "-Xmx${mem}m") + local arg_rccs=$([[ "${java_args[@]}" == *-XX:ReservedCodeCacheSize* ]] && echo "" || echo "-XX:ReservedCodeCacheSize=${codecache}m") + local arg_meta=$([[ "${java_args[@]}" == *-XX:${class_metadata_opt}* && ! (( $java_version < 8 )) ]] && echo "" || echo "-XX:${class_metadata_opt}=${class_metadata_size}m") + + echo "${arg_xms} ${arg_xmx} ${arg_rccs} ${arg_meta}" + fi +} + +get_gc_opts () { + local older_than_9=$(( $java_version < 9 )) + + if [[ "$older_than_9" == "1" ]]; then + # don't need to worry about gc + echo "" + elif [[ "${JAVA_OPTS}" =~ Use.*GC ]] || [[ "${JAVA_TOOL_OPTIONS}" =~ Use.*GC ]] || [[ "${SBT_OPTS}" =~ Use.*GC ]] ; then + # GC arg has been passed in - don't change + echo "" + else + # Java 9+ so revert to old + echo "-XX:+UseParallelGC" + fi +} + +require_arg () { + local type="$1" + local opt="$2" + local arg="$3" + if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then + echo "$opt requires <$type> argument" + exit 1 + fi +} + +is_function_defined() { + declare -f "$1" > /dev/null +} + +# parses JDK version from the -version output line. +# 8 for 1.8.0_nn, 9 for 9-ea etc, and "no_java" for undetected +jdk_version() { + local result + local lines=$("$java_cmd" -Xms32M -Xmx32M -version 2>&1 | tr '\r' '\n') + local IFS=$'\n' + for line in $lines; do + if [[ (-z $result) && ($line = *"version \""*) ]] + then + local ver=$(echo $line | sed -e 's/.*version "\(.*\)"\(.*\)/\1/; 1q') + # on macOS sed doesn't support '?' + if [[ $ver = "1."* ]] + then + result=$(echo $ver | sed -e 's/1\.\([0-9]*\)\(.*\)/\1/; 1q') + else + result=$(echo $ver | sed -e 's/\([0-9]*\)\(.*\)/\1/; 1q') + fi + fi + done + if [[ -z $result ]] + then + result=no_java + fi + echo "$result" +} + +process_args () { + while [[ $# -gt 0 ]]; do + case "$1" in + -h|-help) usage; exit 1 ;; + -v|-verbose) verbose=1 && shift ;; + -d|-debug) debug=1 && addSbt "-debug" && shift ;; + + -ivy) require_arg path "$1" "$2" && addJava "-Dsbt.ivy.home=$2" && shift 2 ;; + -mem) require_arg integer "$1" "$2" && sbt_mem="$2" && shift 2 ;; + -jvm-debug) require_arg port "$1" "$2" && addDebugger $2 && shift 2 ;; + -batch) exec /dev/null 2>&1 && { + mkdir -p "$target_preloaded" + rsync -a --ignore-existing "$source_preloaded" "$target_preloaded" + } + } + } +} + +# Detect that we have java installed. +checkJava() { + local required_version="$1" + # Now check to see if it's a good enough version + local good_enough="$(expr $java_version ">=" $required_version)" + if [[ "$java_version" == "" ]]; then + echo + echo "No Java Development Kit (JDK) installation was detected." + echo Please go to http://www.oracle.com/technetwork/java/javase/downloads/ and download. + echo + exit 1 + elif [[ "$good_enough" != "1" ]]; then + echo + echo "The Java Development Kit (JDK) installation you have is not up to date." + echo $script_name requires at least version $required_version+, you have + echo version $java_version + echo + echo Please go to http://www.oracle.com/technetwork/java/javase/downloads/ and download + echo a valid JDK and install before running $script_name. + echo + exit 1 + fi +} + +copyRt() { + local at_least_9="$(expr $java_version ">=" 9)" + if [[ "$at_least_9" == "1" ]]; then + rtexport=$(rt_export_file) + # The grep for java9-rt-ext- matches the filename prefix printed in Export.java + java9_ext=$("$java_cmd" ${JAVA_OPTS} ${SBT_OPTS:-$default_sbt_opts} ${java_args[@]} \ + -jar "$rtexport" --rt-ext-dir | grep java9-rt-ext-) + java9_rt=$(echo "$java9_ext/rt.jar") + vlog "[copyRt] java9_rt = '$java9_rt'" + if [[ ! -f "$java9_rt" ]]; then + echo Copying runtime jar. + mkdir -p "$java9_ext" + execRunner "$java_cmd" \ + ${JAVA_OPTS} \ + ${SBT_OPTS:-$default_sbt_opts} \ + ${java_args[@]} \ + -jar "$rtexport" \ + "${java9_rt}" + fi + addJava "-Dscala.ext.dirs=${java9_ext}" + fi +} + +run() { + # process the combined args, then reset "$@" to the residuals + process_args "$@" + set -- "${residual_args[@]}" + argumentCount=$# + + # Copy preloaded repo to user's preloaded directory + syncPreloaded + + # no jar? download it. + [[ -f "$sbt_jar" ]] || acquire_sbt_jar "$sbt_version" || { + # still no jar? uh-oh. + echo "Download failed. Obtain the sbt-launch.jar manually and place it at $sbt_jar" + exit 1 + } + + # TODO - java check should be configurable... + checkJava "6" + + # Java 9 support + copyRt + + #If we're in cygwin, we should use the windows config, and terminal hacks + if [[ "$CYGWIN_FLAG" == "true" ]]; then + stty -icanon min 1 -echo > /dev/null 2>&1 + addJava "-Djline.terminal=jline.UnixTerminal" + addJava "-Dsbt.cygwin=true" + fi + + # run sbt + execRunner "$java_cmd" \ + $(get_mem_opts $sbt_mem) \ + $(get_gc_opts) \ + ${JAVA_OPTS} \ + ${SBT_OPTS:-$default_sbt_opts} \ + ${java_args[@]} \ + -jar "$sbt_jar" \ + "${sbt_commands[@]}" \ + "${residual_args[@]}" + + exit_code=$? + + # Clean up the terminal from cygwin hacks. + if [[ "$CYGWIN_FLAG" == "true" ]]; then + stty icanon echo > /dev/null 2>&1 + fi + exit $exit_code +} diff --git a/scala-stream/sbt-dist/bin/sbt-launch.jar b/scala-stream/sbt-dist/bin/sbt-launch.jar new file mode 100644 index 0000000000000000000000000000000000000000..26ab884eb1f80c7b603c35ddcc8bbf646b7ecd1f GIT binary patch literal 1273821 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 @i9d$)=NAGa&cW=>~k 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

    & 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�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 $n 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ྵ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

    q8g^vJSyrUGp+; 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 z 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

    IAP0~nrdt|_h$MS2E)TRg#} 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 >nul +) else if "!JVM_DEBUG!" == "true" ( + set /a JVM_DEBUG_PORT=%1 2>nul >nul + if not "%~1" == "!JVM_DEBUG_PORT!" ( + set SBT_ARGS=!SBT_ARGS! %1 + ) +) else if /I "%~1" == "new" ( + set sbt_new=true + set SBT_ARGS=!SBT_ARGS! %1 +) else ( + set SBT_ARGS=!SBT_ARGS! %1 +) + +shift +goto args_loop +:args_end + +rem Confirm a user's intent if the current directory does not look like an sbt +rem top-level directory and the "new" command was not given. +if not exist build.sbt ( + if not exist project\ ( + if not defined sbt_new ( + echo [warn] Neither build.sbt nor a 'project' directory in the current directory: %CD% + setlocal +:confirm + echo c^) continue + echo q^) quit + + set /P reply=?^ + if /I "!reply!" == "c" ( + goto confirm_end + ) else if /I "!reply!" == "q" ( + exit /B 1 + ) + + goto confirm +:confirm_end + endlocal + ) + ) +) + +call :process + +call :checkjava + +call :copyrt + +if defined JVM_DEBUG_PORT ( + set _JAVA_OPTS=!_JAVA_OPTS! -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=!JVM_DEBUG_PORT! +) + +call :sync_preloaded + +call :run %SBT_ARGS% + +if ERRORLEVEL 1 goto error +goto end + +:run + +"%_JAVACMD%" %_JAVA_OPTS% %SBT_OPTS% -cp "%SBT_HOME%sbt-launch.jar" xsbt.boot.Boot %* +goto :eof + +:process +rem Parses x out of 1.x; for example 8 out of java version 1.8.0_xx +rem Otherwise, parses the major version; 9 out of java version 9-ea +set JAVA_VERSION=0 +for /f "tokens=3" %%g in ('"%_JAVACMD%" -Xms32M -Xmx32M -version 2^>^&1 ^| findstr /i version') do ( + set JAVA_VERSION=%%g +) +set JAVA_VERSION=%JAVA_VERSION:"=% +for /f "delims=.-_ tokens=1-2" %%v in ("%JAVA_VERSION%") do ( + if /I "%%v" EQU "1" ( + set JAVA_VERSION=%%w + ) else ( + set JAVA_VERSION=%%v + ) +) +exit /B 0 + +:checkjava +set required_version=6 +if /I %JAVA_VERSION% GEQ %required_version% ( + exit /B 0 +) +echo. +echo The Java Development Kit (JDK) installation you have is not up to date. +echo sbt requires at least version %required_version%+, you have +echo version %JAVA_VERSION% +echo. +echo Please go to http://www.oracle.com/technetwork/java/javase/downloads/ and download +echo a valid JDK and install before running sbt. +echo. +exit /B 1 + +:copyrt +if /I %JAVA_VERSION% GEQ 9 ( + set rtexport=!SBT_HOME!java9-rt-export.jar + + "%_JAVACMD%" %_JAVA_OPTS% %SBT_OPTS% -jar "!rtexport!" --rt-ext-dir > "%TEMP%.\rtext.txt" + set /p java9_ext= < "%TEMP%.\rtext.txt" + set java9_rt=!java9_ext!\rt.jar + + if not exist "!java9_rt!" ( + mkdir "!java9_ext!" + "%_JAVACMD%" %_JAVA_OPTS% %SBT_OPTS% -jar "!rtexport!" "!java9_rt!" + ) + set _JAVA_OPTS=!_JAVA_OPTS! -Dscala.ext.dirs="!java9_ext!" + + rem check to see if a GC has been set in the opts + echo !_JAVA_OPTS! | findstr /r "Use.*GC" >nul + if ERRORLEVEL 1 ( + rem don't have a GC set - revert to old GC + set _JAVA_OPTS=!_JAVA_OPTS! -XX:+UseParallelGC + ) +) +exit /B 0 + +:sync_preloaded +if "%INIT_SBT_VERSION%"=="" ( + rem FIXME: better %INIT_SBT_VERSION% detection + FOR /F "tokens=* USEBACKQ" %%F IN (`dir /b "%SBT_HOME%\..\lib\local-preloaded\org.scala-sbt\sbt" /B`) DO ( + SET INIT_SBT_VERSION=%%F + ) +) +set PRELOAD_SBT_JAR="%UserProfile%\.sbt\preloaded\org.scala-sbt\sbt\%INIT_SBT_VERSION%\jars\sbt.jar" +if /I %JAVA_VERSION% GEQ 8 ( + where robocopy >nul 2>nul + if %ERRORLEVEL% equ 0 ( + REM echo %PRELOAD_SBT_JAR% + if not exist %PRELOAD_SBT_JAR% ( + if exist "%SBT_HOME%\..\lib\local-preloaded\" ( + echo "about to robocopy" + robocopy "%SBT_HOME%\..\lib\local-preloaded" "%UserProfile%\.sbt\preloaded" /E + ) + ) + ) +) +exit /B 0 + +:error +@endlocal +exit /B 1 + +:end +@endlocal +exit /B 0 diff --git a/scala-stream/sbt-dist/conf/sbtconfig.txt b/scala-stream/sbt-dist/conf/sbtconfig.txt new file mode 100644 index 0000000..a4da43e --- /dev/null +++ b/scala-stream/sbt-dist/conf/sbtconfig.txt @@ -0,0 +1,14 @@ +# Set the java args to high + +-Xmx512M + +-XX:MaxPermSize=256m + +-XX:ReservedCodeCacheSize=128m + + + +# Set the extra SBT options + +-Dsbt.log.format=true + diff --git a/scala-stream/sbt-dist/conf/sbtopts b/scala-stream/sbt-dist/conf/sbtopts new file mode 100644 index 0000000..f018465 --- /dev/null +++ b/scala-stream/sbt-dist/conf/sbtopts @@ -0,0 +1,49 @@ +# ------------------------------------------------ # +# The SBT Configuration file. # +# ------------------------------------------------ # + + +# Disable ANSI color codes +# +#-no-colors + +# Starts sbt even if the current directory contains no sbt project. +# +-sbt-create + +# Path to global settings/plugins directory (default: ~/.sbt) +# +#-sbt-dir /etc/sbt + +# Path to shared boot directory (default: ~/.sbt/boot in 0.11 series) +# +#-sbt-boot ~/.sbt/boot + +# Path to local Ivy repository (default: ~/.ivy2) +# +#-ivy ~/.ivy2 + +# set memory options +# +#-mem + +# Use local caches for projects, no sharing. +# +#-no-share + +# Put SBT in offline mode. +# +#-offline + +# Sets the SBT version to use. +#-sbt-version 0.11.3 + +# Scala version (default: latest release) +# +#-scala-home +#-scala-version + +# java version (default: java from PATH, currently $(java -version |& grep version)) +# +#-java-home + diff --git a/scala-stream/src/main/scala/scalastream/J8SStream.scala b/scala-stream/src/main/scala/scalastream/J8SStream.scala new file mode 100644 index 0000000..ba509a5 --- /dev/null +++ b/scala-stream/src/main/scala/scalastream/J8SStream.scala @@ -0,0 +1,44 @@ +package scalastream + +import scalastream.App.{Config, Data} + +import scala.collection.immutable.ArraySeq +import scala.reflect.{ClassTag, classTag} + +class J8SStream[@specialized(Float, Double) A: Fractional: ClassTag](val config: Config[A]) + extends ScalaStream[A]: + + private var a: Array[A] = _ + private var b: Array[A] = _ + private var c: Array[A] = _ + private val scalar: A = config.scalar + + inline private def stream = + java.util.stream.IntStream.range(0, config.options.arraysize).parallel() + + override inline def initArrays(): Unit = + a = Array.ofDim(config.options.arraysize) + b = Array.ofDim(config.options.arraysize) + c = Array.ofDim(config.options.arraysize) + stream.forEach { i => + a(i) = config.init._1 + b(i) = config.init._2 + c(i) = config.init._3 + } + + override inline def copy(): Unit = stream.forEach(i => c(i) = a(i)) + override inline def mul(): Unit = stream.forEach(i => b(i) = scalar * c(i)) + override inline def add(): Unit = stream.forEach(i => c(i) = a(i) + b(i)) + override inline def triad(): Unit = stream.forEach(i => a(i) = b(i) + scalar * c(i)) + override inline def nstream(): Unit = stream.forEach(i => a(i) = b(i) * scalar * c(i)) + override inline def dot(): A = + // horrible special-case for double, there isn't a mapToFloat so we give up on that + val cls = classTag[A].runtimeClass + if java.lang.Double.TYPE == cls then + stream + .mapToDouble(i => (a(i) * b(i)).asInstanceOf[Double]) + .reduce(0, (l: Double, r: Double) => l + r) + .asInstanceOf[A] + else stream.mapToObj[A](i => a(i) * b(i)).reduce(0.fractional, (l: A, r: A) => l + r) + + override inline def data(): Data[A] = Data(a.to(ArraySeq), b.to(ArraySeq), c.to(ArraySeq)) diff --git a/scala-stream/src/main/scala/scalastream/ParStream.scala b/scala-stream/src/main/scala/scalastream/ParStream.scala new file mode 100644 index 0000000..bb146a2 --- /dev/null +++ b/scala-stream/src/main/scala/scalastream/ParStream.scala @@ -0,0 +1,36 @@ +package scalastream + +import scalastream.App.{Config, Data} + +import scala.collection.immutable.ArraySeq +import scala.collection.parallel.CollectionConverters._ +import scala.reflect.ClassTag +class ParStream[@specialized(Float, Double) A: Fractional: ClassTag](val config: Config[A]) + extends ScalaStream[A]: + + private var a: Array[A] = _ + private var b: Array[A] = _ + private var c: Array[A] = _ + private val scalar: A = config.scalar + + inline private def indices = (0 until config.options.arraysize).par + + override inline def initArrays(): Unit = + a = Array.ofDim(config.options.arraysize) + b = Array.ofDim(config.options.arraysize) + c = Array.ofDim(config.options.arraysize) + + for i <- indices do + a(i) = config.init._1 + b(i) = config.init._2 + c(i) = config.init._3 + + override inline def copy(): Unit = for i <- indices do c(i) = a(i) + override inline def mul(): Unit = for i <- indices do b(i) = scalar * c(i) + override inline def add(): Unit = for i <- indices do c(i) = a(i) + b(i) + override inline def triad(): Unit = for i <- indices do a(i) = b(i) + scalar * c(i) + override inline def nstream(): Unit = for i <- indices do a(i) = b(i) * scalar * c(i) + override inline def dot(): A = + indices.aggregate[A](0.fractional)((acc, i) => acc + (a(i) * b(i)), _ + _) + + override inline def data(): Data[A] = Data(a.to(ArraySeq), b.to(ArraySeq), c.to(ArraySeq)) diff --git a/scala-stream/src/main/scala/scalastream/PlainStream.scala b/scala-stream/src/main/scala/scalastream/PlainStream.scala new file mode 100644 index 0000000..2b42571 --- /dev/null +++ b/scala-stream/src/main/scala/scalastream/PlainStream.scala @@ -0,0 +1,31 @@ +package scalastream + +import scalastream.App.{Config, Data} + +import scala.collection.immutable.ArraySeq +import scala.reflect.ClassTag +class PlainStream[@specialized(Float, Double) A: Fractional: ClassTag](val config: Config[A]) + extends ScalaStream[A]: + + private var a: Array[A] = _ + private var b: Array[A] = _ + private var c: Array[A] = _ + private val scalar: A = config.scalar + + override inline def initArrays(): Unit = + a = Array.fill(config.options.arraysize)(config.init._1) + b = Array.fill(config.options.arraysize)(config.init._2) + c = Array.fill(config.options.arraysize)(config.init._3) + + private inline def indices = 0 until config.options.arraysize + + override inline def copy(): Unit = for i <- indices do c(i) = a(i) + override inline def mul(): Unit = for i <- indices do b(i) = scalar * c(i) + override inline def add(): Unit = for i <- indices do c(i) = a(i) + b(i) + override inline def triad(): Unit = for i <- indices do a(i) = b(i) + (scalar * c(i)) + override inline def nstream(): Unit = for i <- indices do a(i) = b(i) * scalar * c(i) + override inline def dot(): A = + var acc: A = 0.fractional + for i <- indices do acc = acc + (a(i) * b(i)) + acc + override inline def data(): Data[A] = Data(a.to(ArraySeq), b.to(ArraySeq), c.to(ArraySeq)) diff --git a/scala-stream/src/main/scala/scalastream/ScalaStream.scala b/scala-stream/src/main/scala/scalastream/ScalaStream.scala new file mode 100644 index 0000000..4ed90e4 --- /dev/null +++ b/scala-stream/src/main/scala/scalastream/ScalaStream.scala @@ -0,0 +1,369 @@ +package scalastream +import scalastream.App.{Config, Data, Timings} + +import java.util.concurrent.TimeUnit +import scala.collection.immutable.ArraySeq +import scala.collection.mutable.ArrayBuffer +import scala.concurrent.duration.{Duration, FiniteDuration, SECONDS} +import scala.math.{Pi, pow} +import scala.reflect.ClassTag +import scopt.OParser + +transparent trait ScalaStream[@specialized(Float, Double) A]: + + def config: Config[A] + + def initArrays(): Unit + def copy(): Unit + def mul(): Unit + def add(): Unit + def triad(): Unit + def nstream(): Unit + def dot(): A + + transparent inline def timed[R](f: => R): (FiniteDuration, R) = + val start = System.nanoTime() + val r = f + val end = System.nanoTime() + FiniteDuration(end - start, TimeUnit.NANOSECONDS) -> r + + inline def runAll(times: Int)(using Fractional[A]): (Timings[Vector[FiniteDuration]], A) = + val copy = ArrayBuffer.fill[FiniteDuration](times)(Duration.Zero) + val mul = ArrayBuffer.fill[FiniteDuration](times)(Duration.Zero) + val add = ArrayBuffer.fill[FiniteDuration](times)(Duration.Zero) + val triad = ArrayBuffer.fill[FiniteDuration](times)(Duration.Zero) + val dot = ArrayBuffer.fill[FiniteDuration](times)(Duration.Zero) + + var lastSum: A = 0.fractional + + for i <- 0 until times do + copy(i) = timed(this.copy())._1 + mul(i) = timed(this.mul())._1 + add(i) = timed(this.add())._1 + triad(i) = timed(this.triad())._1 + val (dot_, sum) = timed(this.dot()) + dot(i) = dot_ + lastSum = sum + val s = lastSum + + ( + Timings( + copy = copy.toVector, + mul = mul.toVector, + add = add.toVector, + triad = triad.toVector, + dot = dot.toVector + ), + s + ) + + def runTriad(times: Int): FiniteDuration = timed(for _ <- 0 until times do triad())._1 + def runNStream(times: Int): Vector[FiniteDuration] = Vector.fill(times)(timed(nstream())._1) + + def data(): Data[A] + + +trait Fractional[@specialized(Double, Float) A]: + def toFractional(f: Float): A + def toFractional(f: Double): A + def compare(x: A, y: A): Int + def add(x: A, y: A): A + def sub(x: A, y: A): A + def mul(x: A, y: A): A + def div(x: A, y: A): A + def abs(x: A): A + extension (x: Float) inline def fractional = toFractional(x) + extension (x: Double) inline def fractional = toFractional(x) + extension (x: Int) inline def fractional = toFractional(x.toFloat) + extension (x: Long) inline def fractional = toFractional(x.toDouble) + extension (x: A) + inline def +(y: A) = add(x, y) + inline def -(y: A) = sub(x, y) + inline def *(y: A) = mul(x, y) + inline def /(y: A) = div(x, y) + inline def >(y: A) = compare(x, y) > 0 + inline def <(y: A) = compare(x, y) < 0 + inline def abs_ = abs(x) +end Fractional + +given FloatFractional: Fractional[Float] with + inline def toFractional(f: Float): Float = f + inline def toFractional(f: Double): Float = f.toFloat + inline def compare(x: Float, y: Float): Int = x.compare(y) + inline def add(x: Float, y: Float): Float = x + y + inline def sub(x: Float, y: Float): Float = x - y + inline def mul(x: Float, y: Float): Float = x * y + inline def div(x: Float, y: Float): Float = x / y + inline def abs(x: Float): Float = math.abs(x) + +given DoubleFractional: Fractional[Double] with + inline def toFractional(f: Float): Double = f.toDouble + inline def toFractional(f: Double): Double = f + inline def compare(x: Double, y: Double): Int = x.compare(y) + inline def add(x: Double, y: Double): Double = x + y + inline def sub(x: Double, y: Double): Double = x - y + inline def mul(x: Double, y: Double): Double = x * y + inline def div(x: Double, y: Double): Double = x / y + inline def abs(x: Double): Double = math.abs(x) + +object App: + + final val Version: String = "3.4.0" + + case class Config[@specialized(Double, Float) A]( + options: Options, + benchmark: Benchmark, + typeSize: Int, + ulp: A, + scalar: A, + init: (A, A, A) + ) + + case class Timings[A](copy: A, mul: A, add: A, triad: A, dot: A) + case class Data[A](@specialized(Double, Float) a: ArraySeq[A], b: ArraySeq[A], c: ArraySeq[A]) + + case class Options( + list: Boolean = false, + device: Int = 0, + numtimes: Int = 100, + arraysize: Int = 33554432, + float: Boolean = false, + triad_only: Boolean = false, + nstream_only: Boolean = false, + csv: Boolean = false, + mibibytes: Boolean = false + ) + + object Options: + val Default = Options() + val builder = OParser.builder[Options] + val parser1 = + import builder._ + OParser.sequence( + programName("scala-stream"), + head("ScalaStream", s"$Version"), + opt[Unit]('l', "list").text("List available devices").action((_, x) => x.copy(list = true)), + opt[Int]('d', "device") + .text(s"Select device at , defaults to ${Default.device}") + .action((v, x) => x.copy(device = v)), + opt[Int]('n', "numtimes") + .text(s"Run the test times (NUM >= 2), defaults to ${Default.numtimes}") + .validate { + case n if n >= 2 => success + case n => failure(s"$n <= 2") + } + .action((n, x) => x.copy(numtimes = n)), + opt[Int]('a', "arraysize") + .text(s"Use elements in the array, defaults to ${Default.arraysize}") + .action((v, x) => x.copy(arraysize = v)), + opt[Unit]('f', "float") + .text("Use floats (rather than doubles)") + .action((_, x) => x.copy(float = true)), + opt[Unit]('t', "triad_only") + .text("Only run triad") + .action((_, x) => x.copy(triad_only = true)), + opt[Unit]('n', "nstream_only") + .text("Only run nstream") + .action((_, x) => x.copy(nstream_only = true)), + opt[Unit]('c', "csv").text("Output as csv table").action((_, x) => x.copy(csv = true)), + opt[Unit]('m', "mibibytes") + .text("Use MiB=2^20 for bandwidth calculation (default MB=10^6)") + .action((_, x) => x.copy(mibibytes = true)), + help('h', "help").text("prints this usage text") + ) + + enum Benchmark: + case All, NStream, Triad + + implicit class RichDuration(private val d: Duration) extends AnyVal: + def seconds: Double = d.toUnit(SECONDS) + + def validate[A: Fractional](vec: Data[A], config: Config[A], dotSum: Option[A] = None): Unit = + + var (goldA, goldB, goldC) = config.init + for _ <- 0 until config.options.numtimes do + config.benchmark match + case Benchmark.All => + goldC = goldA + goldB = config.scalar * goldC + goldC = goldA + goldB + goldA = goldB + config.scalar * goldC + case Benchmark.Triad => + goldA = goldB + config.scalar * goldC + case Benchmark.NStream => + goldA += goldB + config.scalar * goldC + + val tolerance = config.ulp * (100.fractional) + def validateXs(name: String, xs: Seq[A], from: A): Unit = + val error = xs.map(x => (x - from).abs_).fold(0.fractional)(_ + _) / xs.size.fractional + if error > tolerance then + Console.err.println(s"Validation failed on $name. Average error $error ") + + validateXs("a", vec.a, goldA) + validateXs("b", vec.b, goldB) + validateXs("c", vec.c, goldC) + + dotSum.foreach { sum => + val goldSum = (goldA * goldB) * (config.options.arraysize).fractional + val error = ((sum - goldSum) / goldSum).abs_ + if error > 1.fractional / 100000000.fractional then + Console.err.println( + s"Validation failed on sum. Error $error \nSum was $sum but should be $goldSum" + ) + } + + inline def run[A: Fractional: ClassTag]( + name: String, + config: Config[A], + mkStream: Config[A] => ScalaStream[A] + ): Unit = + + val opt = config.options + + val arrayBytes = opt.arraysize * config.typeSize + val totalBytes = arrayBytes * 3 + val (megaScale, megaSuffix, gigaScale, gigaSuffix) = + if !opt.mibibytes then (1.0e-6, "MB", 1.0e-9, "GB") + else (pow(2.0, -20), "MiB", pow(2.0, -30), "GiB") + + if !opt.csv then + + val vendor = System.getProperty("java.vendor") + val ver = System.getProperty("java.version") + val home = System.getProperty("java.home") + println( + s"""BabelStream + |Version: $Version + |Implementation: $name; Scala (Java $ver; $vendor; home=$home)""".stripMargin + ) + + println(s"Running ${config.benchmark match { + case Benchmark.All => "kernels" + case Benchmark.Triad => "triad" + case Benchmark.NStream => "nstream" + }} ${opt.numtimes} times") + + if config.benchmark == Benchmark.Triad then println(s"Number of elements: ${opt.arraysize}") + + println(s"Precision: ${if opt.float then "float" else "double"}") + println( + f"Array size: ${megaScale * arrayBytes}%.1f $megaSuffix (=${gigaScale * arrayBytes}%.1f $gigaSuffix)" + ) + println( + f"Total size: ${megaScale * totalBytes}%.1f $megaSuffix (=${gigaScale * totalBytes}%.1f $gigaSuffix)" + ) + + def mkRow(xs: Vector[FiniteDuration], name: String, totalBytes: Int) = + val tail = xs.tail + (tail.minOption.map(_.seconds), tail.maxOption.map(_.seconds)) match + case (Some(min), Some(max)) => + val avg = (tail.foldLeft(Duration.Zero)(_ + _) / tail.size.toDouble).seconds + val mbps = megaScale * totalBytes.toDouble / min + if opt.csv then + Vector( + "function" -> name, + "num_times" -> opt.numtimes.toString, + "n_elements" -> opt.arraysize.toString, + "sizeof" -> totalBytes.toString, + s"max_m${if opt.mibibytes then "i" else ""}bytes_per_sec" -> mbps.toString, + "min_runtime" -> min.toString, + "max_runtime" -> max.toString, + "avg_runtime" -> avg.toString + ) + else + Vector( + "Function" -> name, + s"M${if opt.mibibytes then "i" else ""}Bytes/sec" -> f"$mbps%.3f", + "Min (sec)" -> f"$min%.5f", + "Max" -> f"$max%.5f", + "Average" -> f"$avg%.5f" + ) + case (_, _) => sys.error(s"No min/max element for $name(size=$totalBytes)") + + def tabulate(rows: Vector[(String, String)]*): Unit = rows.toList match + case Nil => sys.error(s"Empty tabulation") + case header :: _ => + val padding = if opt.csv then 0 else 12 + val sep = if opt.csv then "," else "" + println(header.map(_._1.padTo(padding, ' ')).mkString(sep)) + println(rows.map(_.map(_._2.padTo(padding, ' ')).mkString(sep)).mkString("\n")) + + val stream = mkStream(config) + stream.initArrays() + config.benchmark match + case Benchmark.All => + val (results, sum) = stream.runAll(opt.numtimes) + validate(stream.data(), config, Some(sum)) + tabulate( + mkRow(results.copy, "Copy", 2 * arrayBytes), + mkRow(results.mul, "Mul", 2 * arrayBytes), + mkRow(results.add, "Add", 3 * arrayBytes), + mkRow(results.triad, "Triad", 3 * arrayBytes), + mkRow(results.dot, "Dot", 2 * arrayBytes) + ) + case Benchmark.NStream => + val result = stream.runNStream(opt.numtimes) + validate(stream.data(), config) + tabulate(mkRow(result, "Nstream", 4 * arrayBytes)) + case Benchmark.Triad => + val results = stream.runTriad(opt.numtimes) + val totalBytes = 3 * arrayBytes * opt.numtimes + val bandwidth = megaScale * (totalBytes / results.seconds) + println(f"Runtime (seconds): ${results.seconds}%.5f") + println(f"Bandwidth ($gigaSuffix/s): $bandwidth%.3f ") + + inline def devices[A: Fractional: ClassTag]: Vector[(String, Config[A] => ScalaStream[A])] = + Vector( + "Scala Parallel Collections" -> (ParStream(_)), + "Java 8 Stream" -> (J8SStream(_)), + "Threaded" -> (ThreadStream(_)), + "Serial" -> (PlainStream(_)) + ) + + inline def runWith[A: Fractional: ClassTag](i: Int, config: Config[A]): Unit = + devices[A].lift(i) match + case None => println(s"Device index out of bounds: $i") + case Some((name, mkStream)) => run(name, config, mkStream) + + def main(args: Array[String]): Unit = + + def handleOpt(opt: Options) = + val benchmark = (opt.nstream_only, opt.triad_only) match + case (true, false) => Benchmark.NStream + case (false, true) => Benchmark.Triad + case (false, false) => Benchmark.All + case (true, true) => + throw new RuntimeException( + "Both triad and nstream are enabled, pick one or omit both to run all benchmarks" + ) + + if opt.list then + devices[Float].zipWithIndex.foreach { case ((name, _), i) => println(s"$i: $name") } + else if opt.float then + runWith( + opt.device, + Config( + options = opt, + benchmark = benchmark, + typeSize = 4, // 32bit + ulp = math.ulp(Float.MaxValue), + scalar = 0.4f, + init = (0.1f, 0.2f, 0.0f) + ) + ) + else + runWith( + opt.device, + Config( + options = opt, + benchmark = benchmark, + typeSize = 8, + ulp = math.ulp(Double.MaxValue), + scalar = 0.4, // 64bit + init = (0.1, 0.2, 0.0) + ) + ) + + OParser.parse(Options.parser1, args, Options.Default) match + case Some(config) => handleOpt(config) + case _ => sys.exit(1) diff --git a/scala-stream/src/main/scala/scalastream/ThreadStream.scala b/scala-stream/src/main/scala/scalastream/ThreadStream.scala new file mode 100644 index 0000000..969a71f --- /dev/null +++ b/scala-stream/src/main/scala/scalastream/ThreadStream.scala @@ -0,0 +1,68 @@ +package scalastream + +import net.openhft.affinity.{AffinityStrategies, AffinityThreadFactory} +import scalastream.App.{Config, Data} + +import java.util.concurrent.{Callable, Executors} +import scala.collection.immutable.ArraySeq +import scala.reflect.ClassTag +object ThreadStream {} +class ThreadStream[@specialized(Float, Double) A: Fractional: ClassTag](val config: Config[A]) + extends ScalaStream[A]: + + private var a: Array[A] = _ + private var b: Array[A] = _ + private var c: Array[A] = _ + private val scalar: A = config.scalar + + private val chunks: Int = sys.runtime.availableProcessors() + + private val pool = Executors.newFixedThreadPool( + chunks, + new AffinityThreadFactory("scala-stream", true, AffinityStrategies.DIFFERENT_CORE) + ) + + private val indices = (0 until config.options.arraysize) + .grouped(config.options.arraysize / chunks) + .toSeq + + private inline def forEachAll[C](c: => C)(f: (C, Int) => Unit): Seq[C] = + import scala.jdk.CollectionConverters._ + val xs = pool + .invokeAll( + indices.map { r => + { () => + val ctx = c + r.foreach(f(ctx, _)) + ctx + }: Callable[C] + }.asJavaCollection + ) + .asScala + .map(_.get()) + .toSeq + xs + + override inline def initArrays(): Unit = + a = Array.ofDim(config.options.arraysize) + b = Array.ofDim(config.options.arraysize) + c = Array.ofDim(config.options.arraysize) + forEachAll(()) { (_, i) => + a(i) = config.init._1 + b(i) = config.init._2 + c(i) = config.init._3 + } + () + + class Box(var value: A) + override inline def copy(): Unit = { forEachAll(())((_, i) => c(i) = a(i)); () } + override inline def mul(): Unit = { forEachAll(())((_, i) => b(i) = scalar * c(i)); () } + override inline def add(): Unit = { forEachAll(())((_, i) => c(i) = a(i) + b(i)); () } + override inline def triad(): Unit = { forEachAll(())((_, i) => a(i) = b(i) + scalar * c(i)); () } + override inline def nstream(): Unit = { forEachAll(())((_, i) => a(i) = b(i) * scalar * c(i)); () } + + override inline def dot(): A = + forEachAll(Box(0.fractional))((acc, i) => acc.value = acc.value + (a(i) * b(i))) + .map(_.value) + .fold(0.fractional)(_ + _) + override inline def data(): Data[A] = Data(a.to(ArraySeq), b.to(ArraySeq), c.to(ArraySeq)) From 5318404249a4b1ac00059f2c08c784bfb28e2e02 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Wed, 26 May 2021 17:46:07 +0100 Subject: [PATCH 25/78] Use ./src instead of ./cpp Create subdir for each cpp-based implementation --- .github/workflows/main.yaml | 4 +- cpp/LICENSE => LICENSE | 0 README.md | 18 ++- cpp/.gitignore | 30 ---- {cpp/legacy => legacy}/HCStream.cpp | 0 {cpp/legacy => legacy}/HCStream.h | 0 cpp/legacy/HC.make => legacy/Makefile | 3 +- src/.gitignore | 29 ++++ {cpp => src}/CL/cl.h | 0 {cpp => src}/CL/cl2.hpp | 0 {cpp => src}/CL/cl_d3d10.h | 0 {cpp => src}/CL/cl_d3d11.h | 0 {cpp => src}/CL/cl_dx9_media_sharing.h | 0 {cpp => src}/CL/cl_dx9_media_sharing_intel.h | 0 {cpp => src}/CL/cl_egl.h | 0 {cpp => src}/CL/cl_ext.h | 0 {cpp => src}/CL/cl_ext_intel.h | 0 {cpp => src}/CL/cl_gl.h | 0 {cpp => src}/CL/cl_gl_ext.h | 0 {cpp => src}/CL/cl_half.h | 0 {cpp => src}/CL/cl_icd.h | 0 {cpp => src}/CL/cl_platform.h | 0 .../CL/cl_va_api_media_sharing_intel.h | 0 {cpp => src}/CL/cl_version.h | 0 {cpp => src}/CL/opencl.h | 0 {cpp => src}/CMakeLists.txt | 64 ++------ {cpp => src}/Stream.h | 0 {cpp => src/acc}/ACCStream.cpp | 0 {cpp => src/acc}/ACCStream.h | 0 cpp/OpenACC.make => src/acc/Makefile | 4 +- cpp/ACC.cmake => src/acc/model.cmake | 0 {cpp => src}/ci-prepare-bionic.sh | 0 {cpp => src}/ci-test-compile.sh | 150 +++++++++--------- .../Modules/ComputeCppCompilerChecks.cmake | 0 .../cmake/Modules/ComputeCppIRMap.cmake | 0 .../cmake/Modules/FindComputeCpp.cmake | 0 .../cmake/toolchains/arm-gcc-poky.cmake | 0 .../cmake/toolchains/gcc-generic.cmake | 0 {cpp => src/cuda}/CUDAStream.cu | 0 {cpp => src/cuda}/CUDAStream.h | 0 cpp/CUDA.make => src/cuda/Makefile | 4 +- cpp/CUDA.cmake => src/cuda/model.cmake | 0 {cpp => src/hip}/HIPStream.cpp | 0 {cpp => src/hip}/HIPStream.h | 0 cpp/HIP.make => src/hip/Makefile | 4 +- cpp/HIP.cmake => src/hip/model.cmake | 0 {cpp => src/kokkos}/KokkosStream.cpp | 0 {cpp => src/kokkos}/KokkosStream.hpp | 0 cpp/Kokkos.make => src/kokkos/Makefile | 13 +- cpp/KOKKOS.cmake => src/kokkos/model.cmake | 0 {cpp => src}/main.cpp | 0 cpp/OpenCL.make => src/ocl/Makefile | 4 +- {cpp => src/ocl}/OCLStream.cpp | 0 {cpp => src/ocl}/OCLStream.h | 0 cpp/OCL.cmake => src/ocl/model.cmake | 0 cpp/OpenMP.make => src/omp/Makefile | 4 +- {cpp => src/omp}/OMPStream.cpp | 0 {cpp => src/omp}/OMPStream.h | 0 cpp/OMP.cmake => src/omp/model.cmake | 0 cpp/RAJA.make => src/raja/Makefile | 4 +- {cpp => src/raja}/RAJAStream.cpp | 0 {cpp => src/raja}/RAJAStream.hpp | 0 cpp/RAJA.cmake => src/raja/model.cmake | 0 {cpp => src}/register_models.cmake | 12 +- cpp/STD.make => src/std/Makefile | 4 +- {cpp => src/std}/STDStream.cpp | 0 {cpp => src/std}/STDStream.h | 0 cpp/STD.cmake => src/std/model.cmake | 0 cpp/STD20.make => src/std20/Makefile | 4 +- {cpp => src/std20}/STD20Stream.cpp | 0 {cpp => src/std20}/STD20Stream.hpp | 0 cpp/STD20.cmake => src/std20/model.cmake | 0 cpp/SYCL.make => src/sycl/Makefile | 4 +- {cpp => src/sycl}/SYCLStream.cpp | 0 {cpp => src/sycl}/SYCLStream.h | 0 cpp/SYCL.cmake => src/sycl/model.cmake | 0 76 files changed, 163 insertions(+), 196 deletions(-) rename cpp/LICENSE => LICENSE (100%) delete mode 100644 cpp/.gitignore rename {cpp/legacy => legacy}/HCStream.cpp (100%) rename {cpp/legacy => legacy}/HCStream.h (100%) rename cpp/legacy/HC.make => legacy/Makefile (79%) create mode 100644 src/.gitignore rename {cpp => src}/CL/cl.h (100%) rename {cpp => src}/CL/cl2.hpp (100%) rename {cpp => src}/CL/cl_d3d10.h (100%) rename {cpp => src}/CL/cl_d3d11.h (100%) rename {cpp => src}/CL/cl_dx9_media_sharing.h (100%) rename {cpp => src}/CL/cl_dx9_media_sharing_intel.h (100%) rename {cpp => src}/CL/cl_egl.h (100%) rename {cpp => src}/CL/cl_ext.h (100%) rename {cpp => src}/CL/cl_ext_intel.h (100%) rename {cpp => src}/CL/cl_gl.h (100%) rename {cpp => src}/CL/cl_gl_ext.h (100%) rename {cpp => src}/CL/cl_half.h (100%) rename {cpp => src}/CL/cl_icd.h (100%) rename {cpp => src}/CL/cl_platform.h (100%) rename {cpp => src}/CL/cl_va_api_media_sharing_intel.h (100%) rename {cpp => src}/CL/cl_version.h (100%) rename {cpp => src}/CL/opencl.h (100%) rename {cpp => src}/CMakeLists.txt (76%) rename {cpp => src}/Stream.h (100%) rename {cpp => src/acc}/ACCStream.cpp (100%) rename {cpp => src/acc}/ACCStream.h (100%) rename cpp/OpenACC.make => src/acc/Makefile (96%) rename cpp/ACC.cmake => src/acc/model.cmake (100%) rename {cpp => src}/ci-prepare-bionic.sh (100%) rename {cpp => src}/ci-test-compile.sh (61%) rename {cpp => src}/cmake/Modules/ComputeCppCompilerChecks.cmake (100%) rename {cpp => src}/cmake/Modules/ComputeCppIRMap.cmake (100%) rename {cpp => src}/cmake/Modules/FindComputeCpp.cmake (100%) rename {cpp => src}/cmake/toolchains/arm-gcc-poky.cmake (100%) rename {cpp => src}/cmake/toolchains/gcc-generic.cmake (100%) rename {cpp => src/cuda}/CUDAStream.cu (100%) rename {cpp => src/cuda}/CUDAStream.h (100%) rename cpp/CUDA.make => src/cuda/Makefile (88%) rename cpp/CUDA.cmake => src/cuda/model.cmake (100%) rename {cpp => src/hip}/HIPStream.cpp (100%) rename {cpp => src/hip}/HIPStream.h (100%) rename cpp/HIP.make => src/hip/Makefile (76%) rename cpp/HIP.cmake => src/hip/model.cmake (100%) rename {cpp => src/kokkos}/KokkosStream.cpp (100%) rename {cpp => src/kokkos}/KokkosStream.hpp (100%) rename cpp/Kokkos.make => src/kokkos/Makefile (85%) rename cpp/KOKKOS.cmake => src/kokkos/model.cmake (100%) rename {cpp => src}/main.cpp (100%) rename cpp/OpenCL.make => src/ocl/Makefile (85%) rename {cpp => src/ocl}/OCLStream.cpp (100%) rename {cpp => src/ocl}/OCLStream.h (100%) rename cpp/OCL.cmake => src/ocl/model.cmake (100%) rename cpp/OpenMP.make => src/omp/Makefile (95%) rename {cpp => src/omp}/OMPStream.cpp (100%) rename {cpp => src/omp}/OMPStream.h (100%) rename cpp/OMP.cmake => src/omp/model.cmake (100%) rename cpp/RAJA.make => src/raja/Makefile (92%) rename {cpp => src/raja}/RAJAStream.cpp (100%) rename {cpp => src/raja}/RAJAStream.hpp (100%) rename cpp/RAJA.cmake => src/raja/model.cmake (100%) rename {cpp => src}/register_models.cmake (93%) rename cpp/STD.make => src/std/Makefile (72%) rename {cpp => src/std}/STDStream.cpp (100%) rename {cpp => src/std}/STDStream.h (100%) rename cpp/STD.cmake => src/std/model.cmake (100%) rename cpp/STD20.make => src/std20/Makefile (76%) rename {cpp => src/std20}/STD20Stream.cpp (100%) rename {cpp => src/std20}/STD20Stream.hpp (100%) rename cpp/STD20.cmake => src/std20/model.cmake (100%) rename cpp/SYCL.make => src/sycl/Makefile (96%) rename {cpp => src/sycl}/SYCLStream.cpp (100%) rename {cpp => src/sycl}/SYCLStream.h (100%) rename cpp/SYCL.cmake => src/sycl/model.cmake (100%) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 43a036f..5bed7b8 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-18.04 defaults: run: - working-directory: ./cpp + working-directory: ./src steps: - uses: actions/checkout@v2 @@ -16,7 +16,7 @@ jobs: uses: actions/cache@v2 with: path: compilers - key: ${{ runner.os }}-${{ hashFiles('./cpp/ci-prepare-bionic.sh') }} + key: ${{ runner.os }}-${{ hashFiles('./src/ci-prepare-bionic.sh') }} - name: Prepare compilers if: steps.prepare-compilers.outputs.cache-hit != 'true' diff --git a/cpp/LICENSE b/LICENSE similarity index 100% rename from cpp/LICENSE rename to LICENSE diff --git a/README.md b/README.md index 66ea661..3eaf9a5 100644 --- a/README.md +++ b/README.md @@ -63,20 +63,22 @@ The project supports building with CMake >= 3.13.0, it can be installed without As with any CMake project, first configure the project: ```shell -> cd babelstream/cpp +> cd babelstream/src > cmake -Bbuild -H. -DMODEL= # configure the build, build type defaults to Release > cmake --build build # compile it -> ./build/babelstream # executable available at ./build/ +> ./build/-stream # executable available at ./build/ ``` +Source for each model's implementations are located in `./src/`. + By default, we have defined a set of optimal flags for known HPC compilers. There are assigned those to `RELEASE_FLAGS`, and you can override them if required. To find out what flag each model supports or requires, simply configure while only specifying the model. For example: ```shell -> cd babelstream/cpp -> cmake -Bbuild -H. -DMODEL=OCL +> cd babelstream/src +> cmake -Bbuild -H. -DMODEL=ocl ... - Common Release flags are `-O3`, set RELEASE_FLAGS to override -- CXX_EXTRA_FLAGS: @@ -90,8 +92,8 @@ For example: Use this for linking extra libraries (e.g `-lmylib`, or simply `mylib`) -- CXX_EXTRA_LINKER_FLAGS: Append to linker flags (i.e GCC's `-Wl` or equivalent) --- Available models: OMP;OCL;STD;STD20;HIP;CUDA;KOKKOS;SYCL;ACC;RAJA --- Selected model : OCL +-- Available models: omp;ocl;std;std20;hip;cuda;kokkos;sycl;acc;raja +-- Selected model : ocl -- Supported flags: CMAKE_CXX_COMPILER (optional, default=c++): Any CXX compiler that is supported by CMake detection @@ -107,7 +109,7 @@ Alternatively, refer to the [CI script](./ci-test-compile.sh), which test-compil We have supplied a series of Makefiles, one for each programming model, to assist with building. The Makefiles contain common build options, and should be simple to customise for your needs too. -General usage is `make -f .make` +General usage is `make -C src/` Common compiler flags and names can be set by passing a `COMPILER` option to Make, e.g. `make COMPILER=GNU`. Some models allow specifying a CPU or GPU style target, and this can be set by passing a `TARGET` option to Make, e.g. `make TARGET=GPU`. @@ -125,7 +127,7 @@ cd wget https://github.com/kokkos/kokkos/archive/3.1.01.tar.gz tar -xvf 3.1.01.tar.gz # should end up with ~/kokkos-3.1.01 cd BabelStream -make -f Kokkos.make KOKKOS_PATH=~/kokkos-3.1.01 +make -C src/kokkos KOKKOS_PATH=~/kokkos-3.1.01 ``` See make output for more information on supported flags. diff --git a/cpp/.gitignore b/cpp/.gitignore deleted file mode 100644 index c3ea1da..0000000 --- a/cpp/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ - -cuda-stream -ocl-stream -omp-stream -acc-stream -raja-stream -kokkos-stream -std-stream -sycl-stream -hip-stream - -*.o -*.bc -*.sycl -*.tar -*.gz -*.a - -KokkosCore_config.* - -.DS_Store - -Makefile - -build/ -cmake-build-*/ -CMakeFiles/ -.idea/ -.vscode/ -.directory \ No newline at end of file diff --git a/cpp/legacy/HCStream.cpp b/legacy/HCStream.cpp similarity index 100% rename from cpp/legacy/HCStream.cpp rename to legacy/HCStream.cpp diff --git a/cpp/legacy/HCStream.h b/legacy/HCStream.h similarity index 100% rename from cpp/legacy/HCStream.h rename to legacy/HCStream.h diff --git a/cpp/legacy/HC.make b/legacy/Makefile similarity index 79% rename from cpp/legacy/HC.make rename to legacy/Makefile index b902ada..8047ae1 100644 --- a/cpp/legacy/HC.make +++ b/legacy/Makefile @@ -14,7 +14,8 @@ endif hc-stream: ../main.cpp HCStream.cpp - $(HCC) $(CXXFLAGS) -DHC $^ $(LDFLAGS) $(EXTRA_FLAGS) -o $@ + $(HCC) $(CXXFLAGS) -DHC $^ $(LDFLAGS) $(EXTRA_FLAGS) -o $@ -I. -I.. + .PHONY: clean clean: diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..568a953 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,29 @@ + +**/cuda-stream +**/ocl-stream +**/omp-stream +**/acc-stream +**/raja-stream +**/kokkos-stream +**/std-stream +**/sycl-stream +**/hip-stream + +**/*.o +**/*.bc +**/*.sycl +**/*.tar +**/*.gz +**/*.a + +**/KokkosCore_Config_* + +**/.DS_Store + + +build/ +cmake-build-*/ +CMakeFiles/ +.idea/ +.vscode/ +.directory \ No newline at end of file diff --git a/cpp/CL/cl.h b/src/CL/cl.h similarity index 100% rename from cpp/CL/cl.h rename to src/CL/cl.h diff --git a/cpp/CL/cl2.hpp b/src/CL/cl2.hpp similarity index 100% rename from cpp/CL/cl2.hpp rename to src/CL/cl2.hpp diff --git a/cpp/CL/cl_d3d10.h b/src/CL/cl_d3d10.h similarity index 100% rename from cpp/CL/cl_d3d10.h rename to src/CL/cl_d3d10.h diff --git a/cpp/CL/cl_d3d11.h b/src/CL/cl_d3d11.h similarity index 100% rename from cpp/CL/cl_d3d11.h rename to src/CL/cl_d3d11.h diff --git a/cpp/CL/cl_dx9_media_sharing.h b/src/CL/cl_dx9_media_sharing.h similarity index 100% rename from cpp/CL/cl_dx9_media_sharing.h rename to src/CL/cl_dx9_media_sharing.h diff --git a/cpp/CL/cl_dx9_media_sharing_intel.h b/src/CL/cl_dx9_media_sharing_intel.h similarity index 100% rename from cpp/CL/cl_dx9_media_sharing_intel.h rename to src/CL/cl_dx9_media_sharing_intel.h diff --git a/cpp/CL/cl_egl.h b/src/CL/cl_egl.h similarity index 100% rename from cpp/CL/cl_egl.h rename to src/CL/cl_egl.h diff --git a/cpp/CL/cl_ext.h b/src/CL/cl_ext.h similarity index 100% rename from cpp/CL/cl_ext.h rename to src/CL/cl_ext.h diff --git a/cpp/CL/cl_ext_intel.h b/src/CL/cl_ext_intel.h similarity index 100% rename from cpp/CL/cl_ext_intel.h rename to src/CL/cl_ext_intel.h diff --git a/cpp/CL/cl_gl.h b/src/CL/cl_gl.h similarity index 100% rename from cpp/CL/cl_gl.h rename to src/CL/cl_gl.h diff --git a/cpp/CL/cl_gl_ext.h b/src/CL/cl_gl_ext.h similarity index 100% rename from cpp/CL/cl_gl_ext.h rename to src/CL/cl_gl_ext.h diff --git a/cpp/CL/cl_half.h b/src/CL/cl_half.h similarity index 100% rename from cpp/CL/cl_half.h rename to src/CL/cl_half.h diff --git a/cpp/CL/cl_icd.h b/src/CL/cl_icd.h similarity index 100% rename from cpp/CL/cl_icd.h rename to src/CL/cl_icd.h diff --git a/cpp/CL/cl_platform.h b/src/CL/cl_platform.h similarity index 100% rename from cpp/CL/cl_platform.h rename to src/CL/cl_platform.h diff --git a/cpp/CL/cl_va_api_media_sharing_intel.h b/src/CL/cl_va_api_media_sharing_intel.h similarity index 100% rename from cpp/CL/cl_va_api_media_sharing_intel.h rename to src/CL/cl_va_api_media_sharing_intel.h diff --git a/cpp/CL/cl_version.h b/src/CL/cl_version.h similarity index 100% rename from cpp/CL/cl_version.h rename to src/CL/cl_version.h diff --git a/cpp/CL/opencl.h b/src/CL/opencl.h similarity index 100% rename from cpp/CL/opencl.h rename to src/CL/opencl.h diff --git a/cpp/CMakeLists.txt b/src/CMakeLists.txt similarity index 76% rename from cpp/CMakeLists.txt rename to src/CMakeLists.txt index d4a11cd..e38130f 100644 --- a/cpp/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,46 +8,7 @@ project(BabelStream VERSION 3.5 LANGUAGES CXX) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD_REQUIRED ON) - -#set(MODEL SYCL) -#set(SYCL_COMPILER COMPUTECPP) -#set(SYCL_COMPILER_DIR /home/tom/Desktop/computecpp_archive/ComputeCpp-CE-2.3.0-x86_64-linux-gnu) -#set(MODEL RAJA) -#set(RAJA_IN_TREE /home/tom/Downloads/RAJA-v0.13.0/) -#set(ENABLE_CUDA ON) -#set(TARGET NVIDIA) -#set(CUDA_TOOLKIT_ROOT_DIR /opt/cuda-11.2) -#set(CUDA_ARCH sm_70) -#set(BLT_DIR /home/tom/Downloads/blt-0.3.6/) - -#set(MODEL STD) -#set(ARCH cc70) -#set(CXX_EXTRA_FLAGS -v) - -#set(MODEL CUDA) -#set(ARCH sm_70) -#set(CMAKE_CUDA_COMPILER /opt/cuda-11.2/bin/nvcc) - -#set(MODEL OCL) -#set(OpenCL_LIBRARY /opt/rocm-4.0.0/opencl/lib/libOpenCL.so) -#set(OpenCL_INCLUDE_DIR /opt/rocm-4.0.0/opencl/lib) -#set(RELEASE_FLAGS -Ofast) -#set(CXX_EXTRA_FLAGS -O2) - -#set(CMAKE_CXX_COMPILER /usr/lib/aomp/bin/clang++) -#set(MODEL OMP) -##set(OFFLOAD "AMD:gfx803") -#set(OFFLOAD "NVIDIA:sm_35") -#set(CXX_EXTRA_FLAGS --cuda-path=/opt/cuda-10.2/) - -#set(OFFLOAD "AMD:_70") -#set(CXX_EXTRA_FLAGS --cuda-path=/opt/cuda-10.2/ --gcc-toolchain=/home/tom/spack/opt/spack/linux-fedora33-zen2/gcc-10.2.1/gcc-8.3.0-latmjo2hl2yv53255xkwko7k3y7bx2vv) -#set(CXX_EXTRA_LINKER_FLAGS ) -#set(MODEL HIP) - -#set(MODEL KOKKOS) -#set(KOKKOS_IN_TREE /home/tom/Downloads/kokkos-3.3.00/) - + # the final executable name set(EXE_NAME babelstream) @@ -101,17 +62,17 @@ endif () include(register_models.cmake) # register out models -register_model(OMP OMP OMPStream.cpp) -register_model(OCL OCL OCLStream.cpp) -register_model(STD STD STDStream.cpp) -register_model(STD20 STD20 STD20Stream.cpp) -register_model(HIP HIP HIPStream.cpp) -register_model(CUDA CUDA CUDAStream.cu) -register_model(KOKKOS KOKKOS KokkosStream.cpp) -register_model(SYCL SYCL SYCLStream.cpp) -register_model(ACC ACC ACCStream.cpp) +register_model(omp OMP OMPStream.cpp) +register_model(ocl OCL OCLStream.cpp) +register_model(std STD STDStream.cpp) +register_model(std20 STD20 STD20Stream.cpp) +register_model(hip HIP HIPStream.cpp) +register_model(cuda CUDA CUDAStream.cu) +register_model(kokkos KOKKOS KokkosStream.cpp) +register_model(sycl SYCL SYCLStream.cpp) +register_model(acc ACC ACCStream.cpp) # defining RAJA collides with the RAJA namespace so USE_RAJA -register_model(RAJA USE_RAJA RAJAStream.cpp) +register_model(raja USE_RAJA RAJAStream.cpp) set(USAGE ON CACHE BOOL "Whether to print all custom flags for the selected model") @@ -169,6 +130,7 @@ message(STATUS "Executable : ${EXE_NAME}") # below we have all the usual CMake target setup steps +include_directories(.) add_executable(${EXE_NAME} ${IMPL_SOURCES} main.cpp) target_link_libraries(${EXE_NAME} PUBLIC ${LINK_LIBRARIES}) target_compile_definitions(${EXE_NAME} PUBLIC ${IMPL_DEFINITIONS}) @@ -184,7 +146,7 @@ target_link_options(${EXE_NAME} PUBLIC LINKER:${CXX_EXTRA_LINKER_FLAGS}) target_link_options(${EXE_NAME} PUBLIC ${LINK_FLAGS} ${CXX_EXTRA_LINK_FLAGS}) # some models require the target to be already specified so they can finish their setup here -# this only happens if the MODEL.cmake definition contains the `setup_target` macro +# this only happens if the model.cmake definition contains the `setup_target` macro if (COMMAND setup_target) setup_target(${EXE_NAME}) endif () diff --git a/cpp/Stream.h b/src/Stream.h similarity index 100% rename from cpp/Stream.h rename to src/Stream.h diff --git a/cpp/ACCStream.cpp b/src/acc/ACCStream.cpp similarity index 100% rename from cpp/ACCStream.cpp rename to src/acc/ACCStream.cpp diff --git a/cpp/ACCStream.h b/src/acc/ACCStream.h similarity index 100% rename from cpp/ACCStream.h rename to src/acc/ACCStream.h diff --git a/cpp/OpenACC.make b/src/acc/Makefile similarity index 96% rename from cpp/OpenACC.make rename to src/acc/Makefile index 7a75fd0..55fbfbd 100644 --- a/cpp/OpenACC.make +++ b/src/acc/Makefile @@ -50,8 +50,8 @@ endif FLAGS_GNU = -O3 -std=c++11 -Drestrict=__restrict -fopenacc CXXFLAGS = $(FLAGS_$(COMPILER)) -acc-stream: main.cpp ACCStream.cpp - $(COMPILER_$(COMPILER)) $(CXXFLAGS) -DACC $^ $(EXTRA_FLAGS) -o $@ +acc-stream: ../main.cpp ACCStream.cpp + $(COMPILER_$(COMPILER)) $(CXXFLAGS) -DACC $^ $(EXTRA_FLAGS) -o $@ -I. -I.. .PHONY: clean clean: diff --git a/cpp/ACC.cmake b/src/acc/model.cmake similarity index 100% rename from cpp/ACC.cmake rename to src/acc/model.cmake diff --git a/cpp/ci-prepare-bionic.sh b/src/ci-prepare-bionic.sh similarity index 100% rename from cpp/ci-prepare-bionic.sh rename to src/ci-prepare-bionic.sh diff --git a/cpp/ci-test-compile.sh b/src/ci-test-compile.sh similarity index 61% rename from cpp/ci-test-compile.sh rename to src/ci-test-compile.sh index 1b5c1bb..85efd39 100755 --- a/cpp/ci-test-compile.sh +++ b/src/ci-test-compile.sh @@ -86,38 +86,38 @@ run_build() { } ### -#KOKKOS_SRC="/home/tom/Downloads/kokkos-3.3.00" -#RAJA_SRC="/home/tom/Downloads/RAJA-v0.13.0" -# -#GCC_CXX="/usr/bin/g++" -#CLANG_CXX="/usr/bin/clang++" -# -#NVSDK="/home/tom/Downloads/nvhpc_2021_212_Linux_x86_64_cuda_11.2/install_components/Linux_x86_64/21.2/" -#NVHPC_NVCXX="$NVSDK/compilers/bin/nvc++" -#NVHPC_NVCC="$NVSDK/cuda/11.2/bin/nvcc" -#NVHPC_CUDA_DIR="$NVSDK/cuda/11.2" -#"$NVSDK/compilers/bin/makelocalrc" "$NVSDK/compilers/bin/" -x -# -#AOCC_CXX="/opt/AMD/aocc-compiler-2.3.0/bin/clang++" -#AOMP_CXX="/usr/lib/aomp/bin/clang++" -#OCL_LIB="/home/tom/Downloads/oclcpuexp-2020.11.11.0.04_rel/x64/libOpenCL.so" -# -## AMD needs this rocm_path thing exported... -#export ROCM_PATH="/opt/rocm-4.0.0" -#HIP_CXX="/opt/rocm-4.0.0/bin/hipcc" -#COMPUTECPP_DIR="/home/tom/Desktop/computecpp_archive/ComputeCpp-CE-2.3.0-x86_64-linux-gnu" -#DPCPP_DIR="/home/tom/Downloads/dpcpp_compiler" -#HIPSYCL_DIR="/opt/hipsycl/cff515c/" -# -#ICPX_CXX="/opt/intel/oneapi/compiler/2021.1.2/linux/bin/icpx" -#ICPC_CXX="/opt/intel/oneapi/compiler/2021.1.2/linux/bin/intel64/icpc" -# -#GCC_STD_PAR_LIB="tbb" -#CLANG_STD_PAR_LIB="tbb" -#GCC_OMP_OFFLOAD_AMD=false -#GCC_OMP_OFFLOAD_NVIDIA=true -#CLANG_OMP_OFFLOAD_AMD=false -#CLANG_OMP_OFFLOAD_NVIDIA=false +# KOKKOS_SRC="/home/tom/Downloads/kokkos-3.3.00" +# RAJA_SRC="/home/tom/Downloads/RAJA-v0.13.0" + +# GCC_CXX="/usr/bin/g++" +# CLANG_CXX="/usr/bin/clang++" + +# NVSDK="/home/tom/Downloads/nvhpc_2021_212_Linux_x86_64_cuda_11.2/install_components/Linux_x86_64/21.2/" +# NVHPC_NVCXX="$NVSDK/compilers/bin/nvc++" +# NVHPC_NVCC="$NVSDK/cuda/11.2/bin/nvcc" +# NVHPC_CUDA_DIR="$NVSDK/cuda/11.2" +# "$NVSDK/compilers/bin/makelocalrc" "$NVSDK/compilers/bin/" -x + +# AOCC_CXX="/opt/AMD/aocc-compiler-2.3.0/bin/clang++" +# AOMP_CXX="/usr/lib/aomp/bin/clang++" +# OCL_LIB="/home/tom/Downloads/oclcpuexp-2020.11.11.0.04_rel/x64/libOpenCL.so" + +# # AMD needs this rocm_path thing exported... +# export ROCM_PATH="/opt/rocm-4.0.0" +# HIP_CXX="/opt/rocm-4.0.0/bin/hipcc" +# COMPUTECPP_DIR="/home/tom/Desktop/computecpp_archive/ComputeCpp-CE-2.3.0-x86_64-linux-gnu" +# DPCPP_DIR="/home/tom/Downloads/dpcpp_compiler" +# HIPSYCL_DIR="/opt/hipsycl/cff515c/" + +# ICPX_CXX="/opt/intel/oneapi/compiler/2021.1.2/linux/bin/icpx" +# ICPC_CXX="/opt/intel/oneapi/compiler/2021.1.2/linux/bin/intel64/icpc" + +# GCC_STD_PAR_LIB="tbb" +# CLANG_STD_PAR_LIB="tbb" +# GCC_OMP_OFFLOAD_AMD=false +# GCC_OMP_OFFLOAD_NVIDIA=true +# CLANG_OMP_OFFLOAD_AMD=false +# CLANG_OMP_OFFLOAD_NVIDIA=false ### AMD_ARCH="gfx_903" @@ -128,39 +128,39 @@ build_gcc() { local name="gcc_build" local cxx="-DCMAKE_CXX_COMPILER=${GCC_CXX:?}" - run_build $name "${GCC_CXX:?}" OMP "$cxx" + run_build $name "${GCC_CXX:?}" omp "$cxx" if [ "$MODEL" = "all" ] || [ "$MODEL" = "OMP" ]; then # sanity check that it at least runs - echo "Sanity checking GCC OMP build..." - "./$BUILD_DIR/OMP_$name/omp-stream" -s 1048576 -n 10 + echo "Sanity checking GCC omp build..." + "./$BUILD_DIR/omp_$name/omp-stream" -s 1048576 -n 10 fi # some distributions like Ubuntu bionic implements std par with TBB, so conditionally link it here - run_build $name "${GCC_CXX:?}" STD "$cxx -DCXX_EXTRA_LIBRARIES=${GCC_STD_PAR_LIB:-}" - run_build $name "${GCC_CXX:?}" STD20 "$cxx -DCXX_EXTRA_LIBRARIES=${GCC_STD_PAR_LIB:-}" + run_build $name "${GCC_CXX:?}" std "$cxx -DCXX_EXTRA_LIBRARIES=${GCC_STD_PAR_LIB:-}" + run_build $name "${GCC_CXX:?}" std20 "$cxx -DCXX_EXTRA_LIBRARIES=${GCC_STD_PAR_LIB:-}" if [ "${GCC_OMP_OFFLOAD_AMD:-false}" != "false" ]; then - run_build "amd_$name" "${GCC_CXX:?}" ACC "$cxx -DCXX_EXTRA_FLAGS=-foffload=amdgcn-amdhsa" - run_build "amd_$name" "${GCC_CXX:?}" OMP "$cxx -DOFFLOAD=AMD:$AMD_ARCH" + run_build "amd_$name" "${GCC_CXX:?}" acc "$cxx -DCXX_EXTRA_FLAGS=-foffload=amdgcn-amdhsa" + run_build "amd_$name" "${GCC_CXX:?}" omp "$cxx -DOFFLOAD=AMD:$AMD_ARCH" fi if [ "${GCC_OMP_OFFLOAD_NVIDIA:-false}" != "false" ]; then - run_build "nvidia_$name" "${GCC_CXX:?}" ACC "$cxx -DCXX_EXTRA_FLAGS=-foffload=nvptx-none" - run_build "nvidia_$name" "${GCC_CXX:?}" OMP "$cxx -DOFFLOAD=NVIDIA:$NV_ARCH" + run_build "nvidia_$name" "${GCC_CXX:?}" acc "$cxx -DCXX_EXTRA_FLAGS=-foffload=nvptx-none" + run_build "nvidia_$name" "${GCC_CXX:?}" omp "$cxx -DOFFLOAD=NVIDIA:$NV_ARCH" fi - run_build $name "${GCC_CXX:?}" CUDA "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH" - run_build $name "${GCC_CXX:?}" CUDA "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DMEM=MANAGED" - run_build $name "${GCC_CXX:?}" CUDA "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DMEM=PAGEFAULT" - # run_build $name "${CC_CXX:?}" KOKKOS "$cxx -DKOKKOS_IN_TREE=${KOKKOS_SRC:?} -DKokkos_ENABLE_CUDA=ON" - run_build "cuda_$name" "${GCC_CXX:?}" KOKKOS "$cxx -DKOKKOS_IN_TREE=${KOKKOS_SRC:?} -DKokkos_ENABLE_OPENMP=ON" - run_build $name "${GCC_CXX:?}" OCL "$cxx -DOpenCL_LIBRARY=${OCL_LIB:?}" - run_build $name "${GCC_CXX:?}" RAJA "$cxx -DRAJA_IN_TREE=${RAJA_SRC:?}" + run_build $name "${GCC_CXX:?}" cuda "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH" + run_build $name "${GCC_CXX:?}" cuda "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DMEM=MANAGED" + run_build $name "${GCC_CXX:?}" cuda "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DMEM=PAGEFAULT" + # run_build $name "${CC_CXX:?}" kokkos "$cxx -DKOKKOS_IN_TREE=${KOKKOS_SRC:?} -DKokkos_ENABLE_CUDA=ON" + run_build "cuda_$name" "${GCC_CXX:?}" kokkos "$cxx -DKOKKOS_IN_TREE=${KOKKOS_SRC:?} -DKokkos_ENABLE_OPENMP=ON" + run_build $name "${GCC_CXX:?}" ocl "$cxx -DOpenCL_LIBRARY=${OCL_LIB:?}" + run_build $name "${GCC_CXX:?}" raja "$cxx -DRAJA_IN_TREE=${RAJA_SRC:?}" # FIXME fails due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100102 # FIXME we also got https://github.com/NVIDIA/nccl/issues/494 -# run_build "cuda_$name" "${GCC_CXX:?}" RAJA "$cxx -DRAJA_IN_TREE=${RAJA_SRC:?} \ +# run_build "cuda_$name" "${GCC_CXX:?}" raja "$cxx -DRAJA_IN_TREE=${RAJA_SRC:?} \ # -DENABLE_CUDA=ON \ # -DTARGET=NVIDIA \ # -DCUDA_TOOLKIT_ROOT_DIR=${NVHPC_CUDA_DIR:?} \ @@ -171,46 +171,46 @@ build_gcc() { build_clang() { local name="clang_build" local cxx="-DCMAKE_CXX_COMPILER=${CLANG_CXX:?}" - run_build $name "${CLANG_CXX:?}" OMP "$cxx" + run_build $name "${CLANG_CXX:?}" omp "$cxx" if [ "${CLANG_OMP_OFFLOAD_AMD:-false}" != "false" ]; then - run_build "amd_$name" "${GCC_CXX:?}" OMP "$cxx -DOFFLOAD=AMD:$AMD_ARCH" + run_build "amd_$name" "${GCC_CXX:?}" omp "$cxx -DOFFLOAD=AMD:$AMD_ARCH" fi if [ "${CLANG_OMP_OFFLOAD_NVIDIA:-false}" != "false" ]; then - run_build "nvidia_$name" "${GCC_CXX:?}" OMP "$cxx -DOFFLOAD=NVIDIA:$NV_ARCH" + run_build "nvidia_$name" "${GCC_CXX:?}" omp "$cxx -DOFFLOAD=NVIDIA:$NV_ARCH" fi - run_build $name "${CLANG_CXX:?}" CUDA "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH" - run_build $name "${CLANG_CXX:?}" CUDA "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DMEM=MANAGED" - run_build $name "${CLANG_CXX:?}" CUDA "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DMEM=PAGEFAULT" - run_build $name "${CLANG_CXX:?}" KOKKOS "$cxx -DKOKKOS_IN_TREE=${KOKKOS_SRC:?} -DKokkos_ENABLE_OPENMP=ON" - run_build $name "${CLANG_CXX:?}" OCL "$cxx -DOpenCL_LIBRARY=${OCL_LIB:?}" - run_build $name "${CLANG_CXX:?}" STD "$cxx -DCXX_EXTRA_LIBRARIES=${CLANG_STD_PAR_LIB:-}" - # run_build $name "${LANG_CXX:?}" STD20 "$cxx -DCXX_EXTRA_LIBRARIES=${CLANG_STD_PAR_LIB:-}" # not yet supported - run_build $name "${CLANG_CXX:?}" RAJA "$cxx -DRAJA_IN_TREE=${RAJA_SRC:?}" + run_build $name "${CLANG_CXX:?}" cuda "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH" + run_build $name "${CLANG_CXX:?}" cuda "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DMEM=MANAGED" + run_build $name "${CLANG_CXX:?}" cuda "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DMEM=PAGEFAULT" + run_build $name "${CLANG_CXX:?}" kokkos "$cxx -DKOKKOS_IN_TREE=${KOKKOS_SRC:?} -DKokkos_ENABLE_OPENMP=ON" + run_build $name "${CLANG_CXX:?}" ocl "$cxx -DOpenCL_LIBRARY=${OCL_LIB:?}" + run_build $name "${CLANG_CXX:?}" std "$cxx -DCXX_EXTRA_LIBRARIES=${CLANG_STD_PAR_LIB:-}" + # run_build $name "${LANG_CXX:?}" std20 "$cxx -DCXX_EXTRA_LIBRARIES=${CLANG_STD_PAR_LIB:-}" # not yet supported + run_build $name "${CLANG_CXX:?}" raja "$cxx -DRAJA_IN_TREE=${RAJA_SRC:?}" # no clang /w RAJA+cuda because it needs nvcc which needs gcc } build_nvhpc() { local name="nvhpc_build" local cxx="-DCMAKE_CXX_COMPILER=${NVHPC_NVCXX:?}" - run_build $name "${NVHPC_NVCXX:?}" STD "$cxx -DNVHPC_OFFLOAD=$NV_ARCH_CCXY" - run_build $name "${NVHPC_NVCXX:?}" ACC "$cxx -DTARGET_DEVICE=gpu -DTARGET_PROCESSOR=px -DCUDA_ARCH=$NV_ARCH_CCXY" - run_build $name "${NVHPC_NVCXX:?}" ACC "$cxx -DTARGET_DEVICE=multicore -DTARGET_PROCESSOR=zen" + run_build $name "${NVHPC_NVCXX:?}" std "$cxx -DNVHPC_OFFLOAD=$NV_ARCH_CCXY" + run_build $name "${NVHPC_NVCXX:?}" acc "$cxx -DTARGET_DEVICE=gpu -DTARGET_PROCESSOR=px -DCUDA_ARCH=$NV_ARCH_CCXY" + run_build $name "${NVHPC_NVCXX:?}" acc "$cxx -DTARGET_DEVICE=multicore -DTARGET_PROCESSOR=zen" } build_aocc() { - run_build aocc_build "${AOCC_CXX:?}" OMP "-DCMAKE_CXX_COMPILER=${AOCC_CXX:?}" + run_build aocc_build "${AOCC_CXX:?}" omp "-DCMAKE_CXX_COMPILER=${AOCC_CXX:?}" } build_aomp() { - run_build aomp_amd_build "${AOMP_CXX:?}" OMP "-DCMAKE_CXX_COMPILER=${AOMP_CXX:?} -DOFFLOAD=AMD:gfx906" + run_build aomp_amd_build "${AOMP_CXX:?}" omp "-DCMAKE_CXX_COMPILER=${AOMP_CXX:?} -DOFFLOAD=AMD:gfx906" #run_build aomp_nvidia_build "-DCMAKE_CXX_COMPILER=${AOMP_CXX:?} -DOFFLOAD=NVIDIA:$NV_ARCH" } build_hip() { - run_build hip_build "${HIP_CXX:?}" HIP "-DCMAKE_CXX_COMPILER=${HIP_CXX:?}" + run_build hip_build "${HIP_CXX:?}" hip "-DCMAKE_CXX_COMPILER=${HIP_CXX:?}" } build_icpx() { @@ -218,7 +218,7 @@ build_icpx() { set +u source /opt/intel/oneapi/setvars.sh -force || true set -u - run_build intel_build "${ICPX_CXX:?}" OMP "-DCMAKE_CXX_COMPILER=${ICPX_CXX:?} -DOFFLOAD=INTEL" + run_build intel_build "${ICPX_CXX:?}" omp "-DCMAKE_CXX_COMPILER=${ICPX_CXX:?} -DOFFLOAD=INTEL" } build_icpc() { @@ -228,31 +228,31 @@ build_icpc() { set -u local name="intel_build" local cxx="-DCMAKE_CXX_COMPILER=${ICPC_CXX:?}" - run_build $name "${ICPC_CXX:?}" OMP "$cxx" - run_build $name "${ICPC_CXX:?}" OCL "$cxx -DOpenCL_LIBRARY=${OCL_LIB:?}" - run_build $name "${ICPC_CXX:?}" RAJA "$cxx -DRAJA_IN_TREE=${RAJA_SRC:?}" - run_build $name "${ICPC_CXX:?}" KOKKOS "$cxx -DKOKKOS_IN_TREE=${KOKKOS_SRC:?} -DKokkos_ENABLE_OPENMP=ON" + run_build $name "${ICPC_CXX:?}" omp "$cxx" + run_build $name "${ICPC_CXX:?}" ocl "$cxx -DOpenCL_LIBRARY=${OCL_LIB:?}" + run_build $name "${ICPC_CXX:?}" raja "$cxx -DRAJA_IN_TREE=${RAJA_SRC:?}" + run_build $name "${ICPC_CXX:?}" kokkos "$cxx -DKOKKOS_IN_TREE=${KOKKOS_SRC:?} -DKokkos_ENABLE_OPENMP=ON" } build_computecpp() { - run_build computecpp_build "compute++" SYCL "-DCMAKE_CXX_COMPILER=${GCC_CXX:?} \ + run_build computecpp_build "compute++" sycl "-DCMAKE_CXX_COMPILER=${GCC_CXX:?} \ -DSYCL_COMPILER=COMPUTECPP \ -DSYCL_COMPILER_DIR=${COMPUTECPP_DIR:?} \ -DOpenCL_LIBRARY=${OCL_LIB:?}" } build_dpcpp() { - run_build intel_build "${DPCPP_DIR:?}" SYCL "-DCMAKE_CXX_COMPILER=${GCC_CXX:?} \ + run_build intel_build "${DPCPP_DIR:?}" sycl "-DCMAKE_CXX_COMPILER=${GCC_CXX:?} \ -DSYCL_COMPILER=DPCPP \ -DSYCL_COMPILER_DIR=${DPCPP_DIR:?}" # for oneAPI BaseKit: # source /opt/intel/oneapi/setvars.sh -force - # run_build intel_build "dpcpp" SYCL "-DCMAKE_CXX_COMPILER=${GCC_CXX:?} -DSYCL_COMPILER=ONEAPI-DPCPP" + # run_build intel_build "dpcpp" sycl "-DCMAKE_CXX_COMPILER=${GCC_CXX:?} -DSYCL_COMPILER=ONEAPI-DPCPP" } build_hipsycl() { - run_build hipsycl_build "syclcc" SYCL " + run_build hipsycl_build "syclcc" sycl " -DSYCL_COMPILER=HIPSYCL \ -DSYCL_COMPILER_DIR=${HIPSYCL_DIR:?}" } diff --git a/cpp/cmake/Modules/ComputeCppCompilerChecks.cmake b/src/cmake/Modules/ComputeCppCompilerChecks.cmake similarity index 100% rename from cpp/cmake/Modules/ComputeCppCompilerChecks.cmake rename to src/cmake/Modules/ComputeCppCompilerChecks.cmake diff --git a/cpp/cmake/Modules/ComputeCppIRMap.cmake b/src/cmake/Modules/ComputeCppIRMap.cmake similarity index 100% rename from cpp/cmake/Modules/ComputeCppIRMap.cmake rename to src/cmake/Modules/ComputeCppIRMap.cmake diff --git a/cpp/cmake/Modules/FindComputeCpp.cmake b/src/cmake/Modules/FindComputeCpp.cmake similarity index 100% rename from cpp/cmake/Modules/FindComputeCpp.cmake rename to src/cmake/Modules/FindComputeCpp.cmake diff --git a/cpp/cmake/toolchains/arm-gcc-poky.cmake b/src/cmake/toolchains/arm-gcc-poky.cmake similarity index 100% rename from cpp/cmake/toolchains/arm-gcc-poky.cmake rename to src/cmake/toolchains/arm-gcc-poky.cmake diff --git a/cpp/cmake/toolchains/gcc-generic.cmake b/src/cmake/toolchains/gcc-generic.cmake similarity index 100% rename from cpp/cmake/toolchains/gcc-generic.cmake rename to src/cmake/toolchains/gcc-generic.cmake diff --git a/cpp/CUDAStream.cu b/src/cuda/CUDAStream.cu similarity index 100% rename from cpp/CUDAStream.cu rename to src/cuda/CUDAStream.cu diff --git a/cpp/CUDAStream.h b/src/cuda/CUDAStream.h similarity index 100% rename from cpp/CUDAStream.h rename to src/cuda/CUDAStream.h diff --git a/cpp/CUDA.make b/src/cuda/Makefile similarity index 88% rename from cpp/CUDA.make rename to src/cuda/Makefile index 90aa77c..153f07d 100644 --- a/cpp/CUDA.make +++ b/src/cuda/Makefile @@ -31,8 +31,8 @@ MEM_PAGEFAULT= -DPAGEFAULT MEM_MODE = $(MEM_$(MEM)) -cuda-stream: main.cpp CUDAStream.cu - $(CUDA_CXX) -std=c++11 $(CXXFLAGS) -arch=$(NVARCH) $(MEM_MODE) -DCUDA $^ $(EXTRA_FLAGS) -o $@ +cuda-stream: ../main.cpp CUDAStream.cu + $(CUDA_CXX) -std=c++11 $(CXXFLAGS) -arch=$(NVARCH) $(MEM_MODE) -DCUDA $^ $(EXTRA_FLAGS) -o $@ -I. -I.. .PHONY: clean clean: diff --git a/cpp/CUDA.cmake b/src/cuda/model.cmake similarity index 100% rename from cpp/CUDA.cmake rename to src/cuda/model.cmake diff --git a/cpp/HIPStream.cpp b/src/hip/HIPStream.cpp similarity index 100% rename from cpp/HIPStream.cpp rename to src/hip/HIPStream.cpp diff --git a/cpp/HIPStream.h b/src/hip/HIPStream.h similarity index 100% rename from cpp/HIPStream.h rename to src/hip/HIPStream.h diff --git a/cpp/HIP.make b/src/hip/Makefile similarity index 76% rename from cpp/HIP.make rename to src/hip/Makefile index 7a1196f..21383b0 100644 --- a/cpp/HIP.make +++ b/src/hip/Makefile @@ -2,8 +2,8 @@ HIP_PATH?= /opt/rocm/hip HIPCC=$(HIP_PATH)/bin/hipcc -hip-stream: main.cpp HIPStream.cpp - $(HIPCC) $(CXXFLAGS) -O3 -std=c++11 -DHIP $^ $(EXTRA_FLAGS) -o $@ +hip-stream: ../main.cpp HIPStream.cpp + $(HIPCC) $(CXXFLAGS) -O3 -std=c++11 -DHIP $^ $(EXTRA_FLAGS) -o $@ -I. -I.. .PHONY: clean clean: diff --git a/cpp/HIP.cmake b/src/hip/model.cmake similarity index 100% rename from cpp/HIP.cmake rename to src/hip/model.cmake diff --git a/cpp/KokkosStream.cpp b/src/kokkos/KokkosStream.cpp similarity index 100% rename from cpp/KokkosStream.cpp rename to src/kokkos/KokkosStream.cpp diff --git a/cpp/KokkosStream.hpp b/src/kokkos/KokkosStream.hpp similarity index 100% rename from cpp/KokkosStream.hpp rename to src/kokkos/KokkosStream.hpp diff --git a/cpp/Kokkos.make b/src/kokkos/Makefile similarity index 85% rename from cpp/Kokkos.make rename to src/kokkos/Makefile index 7dd6af8..98d8597 100644 --- a/cpp/Kokkos.make +++ b/src/kokkos/Makefile @@ -71,7 +71,7 @@ CXX = $(NVCC_WRAPPER) endif endif -OBJ = main.o KokkosStream.o +OBJ = KokkosStream.o CXXFLAGS = -O3 LINKFLAGS = # empty for now @@ -85,14 +85,15 @@ endif endif include $(KOKKOS_PATH)/Makefile.kokkos +HEADERS = $(wildcard $(MAKEFILE_PATH)*.hpp) -kokkos-stream: $(OBJ) $(KOKKOS_LINK_DEPENDS) - $(CXX) $(KOKKOS_LDFLAGS) $(LINKFLAGS) $(EXTRA_PATH) $(OBJ) $(KOKKOS_LIBS) $(LIB) -DKOKKOS -o $@ +kokkos-stream: ../main.cpp $(OBJ) $(KOKKOS_LINK_DEPENDS) + $(CXX) $(KOKKOS_LDFLAGS) $(LINKFLAGS) $(EXTRA_PATH) $(OBJ) $(KOKKOS_LIBS) $(LIB) -DKOKKOS -o $@ -I. -I.. -%.o: %.cpp $(KOKKOS_CPP_DEPENDS) - $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) $(EXTRA_INC) -DKOKKOS -c $< +%.o: %.cpp $(KOKKOS_CPP_DEPENDS) $(HEADERS) + $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) $(EXTRA_INC) -c $< -o $(notdir $@) .PHONY: clean clean: - rm -f kokkos-stream main.o KokkosStream.o Kokkos_*.o + rm -f kokkos-stream main.o KokkosStream.o Kokkos_*.o KokkosCore_* diff --git a/cpp/KOKKOS.cmake b/src/kokkos/model.cmake similarity index 100% rename from cpp/KOKKOS.cmake rename to src/kokkos/model.cmake diff --git a/cpp/main.cpp b/src/main.cpp similarity index 100% rename from cpp/main.cpp rename to src/main.cpp diff --git a/cpp/OpenCL.make b/src/ocl/Makefile similarity index 85% rename from cpp/OpenCL.make rename to src/ocl/Makefile index 8ad7108..20cd257 100644 --- a/cpp/OpenCL.make +++ b/src/ocl/Makefile @@ -30,8 +30,8 @@ else LIBS = -lOpenCL endif -ocl-stream: main.cpp OCLStream.cpp - $(CXX) $(CXXFLAGS) -DOCL $^ $(EXTRA_FLAGS) $(LIBS) -o $@ +ocl-stream: ../main.cpp OCLStream.cpp + $(CXX) $(CXXFLAGS) -DOCL $^ $(EXTRA_FLAGS) $(LIBS) -o $@ -I. -I.. .PHONY: clean clean: diff --git a/cpp/OCLStream.cpp b/src/ocl/OCLStream.cpp similarity index 100% rename from cpp/OCLStream.cpp rename to src/ocl/OCLStream.cpp diff --git a/cpp/OCLStream.h b/src/ocl/OCLStream.h similarity index 100% rename from cpp/OCLStream.h rename to src/ocl/OCLStream.h diff --git a/cpp/OCL.cmake b/src/ocl/model.cmake similarity index 100% rename from cpp/OCL.cmake rename to src/ocl/model.cmake diff --git a/cpp/OpenMP.make b/src/omp/Makefile similarity index 95% rename from cpp/OpenMP.make rename to src/omp/Makefile index dde3f75..2d8545c 100644 --- a/cpp/OpenMP.make +++ b/src/omp/Makefile @@ -91,8 +91,8 @@ endif OMP = $(OMP_$(COMPILER)_$(TARGET)) -omp-stream: main.cpp OMPStream.cpp - $(CXX) $(CXXFLAGS) -DOMP $^ $(OMP) $(EXTRA_FLAGS) -o $@ +omp-stream: ../main.cpp OMPStream.cpp + $(CXX) $(CXXFLAGS) -DOMP $^ $(OMP) $(EXTRA_FLAGS) -o $@ -I. -I.. .PHONY: clean clean: diff --git a/cpp/OMPStream.cpp b/src/omp/OMPStream.cpp similarity index 100% rename from cpp/OMPStream.cpp rename to src/omp/OMPStream.cpp diff --git a/cpp/OMPStream.h b/src/omp/OMPStream.h similarity index 100% rename from cpp/OMPStream.h rename to src/omp/OMPStream.h diff --git a/cpp/OMP.cmake b/src/omp/model.cmake similarity index 100% rename from cpp/OMP.cmake rename to src/omp/model.cmake diff --git a/cpp/RAJA.make b/src/raja/Makefile similarity index 92% rename from cpp/RAJA.make rename to src/raja/Makefile index 47aeefb..60f2319 100644 --- a/cpp/RAJA.make +++ b/src/raja/Makefile @@ -49,8 +49,8 @@ endif CXXFLAGS = --expt-extended-lambda -O3 -std=c++11 -x cu -Xcompiler -fopenmp -arch $(ARCH) endif -raja-stream: main.cpp RAJAStream.cpp - $(CXX) $(CXXFLAGS) -DUSE_RAJA -I$(RAJA_PATH)/include $^ $(EXTRA_FLAGS) -L$(RAJA_PATH)/lib -lRAJA -o $@ +raja-stream: ../main.cpp RAJAStream.cpp + $(CXX) $(CXXFLAGS) -DUSE_RAJA -I$(RAJA_PATH)/include $^ $(EXTRA_FLAGS) -L$(RAJA_PATH)/lib -lRAJA -o $@ -I. -I.. .PHONY: clean clean: diff --git a/cpp/RAJAStream.cpp b/src/raja/RAJAStream.cpp similarity index 100% rename from cpp/RAJAStream.cpp rename to src/raja/RAJAStream.cpp diff --git a/cpp/RAJAStream.hpp b/src/raja/RAJAStream.hpp similarity index 100% rename from cpp/RAJAStream.hpp rename to src/raja/RAJAStream.hpp diff --git a/cpp/RAJA.cmake b/src/raja/model.cmake similarity index 100% rename from cpp/RAJA.cmake rename to src/raja/model.cmake diff --git a/cpp/register_models.cmake b/src/register_models.cmake similarity index 93% rename from cpp/register_models.cmake rename to src/register_models.cmake index 82e7243..247612a 100644 --- a/cpp/register_models.cmake +++ b/src/register_models.cmake @@ -118,22 +118,24 @@ endfunction() macro(register_model NAME PREPROCESSOR_NAME) - string(TOUPPER ${NAME} MODEL_UPPER) list(APPEND REGISTERED_MODELS "${NAME}") - list(APPEND IMPL_${MODEL_UPPER}_SOURCES "${ARGN}") + string(TOUPPER ${NAME} MODEL_UPPER) + list(APPEND IMPL_${MODEL_UPPER}_SOURCES "${NAME}/${ARGN}") list(APPEND IMPL_${MODEL_UPPER}_DEFINITIONS "${PREPROCESSOR_NAME}") endmacro() macro(load_model MODEL) - string(TOUPPER "${MODEL}" MODEL_UPPER) - if ("${MODEL_UPPER}" IN_LIST REGISTERED_MODELS) - set(MODEL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${MODEL_UPPER}.cmake) + if ("${MODEL}" IN_LIST REGISTERED_MODELS) + string(TOLOWER "${MODEL}" MODEL_LOWER) + set(MODEL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${MODEL_LOWER}/model.cmake) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/${MODEL_LOWER}) if (NOT EXISTS ${MODEL_FILE}) message(FATAL_ERROR "${MODEL_FILE} not found, perhaps it needs to be implemented?") endif () include(${MODEL_FILE}) + string(TOUPPER "${MODEL}" MODEL_UPPER) list(APPEND IMPL_SOURCES ${IMPL_${MODEL_UPPER}_SOURCES}) list(APPEND IMPL_DEFINITIONS ${IMPL_${MODEL_UPPER}_DEFINITIONS}) diff --git a/cpp/STD.make b/src/std/Makefile similarity index 72% rename from cpp/STD.make rename to src/std/Makefile index 3225a08..a5a8847 100644 --- a/cpp/STD.make +++ b/src/std/Makefile @@ -6,8 +6,8 @@ CXXFLAGS=-O3 -std=c++17 -stdpar -DSTD STD_CXX=nvc++ -std-stream: main.cpp STDStream.cpp - $(STD_CXX) $(CXXFLAGS) $^ $(EXTRA_FLAGS) -o $@ +std-stream: ../main.cpp STDStream.cpp + $(STD_CXX) $(CXXFLAGS) $^ $(EXTRA_FLAGS) -o $@ -I. -I.. .PHONY: clean clean: diff --git a/cpp/STDStream.cpp b/src/std/STDStream.cpp similarity index 100% rename from cpp/STDStream.cpp rename to src/std/STDStream.cpp diff --git a/cpp/STDStream.h b/src/std/STDStream.h similarity index 100% rename from cpp/STDStream.h rename to src/std/STDStream.h diff --git a/cpp/STD.cmake b/src/std/model.cmake similarity index 100% rename from cpp/STD.cmake rename to src/std/model.cmake diff --git a/cpp/STD20.make b/src/std20/Makefile similarity index 76% rename from cpp/STD20.make rename to src/std20/Makefile index eced9f7..3a93bcb 100644 --- a/cpp/STD20.make +++ b/src/std20/Makefile @@ -17,8 +17,8 @@ FLAGS_GNU = -O3 -std=c++2a -march=native CXXFLAGS = $(FLAGS_$(COMPILER)) -std20-stream: main.cpp STD20Stream.cpp - $(CXX) -DSTD20 $(CXXFLAGS) $^ $(EXTRA_FLAGS) -o $@ +std20-stream: ../main.cpp STD20Stream.cpp + $(CXX) -DSTD20 $(CXXFLAGS) $^ $(EXTRA_FLAGS) -o $@ -I. -I.. .PHONY: clean clean: diff --git a/cpp/STD20Stream.cpp b/src/std20/STD20Stream.cpp similarity index 100% rename from cpp/STD20Stream.cpp rename to src/std20/STD20Stream.cpp diff --git a/cpp/STD20Stream.hpp b/src/std20/STD20Stream.hpp similarity index 100% rename from cpp/STD20Stream.hpp rename to src/std20/STD20Stream.hpp diff --git a/cpp/STD20.cmake b/src/std20/model.cmake similarity index 100% rename from cpp/STD20.cmake rename to src/std20/model.cmake diff --git a/cpp/SYCL.make b/src/sycl/Makefile similarity index 96% rename from cpp/SYCL.make rename to src/sycl/Makefile index 58df8d0..05d2022 100644 --- a/cpp/SYCL.make +++ b/src/sycl/Makefile @@ -73,8 +73,8 @@ SYCL_LINK_FLAGS = $(SYCL_$(COMPILER)_LINK_FLAGS) SYCL_INCLUDE = $(SYCL_$(COMPILER)_INCLUDE) # only ComputeCpp generates .sycl files which is a bit odd to deal with so we opted to compile everything together -sycl-stream: main.cpp SYCLStream.cpp - $(SYCL_SYCLCXX) $(SYCL_SYCLFLAGS) $(SYCL_FLAGS) $(SYCL_INCLUDE) -DSYCL $(EXTRA_FLAGS) $(SYCL_LINK_FLAGS) $^ -o $@ +sycl-stream: ../main.cpp SYCLStream.cpp + $(SYCL_SYCLCXX) $(SYCL_SYCLFLAGS) $(SYCL_FLAGS) $(SYCL_INCLUDE) -DSYCL $(EXTRA_FLAGS) $(SYCL_LINK_FLAGS) $^ -o $@ -I. -I.. .PHONY: clean clean: diff --git a/cpp/SYCLStream.cpp b/src/sycl/SYCLStream.cpp similarity index 100% rename from cpp/SYCLStream.cpp rename to src/sycl/SYCLStream.cpp diff --git a/cpp/SYCLStream.h b/src/sycl/SYCLStream.h similarity index 100% rename from cpp/SYCLStream.h rename to src/sycl/SYCLStream.h diff --git a/cpp/SYCL.cmake b/src/sycl/model.cmake similarity index 100% rename from cpp/SYCL.cmake rename to src/sycl/model.cmake From 4d00a8699e1889b01e4194f5deb311462b2f0454 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 27 May 2021 09:41:41 +0100 Subject: [PATCH 26/78] Don't point to the CL dir for SYCL --- src/sycl/model.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sycl/model.cmake b/src/sycl/model.cmake index c35f435..c0c6c3f 100644 --- a/src/sycl/model.cmake +++ b/src/sycl/model.cmake @@ -48,7 +48,7 @@ macro(setup) set(ComputeCpp_DIR ${SYCL_COMPILER_DIR}) # don't point to the CL dir as the imports already have the CL prefix - set(OpenCL_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/CL") + set(OpenCL_INCLUDE_DIR "${CMAKE_SOURCE_DIR}") register_definitions(CL_TARGET_OPENCL_VERSION=220 _GLIBCXX_USE_CXX11_ABI=0) # ComputeCpp needs OpenCL From d3b676cb37771740c57d974404df42aa213cad93 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 27 May 2021 10:47:46 +0100 Subject: [PATCH 27/78] Include CL_MEM_CHANNEL_INTEL directly to avoid header precedence issues --- src/sycl/SYCLStream.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sycl/SYCLStream.h b/src/sycl/SYCLStream.h index f312009..dd13387 100644 --- a/src/sycl/SYCLStream.h +++ b/src/sycl/SYCLStream.h @@ -11,6 +11,15 @@ #include "Stream.h" +#include "CL/opencl.h" + +// XXX Intel's SYCL impl. needs CL_MEM_CHANNEL_INTEL which is provided in dpcpp's include dir +// however, depending the system configuration, the system CL header sometimes takes precedence +// we only really need this macro to refer to the extension so this is probably OK +#ifndef CL_MEM_CHANNEL_INTEL +#define CL_MEM_CHANNEL_INTEL 0x4213 +#endif + #include "CL/sycl.hpp" #define IMPLEMENTATION_STRING "SYCL" From 5d9e408a06b330f550b7359526d90e7b85127477 Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Fri, 4 Jun 2021 16:42:49 +0000 Subject: [PATCH 28/78] [SYCL 2020] Make array size a size_t --- SYCLStream.cpp | 2 +- SYCLStream.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SYCLStream.cpp b/SYCLStream.cpp index 9e23175..76c0a8b 100644 --- a/SYCLStream.cpp +++ b/SYCLStream.cpp @@ -15,7 +15,7 @@ std::vector devices; void getDeviceList(void); template -SYCLStream::SYCLStream(const int ARRAY_SIZE, const int device_index) +SYCLStream::SYCLStream(const size_t ARRAY_SIZE, const int device_index) : array_size {ARRAY_SIZE}, d_a {ARRAY_SIZE}, d_b {ARRAY_SIZE}, diff --git a/SYCLStream.h b/SYCLStream.h index cd8e39a..e8b9134 100644 --- a/SYCLStream.h +++ b/SYCLStream.h @@ -21,7 +21,7 @@ class SYCLStream : public Stream { protected: // Size of arrays - int array_size; + size_t array_size; // SYCL objects // Queue is a pointer because we allow device selection @@ -35,7 +35,7 @@ class SYCLStream : public Stream public: - SYCLStream(const int, const int); + SYCLStream(const size_t, const int); ~SYCLStream() = default; virtual void copy() override; From b3efa6af67cc7fed7cbba3b0e2dbc50f6010f99d Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 10 Jun 2021 04:20:40 +0100 Subject: [PATCH 29/78] Initial Julia implementation --- .github/workflows/main.yaml | 17 + JuliaStream.jl/.JuliaFormatter.toml | 2 + JuliaStream.jl/.gitignore | 5 + JuliaStream.jl/Manifest.toml | 411 ++++++++++++++++++++++++ JuliaStream.jl/Project.toml | 14 + JuliaStream.jl/README.md | 30 ++ JuliaStream.jl/src/AMDGPUStream.jl | 178 ++++++++++ JuliaStream.jl/src/CUDAStream.jl | 146 +++++++++ JuliaStream.jl/src/DistributedStream.jl | 84 +++++ JuliaStream.jl/src/JuliaStream.jl | 4 + JuliaStream.jl/src/PlainStream.jl | 64 ++++ JuliaStream.jl/src/Stream.jl | 292 +++++++++++++++++ JuliaStream.jl/src/StreamData.jl | 8 + JuliaStream.jl/src/ThreadedStream.jl | 72 +++++ 14 files changed, 1327 insertions(+) create mode 100644 JuliaStream.jl/.JuliaFormatter.toml create mode 100644 JuliaStream.jl/.gitignore create mode 100644 JuliaStream.jl/Manifest.toml create mode 100644 JuliaStream.jl/Project.toml create mode 100644 JuliaStream.jl/README.md create mode 100644 JuliaStream.jl/src/AMDGPUStream.jl create mode 100644 JuliaStream.jl/src/CUDAStream.jl create mode 100644 JuliaStream.jl/src/DistributedStream.jl create mode 100644 JuliaStream.jl/src/JuliaStream.jl create mode 100644 JuliaStream.jl/src/PlainStream.jl create mode 100644 JuliaStream.jl/src/Stream.jl create mode 100644 JuliaStream.jl/src/StreamData.jl create mode 100644 JuliaStream.jl/src/ThreadedStream.jl diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 20e1034..83bcd9d 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -3,6 +3,23 @@ on: [push, pull_request] jobs: + test-julia: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Setup project + run: julia --project -e 'import Pkg; Pkg.instantiate()' + - name: Test run PlainStream.jl + run: julia --project src/PlainStream.jl --arraysize 100 + - name: Test run ThreadedStream.jl + run: julia --threads 2 --project src/ThreadedStream.jl --arraysize 100 + - name: Test run DistributedStream.jl + run: julia -p2 --project src/DistributedStream.jl --arraysize 100 + - name: Test run CUDAStream.jl + run: julia --project src/CUDAStream.jl --list + - name: Test run AMDGPUStream.jl + run: julia --project src/AMDGPUStream.jl --list + test: runs-on: ubuntu-18.04 steps: diff --git a/JuliaStream.jl/.JuliaFormatter.toml b/JuliaStream.jl/.JuliaFormatter.toml new file mode 100644 index 0000000..ac95ddd --- /dev/null +++ b/JuliaStream.jl/.JuliaFormatter.toml @@ -0,0 +1,2 @@ +indent = 2 +margin = 100 \ No newline at end of file diff --git a/JuliaStream.jl/.gitignore b/JuliaStream.jl/.gitignore new file mode 100644 index 0000000..12b143b --- /dev/null +++ b/JuliaStream.jl/.gitignore @@ -0,0 +1,5 @@ +*.jl.cov +*.jl.*.cov +*.jl.mem +/docs/build/ +/docs/Manifest.toml \ No newline at end of file diff --git a/JuliaStream.jl/Manifest.toml b/JuliaStream.jl/Manifest.toml new file mode 100644 index 0000000..c60d77f --- /dev/null +++ b/JuliaStream.jl/Manifest.toml @@ -0,0 +1,411 @@ +# This file is machine-generated - editing it directly is not advised + +[[AMDGPU]] +deps = ["AbstractFFTs", "Adapt", "BinaryProvider", "CEnum", "GPUArrays", "GPUCompiler", "LLVM", "Libdl", "LinearAlgebra", "MacroTools", "Printf", "Random", "Requires", "Setfield", "hsa_rocr_jll", "hsakmt_roct_jll"] +git-tree-sha1 = "04fdb3923ac6f55fa7347dce0f0f6f10e321e2e9" +uuid = "21141c5a-9bdb-4563-92ae-f87d6854732e" +version = "0.2.7" + +[[AbstractFFTs]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "485ee0867925449198280d4af84bdb46a2a404d0" +uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" +version = "1.0.1" + +[[Adapt]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "84918055d15b3114ede17ac6a7182f68870c16f7" +uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +version = "3.3.1" + +[[ArgParse]] +deps = ["Logging", "TextWrap"] +git-tree-sha1 = "3102bce13da501c9104df33549f511cd25264d7d" +uuid = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" +version = "1.1.4" + +[[ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" + +[[Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[BFloat16s]] +deps = ["LinearAlgebra", "Test"] +git-tree-sha1 = "4af69e205efc343068dc8722b8dfec1ade89254a" +uuid = "ab4f0b2a-ad5b-11e8-123f-65d77653426b" +version = "0.1.0" + +[[Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[BinaryProvider]] +deps = ["Libdl", "Logging", "SHA"] +git-tree-sha1 = "ecdec412a9abc8db54c0efc5548c64dfce072058" +uuid = "b99e7846-7c00-51b0-8f62-c81ae34c0232" +version = "0.5.10" + +[[Bzip2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c3598e525718abcc440f69cc6d5f60dda0a1b61e" +uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" +version = "1.0.6+5" + +[[CEnum]] +git-tree-sha1 = "215a9aa4a1f23fbd05b92769fdd62559488d70e9" +uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82" +version = "0.4.1" + +[[CUDA]] +deps = ["AbstractFFTs", "Adapt", "BFloat16s", "CEnum", "CompilerSupportLibraries_jll", "DataStructures", "ExprTools", "GPUArrays", "GPUCompiler", "LLVM", "LazyArtifacts", "Libdl", "LinearAlgebra", "Logging", "MacroTools", "Memoize", "Printf", "Random", "Random123", "RandomNumbers", "Reexport", "Requires", "SparseArrays", "SpecialFunctions", "TimerOutputs"] +git-tree-sha1 = "364179416eabc34c9ca32126a6bdb431680c3bad" +uuid = "052768ef-5323-5732-b1bb-66c8b64840ba" +version = "3.2.1" + +[[ChainRulesCore]] +deps = ["Compat", "LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "8b31cc69cbc38c5c826aaa1c890c694be3622d99" +uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +version = "0.10.3" + +[[Compat]] +deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] +git-tree-sha1 = "e4e2b39db08f967cc1360951f01e8a75ec441cab" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "3.30.0" + +[[CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" + +[[ConstructionBase]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "1dc43957fb9a1574fa1b7a449e101bd1fd3a9fb7" +uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" +version = "1.2.1" + +[[DataStructures]] +deps = ["Compat", "InteractiveUtils", "OrderedCollections"] +git-tree-sha1 = "4437b64df1e0adccc3e5d1adbc3ac741095e4677" +uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +version = "0.18.9" + +[[Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[DelimitedFiles]] +deps = ["Mmap"] +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" + +[[Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[DocStringExtensions]] +deps = ["LibGit2"] +git-tree-sha1 = "a32185f5428d3986f47c2ab78b1f216d5e6cc96f" +uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +version = "0.8.5" + +[[Downloads]] +deps = ["ArgTools", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" + +[[Elfutils_jll]] +deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "XZ_jll", "Zlib_jll", "argp_standalone_jll", "fts_jll", "obstack_jll"] +git-tree-sha1 = "76cbf1134983cfb371ad77117bb2659600ed64d6" +uuid = "ab5a07f8-06af-567f-a878-e8bb879eba5a" +version = "0.179.0+0" + +[[ExprTools]] +git-tree-sha1 = "10407a39b87f29d47ebaca8edbc75d7c302ff93e" +uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" +version = "0.1.3" + +[[Future]] +deps = ["Random"] +uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" + +[[GPUArrays]] +deps = ["AbstractFFTs", "Adapt", "LinearAlgebra", "Printf", "Random", "Serialization", "Statistics"] +git-tree-sha1 = "df5b8569904c5c10e84c640984cfff054b18c086" +uuid = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7" +version = "6.4.1" + +[[GPUCompiler]] +deps = ["DataStructures", "ExprTools", "InteractiveUtils", "LLVM", "Libdl", "Logging", "Scratch", "Serialization", "TimerOutputs", "UUIDs"] +git-tree-sha1 = "42d635f6d87af125b86288df3819f805fb4d851a" +uuid = "61eb1bfa-7361-4325-ad38-22787b887f55" +version = "0.11.5" + +[[InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[JLLWrappers]] +deps = ["Preferences"] +git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.3.0" + +[[LLVM]] +deps = ["CEnum", "Libdl", "Printf", "Unicode"] +git-tree-sha1 = "b499c68a45249b0385585c62f4a9b62b5db8e691" +uuid = "929cbde3-209d-540e-8aea-75f648917ca0" +version = "3.7.1" + +[[LazyArtifacts]] +deps = ["Artifacts", "Pkg"] +uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" + +[[LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" + +[[LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" + +[[LibGit2]] +deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" + +[[Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[LinearAlgebra]] +deps = ["Libdl"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[LogExpFunctions]] +deps = ["DocStringExtensions", "LinearAlgebra"] +git-tree-sha1 = "1ba664552f1ef15325e68dc4c05c3ef8c2d5d885" +uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" +version = "0.2.4" + +[[Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[MacroTools]] +deps = ["Markdown", "Random"] +git-tree-sha1 = "6a8a2a625ab0dea913aba95c11370589e0239ff0" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.5.6" + +[[Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" + +[[Memoize]] +deps = ["MacroTools"] +git-tree-sha1 = "2b1dfcba103de714d31c033b5dacc2e4a12c7caa" +uuid = "c03570c3-d221-55d1-a50c-7939bbd78826" +version = "0.4.4" + +[[Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" + +[[NUMA_jll]] +deps = ["Libdl", "Pkg"] +git-tree-sha1 = "778f9bd14400cff2c32ed357e12766ac0e3d766e" +uuid = "7f51dc2b-bb24-59f8-b771-bb1490e4195d" +version = "2.0.13+1" + +[[NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" + +[[OpenSpecFun_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" +uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" +version = "0.5.5+0" + +[[OrderedCollections]] +git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.4.1" + +[[Parameters]] +deps = ["OrderedCollections", "UnPack"] +git-tree-sha1 = "2276ac65f1e236e0a6ea70baff3f62ad4c625345" +uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" +version = "0.12.2" + +[[Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[Preferences]] +deps = ["TOML"] +git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.2.2" + +[[Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[Random]] +deps = ["Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[Random123]] +deps = ["Libdl", "Random", "RandomNumbers"] +git-tree-sha1 = "7c6710c8198fd4444b5eb6a3840b7d47bd3593c5" +uuid = "74087812-796a-5b5d-8853-05524746bad3" +version = "1.3.1" + +[[RandomNumbers]] +deps = ["Random", "Requires"] +git-tree-sha1 = "441e6fc35597524ada7f85e13df1f4e10137d16f" +uuid = "e6cf234a-135c-5ec9-84dd-332b85af5143" +version = "1.4.0" + +[[Reexport]] +git-tree-sha1 = "5f6c21241f0f655da3952fd60aa18477cf96c220" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "1.1.0" + +[[Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.1.3" + +[[SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[Scratch]] +deps = ["Dates"] +git-tree-sha1 = "0b4b7f1393cff97c33891da2a0bf69c6ed241fda" +uuid = "6c6a2e73-6563-6170-7368-637461726353" +version = "1.1.0" + +[[Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[Setfield]] +deps = ["ConstructionBase", "Future", "MacroTools", "Requires"] +git-tree-sha1 = "d5640fc570fb1b6c54512f0bd3853866bd298b3e" +uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" +version = "0.7.0" + +[[SharedArrays]] +deps = ["Distributed", "Mmap", "Random", "Serialization"] +uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + +[[Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[SpecialFunctions]] +deps = ["ChainRulesCore", "LogExpFunctions", "OpenSpecFun_jll"] +git-tree-sha1 = "a50550fa3164a8c46747e62063b4d774ac1bcf49" +uuid = "276daf66-3868-5448-9aa4-cd146d93841b" +version = "1.5.1" + +[[Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" + +[[Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" + +[[Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[TextWrap]] +git-tree-sha1 = "9250ef9b01b66667380cf3275b3f7488d0e25faf" +uuid = "b718987f-49a8-5099-9789-dcd902bef87d" +version = "1.0.1" + +[[TimerOutputs]] +deps = ["ExprTools", "Printf"] +git-tree-sha1 = "bf8aacc899a1bd16522d0350e1e2310510d77236" +uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" +version = "0.5.9" + +[[UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[UnPack]] +git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" +uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +version = "1.0.2" + +[[Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[XZ_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "9f76853ea2ba894054e24640abfb73d73e5a4cb5" +uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" +version = "5.2.5+0" + +[[Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" + +[[argp_standalone_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c4fa3457046fc93249b63e8319e743b6c8590609" +uuid = "c53206cc-00f7-50bf-ad1e-3ae1f6e49bc3" +version = "1.3.0+0" + +[[fts_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "78732b942383d2cb521df8a1a0814911144e663d" +uuid = "d65627f6-89bd-53e8-8ab5-8b75ff535eee" +version = "1.2.7+1" + +[[hsa_rocr_jll]] +deps = ["Artifacts", "Elfutils_jll", "JLLWrappers", "Libdl", "NUMA_jll", "Pkg", "Zlib_jll", "hsakmt_roct_jll"] +git-tree-sha1 = "42189f176d6ae4f37c0c0e652fec339bb0bfab5d" +uuid = "dd59ff1a-a01a-568d-8b29-0669330f116a" +version = "3.7.0+1" + +[[hsakmt_roct_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "NUMA_jll", "Pkg"] +git-tree-sha1 = "8a9ee6c091e952e4ea6585d15131d43f789ae041" +uuid = "1cecccd7-a9b6-5045-9cdc-a44c19b16d76" +version = "3.8.0+0" + +[[nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" + +[[obstack_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "1c4a6b66e934fc6db4649cb2910c72f53bbfea7e" +uuid = "c88a4935-d25e-5644-aacc-5db6f1b8ef79" +version = "1.2.2+0" + +[[p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" diff --git a/JuliaStream.jl/Project.toml b/JuliaStream.jl/Project.toml new file mode 100644 index 0000000..0afa7d0 --- /dev/null +++ b/JuliaStream.jl/Project.toml @@ -0,0 +1,14 @@ +name = "JuliaStream" +uuid = "1bdcc9b7-f5ed-4705-bc7b-be1b748ec681" +authors = ["Wei-Chen Lin "] +version = "3.4.0" + +[deps] +AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" +ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" +CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" +Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" +Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a" + +[compat] +julia = "1.6" diff --git a/JuliaStream.jl/README.md b/JuliaStream.jl/README.md new file mode 100644 index 0000000..7d94a34 --- /dev/null +++ b/JuliaStream.jl/README.md @@ -0,0 +1,30 @@ +JuliaStream.jl +============== + +This is an implementation of BabelStream in Julia which contains the following variants: + + * `PlainStream.jl` - Single threaded `for` + * `ThreadedStream.jl` - Threaded implementation with `Threads.@threads` macros + * `DistributedStream.jl` - Process based parallelism with `@distributed` macros + * `CUDAStream.jl` - Direct port of BabelStream's native CUDA implementation using [CUDA.jl](https://github.com/JuliaGPU/CUDA.jl) + * `AMDGPUStream.jl` - Direct port of BabelStream's native HIP implementation using [AMDGPU.jl](https://github.com/JuliaGPU/AMDGPU.jl) + +### Build & Run + +Prerequisites + + * Julia 1.6+ + +With Julia on path, run the benchmark with: + +```shell +> cd JuliaStream.jl +> julia --project -e 'import Pkg; Pkg.instantiate()' # only required on first run +> julia --project src/Stream.jl +``` + +**Important:** + * Julia is 1-indexed, so N > 1 in `--device N` + * Thread count for `ThreadedStream` must be set via the `JULIA_NUM_THREADS` environment variable (e.g `export JULIA_NUM_THREADS=$(nproc)`) otherwise it defaults to 1 + * You must *prepend* the number of processes needed for `DistributedStream`, e.g `julia -p$(nproc) --project src/DistributedStream.jl` + * Certain implementations such as CUDA and AMDGPU will do hardware detection at runtime and may download and/or compile further software packages for the platform. diff --git a/JuliaStream.jl/src/AMDGPUStream.jl b/JuliaStream.jl/src/AMDGPUStream.jl new file mode 100644 index 0000000..80f69b4 --- /dev/null +++ b/JuliaStream.jl/src/AMDGPUStream.jl @@ -0,0 +1,178 @@ +# AMDGPU.jl doesn't support CPU agents, so this isn't a feature-complete ROCmStream, only AMD GPUs +include("Stream.jl") +using AMDGPU + +const ROCData = StreamData{T,ROCArray{T}} where {T} +const TBSize = 1024::Int +const DotBlocks = 256::Int + +# AMDGPU.agents()'s internal iteration order isn't stable +function gpu_agents_in_repr_order() + # XXX if we select anything other than :gpu, we get + # HSA_STATUS_ERROR_INVALID_AGENT on the first kernel submission + sort(AMDGPU.get_agents(:gpu), by = repr) +end + +function devices() + try + map(repr, gpu_agents_in_repr_order()) + catch + # probably unsupported + [] + end +end + +function gridsize(data::ROCData{T})::Int where {T} + return data.size +end + +function make_stream( + arraysize::Int, + scalar::T, + device::Int, + silent::Bool, +)::ROCData{T} where {T} + + if arraysize % TBSize != 0 + error("arraysize ($(arraysize)) must be divisible by $(TBSize)!") + end + + # XXX AMDGPU doesn't expose an API for setting the default like CUDA.device!() + # but AMDGPU.get_default_agent returns DEFAULT_AGENT so we can do it by hand + AMDGPU.DEFAULT_AGENT[] = gpu_agents_in_repr_order()[device] + + data = ROCData{T}( + ROCArray{T}(undef, arraysize), + ROCArray{T}(undef, arraysize), + ROCArray{T}(undef, arraysize), + scalar, + arraysize, + ) + selected = AMDGPU.get_default_agent() + if !silent + println("Using GPU HSA device: $(AMDGPU.get_name(selected)) ($(repr(selected)))") + println("Kernel parameters : <<<$(gridsize(data)),$(TBSize)>>>") + end + return data +end + +function hard_wait(kernel) + # soft wait causes HSA_REFCOUNT overflow issues + AMDGPU.wait(kernel, soft = false) +end + +function init_arrays!(data::ROCData{T}, init::Tuple{T,T,T}) where {T} + AMDGPU.fill!(data.a, init[1]) + AMDGPU.fill!(data.b, init[2]) + AMDGPU.fill!(data.c, init[3]) +end + +function copy!(data::ROCData{T}) where {T} + function kernel(a, c) + i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x + @inbounds c[i] = a[i] + return + end + hard_wait(@roc groupsize = TBSize gridsize = gridsize(data) kernel(data.a, data.c)) +end + +function mul!(data::ROCData{T}) where {T} + function kernel(b, c, scalar) + i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x + @inbounds b[i] = scalar * c[i] + return + end + hard_wait( + @roc groupsize = TBSize gridsize = gridsize(data) kernel(data.b, data.c, data.scalar) + ) +end + +function add!(data::ROCData{T}) where {T} + function kernel(a, b, c) + i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x + @inbounds c[i] = a[i] + b[i] + return + end + hard_wait( + @roc groupsize = TBSize gridsize = gridsize(data) kernel(data.a, data.b, data.c) + ) +end + +function triad!(data::ROCData{T}) where {T} + function kernel(a, b, c, scalar) + i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x + @inbounds a[i] = b[i] + (scalar * c[i]) + return + end + hard_wait( + @roc groupsize = TBSize gridsize = gridsize(data) kernel( + data.a, + data.b, + data.c, + data.scalar, + ) + ) +end + +function nstream!(data::ROCData{T}) where {T} + function kernel(a, b, c, scalar) + i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x + @inbounds a[i] += b[i] + scalar * c[i] + return + end + hard_wait( + @roc groupsize = TBSize gridsize = gridsize(data) kernel( + data.a, + data.b, + data.c, + data.scalar, + ) + ) +end + +function dot(data::ROCData{T}) where {T} + function kernel(a, b, size, partial) + tb_sum = ROCDeviceArray((TBSize,), alloc_local(:reduce, T, TBSize)) + local_i = workitemIdx().x + @inbounds tb_sum[local_i] = 0.0 + + # do dot first + i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x + while i <= size + @inbounds tb_sum[local_i] += a[i] * b[i] + i += TBSize * DotBlocks # XXX don't use (workgroupDim().x * gridDimWG().x) here + end + + # then tree reduction + offset = workgroupDim().x ÷ 2 + while offset > 0 + sync_workgroup() + if (local_i - 1) < offset + @inbounds tb_sum[local_i] += tb_sum[local_i+offset] + end + offset ÷= 2 + end + + if (local_i == 1) + @inbounds partial[workgroupIdx().x] = tb_sum[local_i] + end + + return + end + partial_sum = ROCArray{T}(undef, DotBlocks) + hard_wait( + @roc groupsize = TBSize gridsize = TBSize * DotBlocks kernel( + data.a, + data.b, + data.size, + partial_sum, + ) + ) + return sum(partial_sum) +end + +function read_data(data::ROCData{T})::VectorData{T} where {T} + return VectorData{T}(data.a, data.b, data.c, data.scalar, data.size) +end + +main() \ No newline at end of file diff --git a/JuliaStream.jl/src/CUDAStream.jl b/JuliaStream.jl/src/CUDAStream.jl new file mode 100644 index 0000000..c4d0510 --- /dev/null +++ b/JuliaStream.jl/src/CUDAStream.jl @@ -0,0 +1,146 @@ +include("Stream.jl") +using CUDA + +const CuData = StreamData{T,CuArray{T}} where {T} +const TBSize = 1024::Int +const DotBlocks = 256::Int + +function devices() + return !CUDA.functional(false) ? [] : + map(d -> "$(CUDA.name(d)) ($(repr(d)))", CUDA.devices()) +end + +function blocks(data::CuData{T})::Int where {T} + return data.size ÷ TBSize +end + +function make_stream( + arraysize::Int, + scalar::T, + device::Int, + silent::Bool, +)::CuData{T} where {T} + + if arraysize % TBSize != 0 + error("arraysize ($(arraysize)) must be divisible by $(TBSize)!") + end + + # so CUDA's device is 0 indexed, so -1 from Julia + CUDA.device!(device - 1) + selected = CUDA.device() + # show_reason is set to true here so it dumps CUDA info + # for us regardless of whether it's functional + if !CUDA.functional(true) + error("Non-functional CUDA configuration") + end + data = CuData{T}( + CuArray{T}(undef, arraysize), + CuArray{T}(undef, arraysize), + CuArray{T}(undef, arraysize), + scalar, + arraysize, + ) + if !silent + println("Using CUDA device: $(CUDA.name(selected)) ($(repr(selected)))") + println("Kernel parameters: <<<$(blocks(data)),$(TBSize)>>>") + end + return data +end + +function init_arrays!(data::CuData{T}, init::Tuple{T,T,T}) where {T} + CUDA.fill!(data.a, init[1]) + CUDA.fill!(data.b, init[2]) + CUDA.fill!(data.c, init[3]) +end + +function copy!(data::CuData{T}) where {T} + function kernel(a, c) + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x + @inbounds c[i] = a[i] + return + end + @cuda blocks = blocks(data) threads = TBSize kernel(data.a, data.c) + CUDA.synchronize() +end + +function mul!(data::CuData{T}) where {T} + function kernel(b, c, scalar) + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x + @inbounds b[i] = scalar * c[i] + return + end + @cuda blocks = blocks(data) threads = TBSize kernel(data.b, data.c, data.scalar) + CUDA.synchronize() +end + +function add!(data::CuData{T}) where {T} + function kernel(a, b, c) + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x + @inbounds c[i] = a[i] + b[i] + return + end + @cuda blocks = blocks(data) threads = TBSize kernel(data.a, data.b, data.c) + CUDA.synchronize() +end + +function triad!(data::CuData{T}) where {T} + function kernel(a, b, c, scalar) + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x + @inbounds a[i] = b[i] + (scalar * c[i]) + return + end + @cuda blocks = blocks(data) threads = TBSize kernel(data.a, data.b, data.c, data.scalar) + CUDA.synchronize() +end + +function nstream!(data::CuData{T}) where {T} + function kernel(a, b, c, scalar) + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x + @inbounds a[i] += b[i] + scalar * c[i] + return + end + @cuda blocks = blocks(data) threads = TBSize kernel(data.a, data.b, data.c, data.scalar) + CUDA.synchronize() +end + +function dot(data::CuData{T}) where {T} + # direct port of the reduction in CUDAStream.cu + function kernel(a, b, size, partial) + tb_sum = @cuStaticSharedMem(T, TBSize) + local_i = threadIdx().x + @inbounds tb_sum[local_i] = 0.0 + + # do dot first + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x + while i <= size + @inbounds tb_sum[local_i] += a[i] * b[i] + i += blockDim().x * gridDim().x + end + + # then tree reduction + offset = blockDim().x ÷ 2 + while offset > 0 + sync_threads() + if (local_i - 1) < offset + @inbounds tb_sum[local_i] += tb_sum[local_i+offset] + end + offset ÷= 2 + end + + if (local_i == 1) + @inbounds partial[blockIdx().x] = tb_sum[local_i] + end + + return + end + partial_sum = CuArray{T}(undef, DotBlocks) + @cuda blocks = DotBlocks threads = TBSize kernel(data.a, data.b, data.size, partial_sum) + CUDA.synchronize() + return sum(partial_sum) +end + +function read_data(data::CuData{T})::VectorData{T} where {T} + return VectorData{T}(data.a, data.b, data.c, data.scalar, data.size) +end + +main() \ No newline at end of file diff --git a/JuliaStream.jl/src/DistributedStream.jl b/JuliaStream.jl/src/DistributedStream.jl new file mode 100644 index 0000000..970c699 --- /dev/null +++ b/JuliaStream.jl/src/DistributedStream.jl @@ -0,0 +1,84 @@ +using Distributed + +include("Stream.jl") + +@everywhere include("StreamData.jl") +@everywhere using SharedArrays +@everywhere const SharedArrayData = StreamData{T,SharedArray{T}} where {T} + +function devices() + return ["CPU (localhost)"] +end + +function make_stream( + arraysize::Int, + scalar::T, + device::Int, + silent::Bool, +)::SharedArrayData{T} where {T} + if device != 1 + error("Only CPU device is supported") + end + + if !silent + println("Using max $(nworkers()) process(es) + 1 master") + end + return SharedArrayData{T}( + SharedArray{T}(arraysize), + SharedArray{T}(arraysize), + SharedArray{T}(arraysize), + scalar, + arraysize, + ) +end + +function init_arrays!(data::SharedArrayData{T}, init::Tuple{T,T,T}) where {T} + + @sync @distributed for i = 1:data.size + @inbounds data.a[i] = init[1] + @inbounds data.b[i] = init[2] + @inbounds data.c[i] = init[3] + end +end + +function copy!(data::SharedArrayData{T}) where {T} + @sync @distributed for i = 1:data.size + @inbounds data.c[i] = data.a[i] + end +end + +function mul!(data::SharedArrayData{T}) where {T} + @sync @distributed for i = 1:data.size + @inbounds data.b[i] = data.scalar * data.c[i] + end +end + +function add!(data::SharedArrayData{T}) where {T} + @sync @distributed for i = 1:data.size + @inbounds data.c[i] = data.a[i] + data.b[i] + end +end + +function triad!(data::SharedArrayData{T}) where {T} + @sync @distributed for i = 1:data.size + @inbounds data.a[i] = data.b[i] + (data.scalar * data.c[i]) + end +end + +function nstream!(data::SharedArrayData{T}) where {T} + @sync @distributed for i = 1:data.size + @inbounds data.a[i] += data.b[i] + data.scalar * data.c[i] + end +end + +function dot(data::SharedArrayData{T}) where {T} + return @distributed (+) for i = 1:data.size + @inbounds data.a[i] * data.b[i] + end +end + +function read_data(data::SharedArrayData{T})::VectorData{T} where {T} + return VectorData{T}(data.a, data.b, data.c, data.scalar, data.size) +end + +main() \ No newline at end of file diff --git a/JuliaStream.jl/src/JuliaStream.jl b/JuliaStream.jl/src/JuliaStream.jl new file mode 100644 index 0000000..e01d46d --- /dev/null +++ b/JuliaStream.jl/src/JuliaStream.jl @@ -0,0 +1,4 @@ +module JuliaStream +end + +println("Please run benchmarks directly via `julia --project src/Stream.jl`") \ No newline at end of file diff --git a/JuliaStream.jl/src/PlainStream.jl b/JuliaStream.jl/src/PlainStream.jl new file mode 100644 index 0000000..259a9b7 --- /dev/null +++ b/JuliaStream.jl/src/PlainStream.jl @@ -0,0 +1,64 @@ +include("Stream.jl") + +function devices() + return ["CPU"] +end + +function make_stream(arraysize::Int, scalar::T, device::Int, silent::Bool)::VectorData{T} where {T} + if device != 1 + error("Only CPU device is supported") + end + return VectorData{T}(1:arraysize, 1:arraysize, 1:arraysize, scalar, arraysize) +end + +function init_arrays!(data::VectorData{T}, init::Tuple{T,T,T}) where {T} + for i = 1:data.size + @inbounds data.a[i] = init[1] + @inbounds data.b[i] = init[2] + @inbounds data.c[i] = init[3] + end +end + +function copy!(data::VectorData{T}) where {T} + for i = 1:data.size + @inbounds data.c[i] = data.a[i] + end +end + +function mul!(data::VectorData{T}) where {T} + for i = 1:data.size + @inbounds data.b[i] = data.scalar * data.c[i] + end +end + +function add!(data::VectorData{T}) where {T} + for i = 1:data.size + @inbounds data.c[i] = data.a[i] + data.b[i] + end +end + +function triad!(data::VectorData{T}) where {T} + for i = 1:data.size + @inbounds data.a[i] = data.b[i] + (data.scalar * data.c[i]) + end +end + +function nstream!(data::VectorData{T}) where {T} + for i = 1:data.size + @inbounds data.a[i] += data.b[i] + data.scalar * data.c[i] + end +end + +function dot(data::VectorData{T}) where {T} + sum = zero(T) + for i = 1:data.size + @inbounds sum += data.a[i] * data.b[i] + end + return sum +end + +function read_data(data::VectorData{T})::VectorData{T} where {T} + return data +end + +main() \ No newline at end of file diff --git a/JuliaStream.jl/src/Stream.jl b/JuliaStream.jl/src/Stream.jl new file mode 100644 index 0000000..cce846b --- /dev/null +++ b/JuliaStream.jl/src/Stream.jl @@ -0,0 +1,292 @@ +using ArgParse +using Parameters +using Printf +using Base: Float64, Int + +include("StreamData.jl") + +const VectorData = StreamData{T,Vector{T}} where {T} + +struct Timings + copy::Vector{Float64} + mul::Vector{Float64} + add::Vector{Float64} + triad::Vector{Float64} + dot::Vector{Float64} + Timings(n) = new(zeros(n), zeros(n), zeros(n), zeros(n), zeros(n)) +end + +@enum Benchmark All Triad Nstream + +function run_all!(data::StreamData{T,C}, times::Int)::Tuple{Timings,T} where {T,C} + timings = Timings(times) + lastSum::T = 0 + for i = 1:times + @inbounds timings.copy[i] = @elapsed copy!(data) + @inbounds timings.mul[i] = @elapsed mul!(data) + @inbounds timings.add[i] = @elapsed add!(data) + @inbounds timings.triad[i] = @elapsed triad!(data) + @inbounds timings.dot[i] = @elapsed lastSum = dot(data) + end + return (timings, lastSum) +end + +function run_triad!(data::StreamData{T,C}, times::Int)::Float64 where {T,C} + return @elapsed for _ = 1:times + triad!(data) + end +end + +function run_nstream!(data::StreamData{T,C}, times::Int)::Vector{Float64} where {T,C} + timings::Vector{Float64} = zeros(times) + for i = 1:times + @inbounds timings[i] = @elapsed nstream!(data) + end + return timings +end + +function check_solutions( + data::StreamData{T,C}, + times::Int, + init::Tuple{T,T,T}, + benchmark::Benchmark, + dot::Union{T,Nothing}, +) where {T,C} + (gold_a, gold_b, gold_c) = init + for _ = 1:times + if benchmark == All + gold_c = gold_a + gold_b = data.scalar * gold_c + gold_c = gold_a + gold_b + gold_a = gold_b + data.scalar * gold_c + elseif benchmark == Triad + gold_a = gold_b + data.scalar * gold_c + elseif benchmark == Nstream + gold_a += gold_b + data.scalar * gold_c + else + error("Unknown benchmark", benchmark) + end + end + + tolerance = eps(T) * 100 + function validate_xs(name::String, xs::AbstractArray{T}, from::T) + error = (map(x -> abs(x - from), xs) |> sum) / length(xs) + failed = error > tolerance + if failed + println("Validation failed on $name. Average error $error") + end + !failed + end + a_valid = validate_xs("a", data.a, gold_a) + b_valid = validate_xs("b", data.b, gold_b) + c_valid = validate_xs("c", data.c, gold_c) + dot_valid = + dot !== nothing ? + begin + gold_sum = gold_a * gold_b * data.size + error = abs((dot - gold_sum) / gold_sum) + failed = error > 1.0e-8 + if failed + println( + "Validation failed on sum. Error $error \nSum was $dot but should be $gold_sum", + ) + end + !failed + end : true + + a_valid && b_valid && c_valid && dot_valid +end + +@with_kw mutable struct Config + list::Bool = false + impl::String = "threaded" + device::Int = 1 + numtimes::Int = 100 + arraysize::Int = 33554432 + float::Bool = false + triad_only::Bool = false + nstream_only::Bool = false + csv::Bool = false + mibibytes::Bool = false +end + +function parse_options(given::Config) + s = ArgParseSettings() + @add_arg_table s begin + "--list" + help = "List available devices" + action = :store_true + "--device", "-d" + help = "Select device at DEVICE, NOTE: Julia is 1-indexed" + arg_type = Int + default = given.device + "--numtimes", "-n" + help = "Run the test NUMTIMES times (NUM >= 2)" + arg_type = Int + default = given.numtimes + "--arraysize", "-s" + help = "Use ARRAYSIZE elements in the array" + arg_type = Int + default = given.arraysize + "--float" + help = "Use floats (rather than doubles)" + action = :store_true + "--triad_only" + help = "Only run triad" + action = :store_true + "--nstream_only" + help = "Only run nstream" + action = :store_true + "--csv" + help = "Output as csv table" + action = :store_true + "--mibibytes" + help = "Use MiB=2^20 for bandwidth calculation (default MB=10^6)" + action = :store_true + end + args = parse_args(s) + # surely there's a better way than doing this: + for (arg, val) in args + setproperty!(given, Symbol(arg), val) + end +end + +const DefaultInit = (0.1, 0.2, 0.0) +const DefaultScalar = 0.4 +const Version = "3.4.0" + +function main() + + config::Config = Config() + parse_options(config) + + if config.list + ds = devices() + for (i, device) in enumerate(ds) + println("[$i] $(device)") + end + exit(0) + end + + ds = devices() + if config.device < 1 || config.device > length(ds) + error( + "Device $(config.device) out of range (1..$(length(ds))), NOTE: Julia is 1-indexed", + ) + end + + if config.float + type = Float32 + else + type = Float64 + end + + if config.nstream_only && !config.triad_only + benchmark = Nstream + elseif !config.nstream_only && config.triad_only + benchmark = Triad + elseif !config.nstream_only && !config.triad_only + benchmark = All + elseif config.nstream_only && config.triad_only + error("Both triad and nstream are enabled, pick one or omit both to run all benchmarks") + else + error("Invalid config: $(repr(config))") + end + + array_bytes = config.arraysize * sizeof(type) + total_bytes = array_bytes * 3 + (mega_scale, mega_suffix, giga_scale, giga_suffix) = + !config.mibibytes ? (1.0e-6, "MB", 1.0e-9, "GB") : (2^-20, "MiB", 2^-30, "GiB") + + if !config.csv + println("""BabelStream + Version: $Version + Implementation: Julia; $(config.impl)""") + println("Running kernels $(config.numtimes) times") + if benchmark == Triad + println("Number of elements: $(config.arraysize)") + end + println("Precision: $(config.float ? "float" : "double")") + r1 = n -> round(n; digits = 1) + println( + "Array size: $(r1(mega_scale * array_bytes)) $mega_suffix(=$(r1(giga_scale * array_bytes)) $giga_suffix)", + ) + println( + "Total size: $(r1(mega_scale * total_bytes)) $mega_suffix(=$(r1(giga_scale * total_bytes)) $giga_suffix)", + ) + end + + function mk_row(xs::Vector{Float64}, name::String, total_bytes::Int) + tail = Base.rest(xs) + min = Iterators.minimum(tail) + max = Iterators.maximum(tail) + avg = Iterators.sum(tail) / Iterators.length(tail) + mbps = mega_scale * total_bytes / min + if config.csv + return [ + ("function", name), + ("num_times", config.numtimes), + ("n_elements", config.arraysize), + ("sizeof", total_bytes), + ("max_m$( config.mibibytes ? "i" : "")bytes_per_sec", mbps), + ("min_runtime", min), + ("max_runtime", max), + ("avg_runtime", avg), + ] + else + return [ + ("Function", name), + ("M$(config.mibibytes ? "i" : "")Bytes/sec", round(mbps; digits = 3)), + ("Min (sec)", round(min; digits = 5)), + ("Max", round(max; digits = 5)), + ("Average", round(avg; digits = 5)), + ] + end + end + + function tabulate(rows::Vector{Tuple{String,Any}}...) + header = Base.first(rows) + padding = config.csv ? 0 : 12 + sep = config.csv ? "," : "" + map(x -> rpad(x[1], padding), header) |> x -> join(x, sep) |> println + for row in rows + map(x -> rpad(x[2], padding), row) |> x -> join(x, sep) |> println + end + end + + init::Tuple{type,type,type} = DefaultInit + scalar::type = DefaultScalar + + data = make_stream(config.arraysize, scalar, config.device, config.csv) + + init_arrays!(data, init) + if benchmark == All + (timings, sum) = run_all!(data, config.numtimes) + valid = check_solutions(read_data(data), config.numtimes, init, benchmark, sum) + tabulate( + mk_row(timings.copy, "Copy", 2 * array_bytes), + mk_row(timings.mul, "Mul", 2 * array_bytes), + mk_row(timings.add, "Add", 3 * array_bytes), + mk_row(timings.triad, "Triad", 3 * array_bytes), + mk_row(timings.dot, "Dot", 2 * array_bytes), + ) + elseif benchmark == Nstream + timings = run_nstream!(data, config.numtimes) + valid = check_solutions(read_data(data), config.numtimes, init, benchmark, nothing) + tabulate(mk_row(timings, "Nstream", 4 * array_bytes)) + elseif benchmark == Triad + elapsed = run_triad!(data, config.numtimes) + valid = check_solutions(read_data(data), config.numtimes, init, benchmark, nothing) + total_bytes = 3 * array_bytes * config.numtimes + bandwidth = mega_scale * (total_bytes / elapsed) + println("Runtime (seconds): $(round(elapsed; digits=5))") + println("Bandwidth ($giga_suffix/s): $(round(bandwidth; digits=3)) ") + else + error("Bad benchmark $(benchmark)") + end + + if !valid + exit(1) + end + +end diff --git a/JuliaStream.jl/src/StreamData.jl b/JuliaStream.jl/src/StreamData.jl new file mode 100644 index 0000000..07498fe --- /dev/null +++ b/JuliaStream.jl/src/StreamData.jl @@ -0,0 +1,8 @@ + +struct StreamData{T,C<:AbstractArray{T}} + a::C + b::C + c::C + scalar::T + size::Int + end \ No newline at end of file diff --git a/JuliaStream.jl/src/ThreadedStream.jl b/JuliaStream.jl/src/ThreadedStream.jl new file mode 100644 index 0000000..fb995e6 --- /dev/null +++ b/JuliaStream.jl/src/ThreadedStream.jl @@ -0,0 +1,72 @@ +include("Stream.jl") + +function devices() + return ["CPU"] +end + +function make_stream( + arraysize::Int, + scalar::T, + device::Int, + silent::Bool, +)::VectorData{T} where {T} + if device != 1 + error("Only CPU device is supported") + end + if !silent + println("Using max $(Threads.nthreads()) threads") + end + return VectorData{T}(1:arraysize, 1:arraysize, 1:arraysize, scalar, arraysize) +end + +function init_arrays!(data::VectorData{T}, init::Tuple{T,T,T}) where {T} + Threads.@threads for i = 1:data.size + @inbounds data.a[i] = init[1] + @inbounds data.b[i] = init[2] + @inbounds data.c[i] = init[3] + end +end + +function copy!(data::VectorData{T}) where {T} + Threads.@threads for i = 1:data.size + @inbounds data.c[i] = data.a[i] + end +end + +function mul!(data::VectorData{T}) where {T} + Threads.@threads for i = 1:data.size + @inbounds data.b[i] = data.scalar * data.c[i] + end +end + +function add!(data::VectorData{T}) where {T} + Threads.@threads for i = 1:data.size + @inbounds data.c[i] = data.a[i] + data.b[i] + end +end + +function triad!(data::VectorData{T}) where {T} + Threads.@threads for i = 1:data.size + @inbounds data.a[i] = data.b[i] + (data.scalar * data.c[i]) + end +end + +function nstream!(data::VectorData{T}) where {T} + Threads.@threads for i = 1:data.size + @inbounds data.a[i] += data.b[i] + data.scalar * data.c[i] + end +end + +function dot(data::VectorData{T}) where {T} + partial = zeros(T, Threads.nthreads()) + Threads.@threads for i = 1:data.size + @inbounds partial[Threads.threadid()] += data.a[i] * data.b[i] + end + return sum(partial) +end + +function read_data(data::VectorData{T})::VectorData{T} where {T} + return data +end + +main() \ No newline at end of file From 63f471f8800dfb8d9bcb2861d3b0f6ec1231f47a Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 10 Jun 2021 04:33:12 +0100 Subject: [PATCH 30/78] set pwd to JuliaStream.jl for CI run --- .github/workflows/main.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 83bcd9d..3c90b37 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -5,6 +5,9 @@ on: [push, pull_request] jobs: test-julia: runs-on: ubuntu-18.04 + defaults: + run: + working-directory: ./JuliaStream.jl steps: - uses: actions/checkout@v2 - name: Setup project From 2cf8ca5f8cdd3b2efb5653dde4da8e5c61143cf1 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 10 Jun 2021 04:57:52 +0100 Subject: [PATCH 31/78] Use addprocs() for DistributedStream --- .github/workflows/main.yaml | 5 +++++ JuliaStream.jl/README.md | 2 +- JuliaStream.jl/src/DistributedStream.jl | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 3c90b37..f666150 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -13,14 +13,19 @@ jobs: - name: Setup project run: julia --project -e 'import Pkg; Pkg.instantiate()' - name: Test run PlainStream.jl + if: ${{ ! cancelled() }} run: julia --project src/PlainStream.jl --arraysize 100 - name: Test run ThreadedStream.jl + if: ${{ ! cancelled() }} run: julia --threads 2 --project src/ThreadedStream.jl --arraysize 100 - name: Test run DistributedStream.jl + if: ${{ ! cancelled() }} run: julia -p2 --project src/DistributedStream.jl --arraysize 100 - name: Test run CUDAStream.jl + if: ${{ ! cancelled() }} run: julia --project src/CUDAStream.jl --list - name: Test run AMDGPUStream.jl + if: ${{ ! cancelled() }} run: julia --project src/AMDGPUStream.jl --list test: diff --git a/JuliaStream.jl/README.md b/JuliaStream.jl/README.md index 7d94a34..9126167 100644 --- a/JuliaStream.jl/README.md +++ b/JuliaStream.jl/README.md @@ -26,5 +26,5 @@ With Julia on path, run the benchmark with: **Important:** * Julia is 1-indexed, so N > 1 in `--device N` * Thread count for `ThreadedStream` must be set via the `JULIA_NUM_THREADS` environment variable (e.g `export JULIA_NUM_THREADS=$(nproc)`) otherwise it defaults to 1 - * You must *prepend* the number of processes needed for `DistributedStream`, e.g `julia -p$(nproc) --project src/DistributedStream.jl` + * `DistributedStream` uses `addprocs()` call directly which defaults to `$(nproc)`, **do not use the `-p ` flag** as per the [documentation](https://docs.julialang.org/en/v1/manual/distributed-computing). * Certain implementations such as CUDA and AMDGPU will do hardware detection at runtime and may download and/or compile further software packages for the platform. diff --git a/JuliaStream.jl/src/DistributedStream.jl b/JuliaStream.jl/src/DistributedStream.jl index 970c699..361c737 100644 --- a/JuliaStream.jl/src/DistributedStream.jl +++ b/JuliaStream.jl/src/DistributedStream.jl @@ -2,6 +2,8 @@ using Distributed include("Stream.jl") +addprocs() + @everywhere include("StreamData.jl") @everywhere using SharedArrays @everywhere const SharedArrayData = StreamData{T,SharedArray{T}} where {T} From c5ad3f34d984c7d481c1a5d3d33303de5079207f Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 10 Jun 2021 05:01:24 +0100 Subject: [PATCH 32/78] Drop -p N for DistributedStream.jl CI --- .github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index f666150..0d7bfd1 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -20,7 +20,7 @@ jobs: run: julia --threads 2 --project src/ThreadedStream.jl --arraysize 100 - name: Test run DistributedStream.jl if: ${{ ! cancelled() }} - run: julia -p2 --project src/DistributedStream.jl --arraysize 100 + run: julia --project src/DistributedStream.jl --arraysize 100 - name: Test run CUDAStream.jl if: ${{ ! cancelled() }} run: julia --project src/CUDAStream.jl --list From d799535c966364a7209ec40c4ca8d2f3093e936b Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 10 Jun 2021 05:06:48 +0100 Subject: [PATCH 33/78] Larger arraysize for CI --- .github/workflows/main.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 0d7bfd1..d54850e 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -14,13 +14,13 @@ jobs: run: julia --project -e 'import Pkg; Pkg.instantiate()' - name: Test run PlainStream.jl if: ${{ ! cancelled() }} - run: julia --project src/PlainStream.jl --arraysize 100 + run: julia --project src/PlainStream.jl --arraysize 2048 - name: Test run ThreadedStream.jl if: ${{ ! cancelled() }} - run: julia --threads 2 --project src/ThreadedStream.jl --arraysize 100 + run: julia --threads 2 --project src/ThreadedStream.jl --arraysize 2048 - name: Test run DistributedStream.jl if: ${{ ! cancelled() }} - run: julia --project src/DistributedStream.jl --arraysize 100 + run: julia --project src/DistributedStream.jl --arraysize 2048 - name: Test run CUDAStream.jl if: ${{ ! cancelled() }} run: julia --project src/CUDAStream.jl --list From fdb2c181cc618319f7af4982a2d997eb24fb781a Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Tue, 15 Jun 2021 23:13:14 +0100 Subject: [PATCH 34/78] Add Crossbeam implementation Add rustfmt and use target-cpu=native Add option for libc malloc, basic thread pinning, touch-free allocation Split modules --- rust-stream/.cargo/config.toml | 2 + rust-stream/Cargo.lock | 277 +++++++++- rust-stream/Cargo.toml | 20 +- rust-stream/README.md | 72 +++ rust-stream/rustfmt.toml | 68 +++ rust-stream/src/crossbeam_stream.rs | 223 ++++++++ rust-stream/src/main.rs | 767 ++++++++++++++-------------- rust-stream/src/plain_stream.rs | 61 +++ rust-stream/src/rayon_stream.rs | 77 +++ rust-stream/src/stream.rs | 166 ++++++ 10 files changed, 1322 insertions(+), 411 deletions(-) create mode 100644 rust-stream/.cargo/config.toml create mode 100644 rust-stream/README.md create mode 100644 rust-stream/rustfmt.toml create mode 100644 rust-stream/src/crossbeam_stream.rs create mode 100644 rust-stream/src/plain_stream.rs create mode 100644 rust-stream/src/rayon_stream.rs create mode 100644 rust-stream/src/stream.rs diff --git a/rust-stream/.cargo/config.toml b/rust-stream/.cargo/config.toml new file mode 100644 index 0000000..d5135e9 --- /dev/null +++ b/rust-stream/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +rustflags = ["-C", "target-cpu=native"] \ No newline at end of file diff --git a/rust-stream/Cargo.lock b/rust-stream/Cargo.lock index 66addaa..eec5b71 100644 --- a/rust-stream/Cargo.lock +++ b/rust-stream/Cargo.lock @@ -1,12 +1,14 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -17,7 +19,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -54,10 +56,45 @@ dependencies = [ ] [[package]] -name = "crossbeam-channel" -version = "0.5.0" +name = "colour" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" +checksum = "a27e4532f26f510c24bb8477d963c0c3ef27e293c3b2c507cccb0536d493201a" +dependencies = [ + "crossterm", +] + +[[package]] +name = "core_affinity" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f8a03115cc34fb0d7c321dd154a3914b3ca082ccc5c11d91bf7117dbbe7171f" +dependencies = [ + "kernel32-sys", + "libc", + "num_cpus", + "winapi 0.2.8", +] + +[[package]] +name = "crossbeam" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" +dependencies = [ + "cfg-if", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" dependencies = [ "cfg-if", "crossbeam-utils", @@ -76,9 +113,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" dependencies = [ "cfg-if", "crossbeam-utils", @@ -88,16 +125,50 @@ dependencies = [ ] [[package]] -name = "crossbeam-utils" -version = "0.8.3" +name = "crossbeam-queue" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" +checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" dependencies = [ - "autocfg", "cfg-if", "lazy_static", ] +[[package]] +name = "crossterm" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c36c10130df424b2f3552fcc2ddcd9b28a27b1e54b358b45874f88d1ca6888c" +dependencies = [ + "bitflags", + "crossterm_winapi", + "lazy_static", + "libc", + "mio", + "parking_lot", + "signal-hook", + "winapi 0.3.9", +] + +[[package]] +name = "crossterm_winapi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0da8964ace4d3e4a044fd027919b2237000b24315a37c916f61809f1ff2140b9" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "either" version = "1.6.1" @@ -106,9 +177,9 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "heck" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" dependencies = [ "unicode-segmentation", ] @@ -122,6 +193,25 @@ dependencies = [ "libc", ] +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -130,19 +220,68 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.91" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" +checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" + +[[package]] +name = "lock_api" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] [[package]] name = "memoffset" -version = "0.6.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" dependencies = [ "autocfg", ] +[[package]] +name = "mio" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi 0.3.9", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "num-traits" version = "0.2.14" @@ -162,6 +301,31 @@ dependencies = [ "libc", ] +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi 0.3.9", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -188,9 +352,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" dependencies = [ "unicode-xid", ] @@ -206,9 +370,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" dependencies = [ "autocfg", "crossbeam-deque", @@ -218,9 +382,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -229,22 +393,69 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "redox_syscall" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" +dependencies = [ + "bitflags", +] + [[package]] name = "rust-stream" version = "3.4.0" dependencies = [ + "colour", + "core_affinity", + "crossbeam", + "libc", "num-traits", + "num_cpus", "rayon", + "rustversion", "structopt", "tabular", ] +[[package]] +name = "rustversion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" + [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "signal-hook" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" +dependencies = [ + "libc", + "mio", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + [[package]] name = "strsim" version = "0.8.0" @@ -277,9 +488,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.64" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" +checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" dependencies = [ "proc-macro2", "quote", @@ -318,9 +529,9 @@ checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "vec_map" @@ -334,6 +545,12 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + [[package]] name = "winapi" version = "0.3.9" @@ -344,6 +561,12 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" diff --git a/rust-stream/Cargo.toml b/rust-stream/Cargo.toml index 35a2880..55db62b 100644 --- a/rust-stream/Cargo.toml +++ b/rust-stream/Cargo.toml @@ -10,4 +10,22 @@ edition = "2018" num-traits = "0.2.14" structopt = "0.3.13" tabular = "0.1.4" -rayon = "1.5" +rayon = "1.5.1" +crossbeam = "0.8.1" +num_cpus = "1.13.0" +rustversion = "1.0" +libc = "0.2.97" +core_affinity = "0.5.10" +colour = "0.6.0" + +[build-dependencies] +rustversion = "1.0" + +[profile.dev] +opt-level = 2 +overflow-checks = true + + +[profile.release] +opt-level = 3 +lto = "thin" # fully enabling this (i.e true) negatively affects performance as tested on both AMD and Intel diff --git a/rust-stream/README.md b/rust-stream/README.md new file mode 100644 index 0000000..ee9c056 --- /dev/null +++ b/rust-stream/README.md @@ -0,0 +1,72 @@ +rust-stream +=========== + +This is an implementation of BabelStream in Rust. + +Currently, we support three CPU threading API as devices: + +* Plain - basic single-threaded `for` version, see [plain_stream.rs](src/plain_stream.rs) +* [Rayon](https://github.com/rayon-rs/rayon) - Parallel with high level API, + see [rayon_stream.rs](src/rayon_stream.rs) +* [Crossbeam](https://github.com/crossbeam-rs/crossbeam) - Parallel with partitions per thread, + see [crossbeam_stream.rs](src/crossbeam_stream.rs) + +In addition, this implementation also supports the following extra flags: + +``` +--init Initialise each benchmark array at allocation time on the main thread +--malloc Use libc malloc instead of the Rust's allocator for benchmark array allocation +--pin Pin threads to distinct cores, this has NO effect in Rayon devices +``` + +There is an ongoing investigation on potential performance issues under NUMA situations. As part of +the experiment, this implementation made use of the +provisional [Allocator traits](https://github.com/rust-lang/rust/issues/32838) which requires rust +unstable. We hope a NUMA aware allocator will be available once the allocator API reaches rust +stable. + +### Build & Run + +Prerequisites: + +* [Rust toolchain](https://www.rust-lang.org/tools/install) + +Once the toolchain is installed, enable the nightly channel: + +```shell +> rustup install nightly +> rustup default nightly # optional, this sets `+nightly` automatically for cargo calls later +``` + +With `cargo` on path, compile and run the benchmark with: + +```shell +> cd rust-stream/ +> cargo +nightly build --release # or simply `cargo build --release` if nightly channel is the default +> ./target/release/rust-stream --help +rust-stream 3.4.0 + +USAGE: + rust-stream [FLAGS] [OPTIONS] + +FLAGS: + --csv Output as csv table + --float Use floats (rather than doubles) + -h, --help Prints help information + --init Initialise each benchmark array at allocation time on the main thread + --list List available devices + --malloc Use libc malloc instead of the Rust's allocator for benchmark array allocation + --mibibytes Use MiB=2^20 for bandwidth calculation (default MB=10^6) + --nstream-only Only run nstream + --pin Pin threads to distinct cores, this has NO effect in Rayon devices + --triad-only Only run triad + -V, --version Prints version information + +OPTIONS: + -s, --arraysize Use elements in the array [default: 33554432] + --device Select device at [default: 0] + -n, --numtimes Run the test times (NUM >= 2) [default: 100] +``` + + + \ No newline at end of file diff --git a/rust-stream/rustfmt.toml b/rust-stream/rustfmt.toml new file mode 100644 index 0000000..23c912d --- /dev/null +++ b/rust-stream/rustfmt.toml @@ -0,0 +1,68 @@ +max_width = 100 +hard_tabs = false +tab_spaces = 2 +newline_style = "Auto" +use_small_heuristics = "Max" +indent_style = "Block" +wrap_comments = false +format_code_in_doc_comments = false +comment_width = 80 +normalize_comments = false +normalize_doc_attributes = false +license_template_path = "" +format_strings = false +format_macro_matchers = false +format_macro_bodies = true +empty_item_single_line = true +struct_lit_single_line = true +fn_single_line = true +where_single_line = true +imports_indent = "Block" +imports_layout = "Mixed" +imports_granularity = "Preserve" +group_imports = "Preserve" +reorder_imports = true +reorder_modules = true +reorder_impl_items = false +type_punctuation_density = "Wide" +space_before_colon = false +space_after_colon = true +spaces_around_ranges = false +binop_separator = "Front" +remove_nested_parens = true +combine_control_expr = true +overflow_delimited_expr = false +struct_field_align_threshold = 0 +enum_discrim_align_threshold = 0 +match_arm_blocks = true +match_arm_leading_pipes = "Never" +force_multiline_blocks = false +fn_args_layout = "Compressed" +brace_style = "PreferSameLine" +control_brace_style = "AlwaysSameLine" +trailing_semicolon = true +trailing_comma = "Vertical" +match_block_trailing_comma = false +blank_lines_upper_bound = 1 +blank_lines_lower_bound = 0 +edition = "2015" +version = "One" +inline_attribute_width = 0 +merge_derives = true +use_try_shorthand = false +use_field_init_shorthand = false +force_explicit_abi = true +condense_wildcard_suffixes = false +color = "Auto" +required_version = "1.4.36" +unstable_features = false +disable_all_formatting = false +skip_children = false +hide_parse_errors = false +error_on_line_overflow = false +error_on_unformatted = false +report_todo = "Never" +report_fixme = "Never" +ignore = [] +emit_mode = "Files" +make_backup = false diff --git a/rust-stream/src/crossbeam_stream.rs b/rust-stream/src/crossbeam_stream.rs new file mode 100644 index 0000000..85b6503 --- /dev/null +++ b/rust-stream/src/crossbeam_stream.rs @@ -0,0 +1,223 @@ +use std::iter::Sum; +use std::slice::{Chunks, ChunksMut}; + +use crossbeam::thread; + +use self::core_affinity::CoreId; +use crate::stream::{AllocatorType, ArrayType, RustStream, StreamData}; + +pub struct ThreadedDevice { + pub(crate) ncore: usize, + pub(crate) pin: bool, + pub(crate) core_ids: Vec, +} + +impl ThreadedDevice { + pub fn new(ncore: usize, pin: bool) -> Self { + let mut core_ids = match core_affinity::get_core_ids() { + Some(xs) => xs, + None => { + colour::e_red_ln!("Cannot enumerate cores, pinning will not work if enabled"); + (0..ncore).map(|i| CoreId { id: i }).collect() + } + }; + core_ids.resize(ncore, core_ids[0]); + ThreadedDevice { ncore, pin, core_ids } + } +} + +impl ThreadedDevice { + // divide the length by the number of cores, the last core gets less work if it does not divide + fn chunk_size(&self, len: usize) -> usize { + (len as f64 / self.ncore as f64).ceil() as usize + } + + // make a mutable chunk from the vec + fn mk_mut_chunks<'a, T, A: AllocatorType>(&self, xs: &'a mut Vec) -> ChunksMut<'a, T> { + let len = xs.len(); + xs.chunks_mut(self.chunk_size(len)) + } + + // make a immutable chunk from the vec + fn mk_chunks<'a, T, A: AllocatorType>(&self, xs: &'a mut Vec) -> Chunks<'a, T> { + xs.chunks(self.chunk_size(xs.len())) + } +} + +extern crate core_affinity; + +// Crossbeam threaded version, it should be semantically equal to the single threaded version +impl RustStream + for StreamData +{ + fn init_arrays(&mut self) { + thread::scope(|s| { + let init = self.init; + let pin = self.device.pin; + for (t, ((a, b), c)) in self.device.core_ids.iter().zip( + self + .device + .mk_mut_chunks(&mut self.a) + .zip(self.device.mk_mut_chunks(&mut self.b)) + .zip(self.device.mk_mut_chunks(&mut self.c)), + ) { + s.spawn(move |_| { + if pin { + core_affinity::set_for_current(*t); + } + for x in a.into_iter() { + *x = init.0; + } + for x in b.into_iter() { + *x = init.1; + } + for x in c.into_iter() { + *x = init.2; + } + }); + } + }) + .unwrap() + } + + fn copy(&mut self) { + thread::scope(|s| { + let pin = self.device.pin; + for (t, (c, a)) in self + .device + .core_ids + .iter() + .zip(self.device.mk_mut_chunks(&mut self.c).zip(self.device.mk_chunks(&mut self.a))) + { + s.spawn(move |_| { + if pin { + core_affinity::set_for_current(*t); + } + for i in 0..c.len() { + c[i] = a[i]; + } + }); + } + }) + .unwrap() + } + + fn mul(&mut self) { + thread::scope(|s| { + let pin = self.device.pin; + let scalar = self.scalar; + for (t, (b, c)) in self + .device + .core_ids + .iter() + .zip(self.device.mk_mut_chunks(&mut self.b).zip(self.device.mk_chunks(&mut self.c))) + { + s.spawn(move |_| { + if pin { + core_affinity::set_for_current(*t); + } + for i in 0..b.len() { + b[i] = scalar * c[i]; + } + }); + } + }) + .unwrap() + } + + fn add(&mut self) { + thread::scope(|s| { + let pin = self.device.pin; + for (t, (c, (a, b))) in (&mut self.device.core_ids.iter()).zip( + self + .device + .mk_mut_chunks(&mut self.c) + .zip(self.device.mk_chunks(&mut self.a).zip(self.device.mk_chunks(&mut self.b))), + ) { + s.spawn(move |_| { + if pin { + core_affinity::set_for_current(*t); + } + for i in 0..c.len() { + c[i] = a[i] + b[i]; + } + }); + } + }) + .unwrap() + } + + fn triad(&mut self) { + thread::scope(|s| { + let pin = self.device.pin; + let scalar = self.scalar; + for (t, (a, (b, c))) in self.device.core_ids.iter().zip( + self + .device + .mk_mut_chunks(&mut self.a) + .zip(self.device.mk_chunks(&mut self.b).zip(self.device.mk_chunks(&mut self.c))), + ) { + s.spawn(move |_| { + if pin { + core_affinity::set_for_current(*t); + } + for i in 0..a.len() { + a[i] = b[i] + scalar * c[i] + } + }); + } + }) + .unwrap() + } + + fn nstream(&mut self) { + thread::scope(|s| { + let pin = self.device.pin; + let scalar = self.scalar; + for (t, (a, (b, c))) in self.device.core_ids.iter().zip( + self + .device + .mk_mut_chunks(&mut self.a) + .zip(self.device.mk_chunks(&mut self.b).zip(self.device.mk_chunks(&mut self.c))), + ) { + s.spawn(move |_| { + if pin { + core_affinity::set_for_current(*t); + } + for i in 0..a.len() { + a[i] += b[i] + scalar * c[i] + } + }); + } + }) + .unwrap() + } + + fn dot(&mut self) -> T { + let mut partial_sum = vec![T::zero(); self.device.ncore]; + thread::scope(|s| { + let pin = self.device.pin; + let a = &self.a; + let b = &self.b; + let chunk_indices = |i: usize| { + let chunk_size = self.device.chunk_size(self.size); + let start = i * chunk_size; + start..((start + chunk_size).min(self.size)) + }; + for (t, (n, acc)) in self.device.core_ids.iter().zip(partial_sum.iter_mut().enumerate()) { + s.spawn(move |_| { + if pin { + core_affinity::set_for_current(*t); + } + let mut p = T::zero(); + for i in chunk_indices(n) { + p += a[i] * b[i]; + } + *acc = p; + }); + } + }) + .unwrap(); + partial_sum.into_iter().sum() + } +} diff --git a/rust-stream/src/main.rs b/rust-stream/src/main.rs index 3e31fd9..8f8f43c 100644 --- a/rust-stream/src/main.rs +++ b/rust-stream/src/main.rs @@ -1,377 +1,257 @@ +#![feature(allocator_api)] +#![feature(vec_into_raw_parts)] + +use std::alloc::System; use std::fmt::{Debug, Display}; use std::iter::Sum; use std::mem::size_of; -use std::time::{Duration, Instant}; +use std::time::Duration; -use num_traits::{abs, NumAssign, Signed}; -use num_traits::real::Real; -use rayon::prelude::*; +use num_traits::abs; use structopt::StructOpt; use tabular::{Row, Table}; +use crate::crossbeam_stream::ThreadedDevice; +use crate::plain_stream::SerialDevice; +use crate::rayon_stream::RayonDevice; +use crate::stream::{AllocatorType, ArrayType, RustStream, StreamData}; + +mod crossbeam_stream; +mod plain_stream; +mod rayon_stream; +mod stream; + #[derive(Debug, StructOpt)] struct Options { - /// List available devices - #[structopt(long)] list: bool, - /// Select device at - #[structopt(long, default_value = "0")] device: usize, - /// Run the test times (NUM >= 2) - #[structopt(long, default_value = "100")] numtimes: usize, - /// Use elements in the array - #[structopt(long, default_value = "33554432")] arraysize: usize, - /// Use floats (rather than doubles) - #[structopt(long)] float: bool, - /// Only run triad - #[structopt(long)] triad_only: bool, - /// Only run nstream - #[structopt(long)] nstream_only: bool, - /// Output as csv table - #[structopt(long)] csv: bool, - /// Use MiB=2^20 for bandwidth calculation (default MB=10^6) - #[structopt(long)] mibibytes: bool, + /// List available devices + #[structopt(long)] + list: bool, + /// Select device at + #[structopt(long, default_value = "0")] + device: usize, + /// Run the test times (NUM >= 2) + #[structopt(long, short = "n", default_value = "100")] + numtimes: usize, + /// Use elements in the array + #[structopt(long, short = "s", default_value = "33554432")] + arraysize: usize, + /// Use floats (rather than doubles) + #[structopt(long)] + float: bool, + /// Only run triad + #[structopt(long)] + triad_only: bool, + /// Only run nstream + #[structopt(long)] + nstream_only: bool, + /// Output as csv table + #[structopt(long)] + csv: bool, + /// Use MiB=2^20 for bandwidth calculation (default MB=10^6) + #[structopt(long)] + mibibytes: bool, + /// Use libc malloc instead of the Rust's allocator for benchmark array allocation + #[structopt(name = "malloc", long)] + malloc: bool, + /// Initialise each benchmark array at allocation time on the main thread + #[structopt(name = "init", long)] + init: bool, + /// Pin threads to distinct cores, this has NO effect in Rayon devices + #[structopt(long)] + pin: bool, } #[derive(PartialEq)] -enum Benchmark { All, Triad, NStream } - -struct StreamData { - size: usize, - scalar: T, - a: Vec, - b: Vec, - c: Vec, +enum Benchmark { + All, + Triad, + NStream, } -impl StreamData { - pub fn new(size: usize, scalar: T) -> StreamData { - StreamData { - size, - scalar, - a: vec![T::default(); size], - b: vec![T::default(); size], - c: vec![T::default(); size], - } - } -} - -struct PlainFor; - -struct RayonPar; - -#[inline(always)] -fn timed(f: F) -> Duration { - let start = Instant::now(); - f(); - start.elapsed() -} - -#[inline(always)] -fn timed_mut T>(f: &mut F) -> (Duration, T) { - let start = Instant::now(); - let x = f(); - (start.elapsed(), x) -} - -struct AllTiming { copy: T, mul: T, add: T, triad: T, dot: T } - -trait RustStream { - fn init_arrays(&mut self, init: (T, T, T)); - fn copy(&mut self); - fn mul(&mut self); - fn add(&mut self); - fn triad(&mut self); - fn nstream(&mut self); - fn dot(&mut self) -> T; - - fn run_all(&mut self, n: usize) -> (AllTiming>, T) { - let mut timings: AllTiming> = AllTiming { - copy: vec![Duration::default(); n], - mul: vec![Duration::default(); n], - add: vec![Duration::default(); n], - triad: vec![Duration::default(); n], - dot: vec![Duration::default(); n], - }; - let mut last_sum = T::default(); - for i in 0..n { - timings.copy[i] = timed(|| self.copy()); - timings.mul[i] = timed(|| self.mul()); - timings.add[i] = timed(|| self.add()); - timings.triad[i] = timed(|| self.triad()); - let (dot, sum) = timed_mut(&mut || self.dot()); - timings.dot[i] = dot; - last_sum = sum; - } - (timings, last_sum) - } - - fn run_triad(&mut self, n: usize) -> Duration { - timed(|| for _ in 0..n { self.triad(); }) - } - - fn run_nstream(&mut self, n: usize) -> Vec { - (0..n).map(|_| timed(|| self.nstream())).collect::>() - } -} - -trait ArrayType: Real + NumAssign + Signed + Default {} - -impl ArrayType for T {} - -// single threaded version -impl RustStream for StreamData { - fn init_arrays(&mut self, init: (T, T, T)) { - self.a.fill(init.0); - self.b.fill(init.1); - self.c.fill(init.2); - } - - fn copy(&mut self) { - for i in 0..self.size { - self.c[i] = self.a[i]; - } - } - - fn mul(&mut self) { - for i in 0..self.size { - self.b[i] = self.scalar * self.c[i]; - } - } - - fn add(&mut self) { - for i in 0..self.size { - self.c[i] = self.a[i] + self.b[i]; - } - } - - fn triad(&mut self) { - for i in 0..self.size { - self.a[i] = self.b[i] + self.scalar * self.c[i]; - } - } - - fn nstream(&mut self) { - for i in 0..self.size { - self.a[i] += self.b[i] * self.scalar * self.c[i]; - } - } - - fn dot(&mut self) -> T { - let mut sum: T = T::default(); - for i in 0..self.size { - sum += self.a[i] * self.b[i]; - } - sum - } -} - -// Rayon version, it should be semantically equal to the single threaded version -impl RustStream for StreamData { - fn init_arrays(&mut self, init: (T, T, T)) { - self.a.fill(init.0); - self.b.fill(init.1); - self.c.fill(init.2); - } - - fn copy(&mut self) { - let a = &self.a; - self.c.par_iter_mut().enumerate().for_each(|(i, c)| *c = a[i]) - } - - fn mul(&mut self) { - let c = &self.c; - let scalar = &self.scalar; - self.b.par_iter_mut().enumerate().for_each(|(i, b)| *b = *scalar * c[i]) - } - - fn add(&mut self) { - let a = &self.a; - let b = &self.b; - self.c.par_iter_mut().enumerate().for_each(|(i, c)| *c = a[i] + b[i]) - } - - fn triad(&mut self) { - let scalar = &self.scalar; - let b = &self.b; - let c = &self.c; - self.a.par_iter_mut().enumerate().for_each(|(i, a)| *a = b[i] + *scalar * c[i]) - } - - fn nstream(&mut self) { - let scalar = &self.scalar; - let b = &self.b; - let c = &self.c; - self.a.par_iter_mut().enumerate().for_each(|(i, a)| *a += b[i] + *scalar * c[i]) - } - - fn dot(&mut self) -> T { - let a = &self.a; - let b = &self.b; - (0..self.size).into_par_iter().fold(|| T::default(), |acc, i| acc + a[i] * b[i]).sum::() - } -} - -fn validate>( - benchmark: Benchmark, - numtimes: usize, - vec: &StreamData, - dot_sum: Option, - scalar: T, init: (T, T, T)) { - let (mut gold_a, mut gold_b, mut gold_c) = init; - for _ in 0..numtimes { - match benchmark { - Benchmark::All => { - gold_c = gold_a; - gold_b = scalar * gold_c; - gold_c = gold_a + gold_b; - gold_a = gold_b + scalar * gold_c; - } - Benchmark::Triad => { - gold_a = gold_b + scalar * gold_c; - } - Benchmark::NStream => { - gold_a += gold_b + scalar * gold_c; - } - }; - } - let tolerance = T::epsilon().into() * 100.0f64; - let validate_xs = |name: &str, xs: &Vec, from: T| { - let error = (xs.iter().map(|x| abs(*x - from)).sum::()).into() / xs.len() as f64; - if error > tolerance { - eprintln!("Validation failed on {}[]. Average error {} ", name, error) - } - }; - validate_xs("a", &vec.a, gold_a); - validate_xs("b", &vec.b, gold_b); - validate_xs("c", &vec.c, gold_c); - - if let Some(sum) = dot_sum { - let gold_sum = (gold_a * gold_b).into() * vec.size as f64; - let error = abs((sum.into() - gold_sum) / gold_sum); - if error > 1.0e-8 { - eprintln!("Validation failed on sum. Error {} \nSum was {} but should be {}", error, sum, gold_sum); - } - } -} - -fn run_cpu + Display>(option: Options, scalar: T, init: (T, T, T)) { - let benchmark = match (option.nstream_only, option.triad_only) { - (true, false) => Benchmark::NStream, - (false, true) => Benchmark::Triad, - (false, false) => Benchmark::All, - (true, true) => panic!("Both triad and nstream are enabled, pick one or omit both to run all benchmarks"), - }; - - let array_bytes = option.arraysize * size_of::(); - let total_bytes = array_bytes * 3; - let (mega_scale, mega_suffix, giga_scale, giga_suffix) = - if !option.mibibytes { (1.0e-6, "MB", 1.0e-9, "GB") } else { (2f64.powi(-20), "MiB", 2f64.powi(-30), "GiB") }; - - if !option.csv { - println!("Running {} {} times", match benchmark { - Benchmark::All => "kernels", - Benchmark::Triad => "triad", - Benchmark::NStream => "nstream", - }, option.numtimes); - - if benchmark == Benchmark::Triad { - println!("Number of elements: {}", option.arraysize); - } - - println!("Precision: {}", if option.float { "float" } else { "double" }); - println!("Array size: {:.1} {}(={:.1} {})", - mega_scale * array_bytes as f64, mega_suffix, giga_scale * array_bytes as f64, giga_suffix); - println!("Total size: {:.1} {}(={:.1} {})", - mega_scale * total_bytes as f64, mega_suffix, giga_scale * total_bytes as f64, giga_suffix); - } - - - let mut vec: StreamData = StreamData::::new(option.arraysize, scalar); - let stream = &mut vec as &mut dyn RustStream; - stream.init_arrays(init); - - let tabulate = |xs: &Vec, name: &str, t_size: usize| -> Vec<(&str, String)> { - let tail = &xs[1..]; // tail only - // do stats - let max = tail.iter().max().map(|d| d.as_secs_f64()); - let min = tail.iter().min().map(|d| d.as_secs_f64()); - match (min, max) { - (Some(min), Some(max)) => { - let avg: f64 = tail.iter().map(|d| d.as_secs_f64()).sum::() / tail.len() as f64; - let mbps = mega_scale * (t_size as f64) / min; - if option.csv { - vec![ - ("function", name.to_string()), - ("num_times", option.numtimes.to_string()), - ("n_elements", option.arraysize.to_string()), - ("sizeof", t_size.to_string()), - (if option.mibibytes { "max_mibytes_per_sec" } else { "max_mbytes_per_sec" }, mbps.to_string()), - ("min_runtime", min.to_string()), - ("max_runtime", max.to_string()), - ("avg_runtime", avg.to_string()), - ] - } else { - vec![ - ("Function", name.to_string()), - (if option.mibibytes { "MiBytes/sec" } else { "MBytes/sec" }, format!("{:.3}", mbps)), - ("Min (sec)", format!("{:.5}", min)), - ("Max", format!("{:.5}", max)), - ("Average", format!("{:.5}", avg)), - ] - } - } - (_, _) => panic!("No min/max element for {}(size={})", name, t_size) - } - }; - - let tabulate_all = |xs: Vec>| { - match xs.as_slice() { - [head, .. ] => { - if option.csv { - println!("{}", head.iter().map(|(col, _)| *col).collect::>().join(",")); - for kvs in xs { - println!("{}", kvs.iter().map(|(_, val)| val.clone()).collect::>().join(",")); - } - } else { - let mut table = Table::new(&vec!["{:<}"; head.len()].join(" ")); - table.add_row(head.iter().fold(Row::new(), |row, (col, _)| row.with_cell(col))); - for kvs in xs { - table.add_row(kvs.iter().fold(Row::new(), |row, (_, val)| row.with_cell(val))); - } - println!("{}", table); - } - } - _ => panic!("Empty tabulation") - }; - }; - +fn check_solution, D, A: AllocatorType>( + benchmark: Benchmark, numtimes: usize, vec: &StreamData, dot_sum: Option, +) { + let (mut gold_a, mut gold_b, mut gold_c) = vec.init; + for _ in 0..numtimes { match benchmark { - Benchmark::All => { - let (results, sum) = stream.run_all(option.numtimes); - validate(benchmark, option.numtimes, &vec, Some(sum), scalar, init); - tabulate_all(vec![ - tabulate(&results.copy, "Copy", 2 * array_bytes), - tabulate(&results.mul, "Mul", 2 * array_bytes), - tabulate(&results.add, "Add", 3 * array_bytes), - tabulate(&results.triad, "Triad", 3 * array_bytes), - tabulate(&results.dot, "Dot", 2 * array_bytes), - ]) - } - Benchmark::NStream => { - let results = stream.run_nstream(option.numtimes); - validate(benchmark, option.numtimes, &vec, None, scalar, init); - tabulate_all(vec![ - tabulate(&results, "Nstream", 4 * array_bytes) - ]); - } - Benchmark::Triad => { - let results = stream.run_triad(option.numtimes); - let total_bytes = 3 * array_bytes * option.numtimes; - let bandwidth = mega_scale * (total_bytes as f64 / results.as_secs_f64()); - - println!("Runtime (seconds): {:.5}", results.as_secs_f64()); - println!("Bandwidth ({}/s): {:.3} ", giga_suffix, bandwidth); - } + Benchmark::All => { + gold_c = gold_a; + gold_b = vec.scalar * gold_c; + gold_c = gold_a + gold_b; + gold_a = gold_b + vec.scalar * gold_c; + } + Benchmark::Triad => { + gold_a = gold_b + vec.scalar * gold_c; + } + Benchmark::NStream => { + gold_a += gold_b + vec.scalar * gold_c; + } }; + } + let tolerance = T::epsilon().into() * 100.0f64; + let validate_xs = |name: &str, xs: &Vec, from: T| { + let error = (xs.iter().map(|x| abs(*x - from)).sum::()).into() / xs.len() as f64; + if error > tolerance { + eprintln!("Validation failed on {}[]. Average error {} ", name, error) + } + }; + validate_xs("a", &vec.a, gold_a); + validate_xs("b", &vec.b, gold_b); + validate_xs("c", &vec.c, gold_c); + + if let Some(sum) = dot_sum { + let gold_sum = (gold_a * gold_b).into() * vec.size as f64; + let error = abs((sum.into() - gold_sum) / gold_sum); + if error > 1.0e-8 { + eprintln!( + "Validation failed on sum. Error {} \nSum was {} but should be {}", + error, sum, gold_sum + ); + } + } +} + +fn run_cpu + Display, D, A: AllocatorType>( + option: &Options, mut stream: StreamData, +) where + StreamData: RustStream, +{ + let benchmark = match (option.nstream_only, option.triad_only) { + (true, false) => Benchmark::NStream, + (false, true) => Benchmark::Triad, + (false, false) => Benchmark::All, + (true, true) => { + panic!("Both triad and nstream are enabled, pick one or omit both to run all benchmarks") + } + }; + + let array_bytes = option.arraysize * size_of::(); + let total_bytes = array_bytes * 3; + let (mega_scale, mega_suffix, giga_scale, giga_suffix) = if !option.mibibytes { + (1.0e-6, "MB", 1.0e-9, "GB") + } else { + (2f64.powi(-20), "MiB", 2f64.powi(-30), "GiB") + }; + + if !option.csv { + println!( + "Running {} {} times", + match benchmark { + Benchmark::All => "kernels", + Benchmark::Triad => "triad", + Benchmark::NStream => "nstream", + }, + option.numtimes + ); + + if benchmark == Benchmark::Triad { + println!("Number of elements: {}", option.arraysize); + } + + println!("Precision: {}", if option.float { "float" } else { "double" }); + println!( + "Array size: {:.1} {} (={:.1} {})", + mega_scale * array_bytes as f64, + mega_suffix, + giga_scale * array_bytes as f64, + giga_suffix + ); + println!( + "Total size: {:.1} {} (={:.1} {})", + mega_scale * total_bytes as f64, + mega_suffix, + giga_scale * total_bytes as f64, + giga_suffix + ); + } + + stream.init_arrays(); + + let tabulate = |xs: &Vec, name: &str, t_size: usize| -> Vec<(&str, String)> { + let tail = &xs[1..]; // tail only + // do stats + let max = tail.iter().max().map(|d| d.as_secs_f64()); + let min = tail.iter().min().map(|d| d.as_secs_f64()); + match (min, max) { + (Some(min), Some(max)) => { + let avg: f64 = tail.iter().map(|d| d.as_secs_f64()).sum::() / tail.len() as f64; + let mbps = mega_scale * (t_size as f64) / min; + if option.csv { + vec![ + ("function", name.to_string()), + ("num_times", option.numtimes.to_string()), + ("n_elements", option.arraysize.to_string()), + ("sizeof", t_size.to_string()), + ( + if option.mibibytes { "max_mibytes_per_sec" } else { "max_mbytes_per_sec" }, + mbps.to_string(), + ), + ("min_runtime", min.to_string()), + ("max_runtime", max.to_string()), + ("avg_runtime", avg.to_string()), + ] + } else { + vec![ + ("Function", name.to_string()), + (if option.mibibytes { "MiBytes/sec" } else { "MBytes/sec" }, format!("{:.3}", mbps)), + ("Min (sec)", format!("{:.5}", min)), + ("Max", format!("{:.5}", max)), + ("Average", format!("{:.5}", avg)), + ] + } + } + (_, _) => panic!("No min/max element for {}(size={})", name, t_size), + } + }; + + let tabulate_all = |xs: Vec>| { + match xs.as_slice() { + [head, ..] => { + if option.csv { + println!("{}", head.iter().map(|(col, _)| *col).collect::>().join(",")); + for kvs in xs { + println!("{}", kvs.iter().map(|(_, val)| val.clone()).collect::>().join(",")); + } + } else { + let mut table = Table::new(&vec!["{:<}"; head.len()].join(" ")); + table.add_row(head.iter().fold(Row::new(), |row, (col, _)| row.with_cell(col))); + for kvs in xs { + table.add_row(kvs.iter().fold(Row::new(), |row, (_, val)| row.with_cell(val))); + } + print!("{}", table); + } + } + _ => panic!("Empty tabulation"), + }; + }; + + match benchmark { + Benchmark::All => { + let (results, sum) = stream.run_all(option.numtimes); + check_solution(benchmark, option.numtimes, &stream, Some(sum)); + tabulate_all(vec![ + tabulate(&results.copy, "Copy", 2 * array_bytes), + tabulate(&results.mul, "Mul", 2 * array_bytes), + tabulate(&results.add, "Add", 3 * array_bytes), + tabulate(&results.triad, "Triad", 3 * array_bytes), + tabulate(&results.dot, "Dot", 2 * array_bytes), + ]) + } + Benchmark::NStream => { + let results = stream.run_nstream(option.numtimes); + check_solution(benchmark, option.numtimes, &stream, None); + tabulate_all(vec![tabulate(&results, "Nstream", 4 * array_bytes)]); + } + Benchmark::Triad => { + let results = stream.run_triad(option.numtimes); + let total_bytes = 3 * array_bytes * option.numtimes; + let bandwidth = giga_scale * (total_bytes as f64 / results.as_secs_f64()); + + println!("Runtime (seconds): {:.5}", results.as_secs_f64()); + println!("Bandwidth ({}/s): {:.3} ", giga_suffix, bandwidth); + } + }; + &stream.clean_up(); } const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); @@ -381,33 +261,154 @@ static START_B: f32 = 0.2; static START_C: f32 = 0.0; static START_SCALAR: f32 = 0.4; +static FLOAT_INIT_SCALAR: f32 = START_SCALAR; +static FLOAT_INIT: (f32, f32, f32) = (START_A, START_B, START_C); + +static DOUBLE_START_SCALAR: f64 = START_SCALAR as f64; +static DOUBLE_INIT: (f64, f64, f64) = (START_A as f64, START_B as f64, START_C as f64); + fn main() { - let options: Options = Options::from_args(); + let options: Options = Options::from_args(); - // only CPU via Rayon for now - let devices = vec![("CPU (Rayon)", |opt: Options| { - if opt.float { - run_cpu::(opt, START_SCALAR, (START_A, START_B, START_C)); - } else { - run_cpu::(opt, START_SCALAR.into(), (START_A.into(), START_B.into(), START_C.into())); - } - })]; + if options.numtimes < 2 { + panic!("numtimes must be >= 2") + } - if options.list { - devices.iter().enumerate().for_each(|(i, (name, _))| { - println!("{}: {}", i, name); - }) - } else { - match devices.get(options.device) { - Some((_, run)) => { - if !&options.csv { - println!("BabelStream\n\ - Version: {}\n\ - Implementation: Rust+Rayon", VERSION.unwrap_or("unknown")) - } - run(options); - } - None => eprintln!("Device index({}) not available", options.device) - } + let alloc = System; + let alloc_name = if options.malloc { "libc-malloc" } else { "rust-system" }; + + let rayon_device = &|| { + let dev = RayonDevice { pool: rayon::ThreadPoolBuilder::default().build().unwrap() }; + if !options.csv { + println!("Using {} thread(s), alloc={}", dev.pool.current_num_threads(), alloc_name); + if options.pin { + colour::e_yellow_ln!("Pinning threads have no effect on Rayon!") + } } + if options.float { + run_cpu( + &options, + StreamData::new_in( + options.arraysize, + FLOAT_INIT_SCALAR, + FLOAT_INIT, + dev, + alloc, + options.malloc, + options.init, + ), + ); + } else { + run_cpu( + &options, + StreamData::new_in( + options.arraysize, + DOUBLE_START_SCALAR, + DOUBLE_INIT, + dev, + alloc, + options.malloc, + options.init, + ), + ); + } + }; + + let crossbeam_device = &|| { + let ncores = num_cpus::get(); + let dev = ThreadedDevice::new(ncores, options.pin); + if !options.csv { + println!("Using {} thread(s), pin={}, alloc={}", ncores, options.pin, alloc_name) + } + if options.float { + run_cpu( + &options, + StreamData::new_in( + options.arraysize, + FLOAT_INIT_SCALAR, + FLOAT_INIT, + dev, + alloc, + options.malloc, + options.init, + ), + ); + } else { + run_cpu( + &options, + StreamData::new_in( + options.arraysize, + DOUBLE_START_SCALAR, + DOUBLE_INIT, + dev, + alloc, + options.malloc, + options.init, + ), + ); + } + }; + let st_device = &|| { + let dev = SerialDevice { pin: options.pin }; + if !options.csv { + println!("Using 1 thread, pin={}, alloc={}", options.pin, alloc_name); + } + if options.float { + run_cpu( + &options, + StreamData::new_in( + options.arraysize, + FLOAT_INIT_SCALAR, + FLOAT_INIT, + dev, + alloc, + options.malloc, + options.init, + ), + ); + } else { + run_cpu( + &options, + StreamData::new_in( + options.arraysize, + DOUBLE_START_SCALAR, + DOUBLE_INIT, + dev, + alloc, + options.malloc, + options.init, + ), + ); + } + }; + let devices: Vec<(String, &'_ dyn Fn())> = vec![ + ("CPU (Rayon)".to_string(), rayon_device), + (format!("CPU (Crossbeam, pinning={})", options.pin), crossbeam_device), + ("CPU (Single threaded)".to_string(), st_device), + ]; + + if options.list { + devices.iter().enumerate().for_each(|(i, (name, _))| { + println!("[{}] {}", i, name); + }) + } else { + match devices.get(options.device) { + Some((name, run)) => { + if !&options.csv { + println!( + "BabelStream\n\ + Version: {}\n\ + Implementation: Rust; {}", + VERSION.unwrap_or("unknown"), + name + ); + if options.init { + println!("Initialising arrays on main thread"); + } + } + run(); + } + None => eprintln!("Device index {} not available", options.device), + } + } } diff --git a/rust-stream/src/plain_stream.rs b/rust-stream/src/plain_stream.rs new file mode 100644 index 0000000..7a1fb01 --- /dev/null +++ b/rust-stream/src/plain_stream.rs @@ -0,0 +1,61 @@ +use crate::stream::{AllocatorType, ArrayType, RustStream, StreamData}; +use core_affinity::CoreId; + +pub struct SerialDevice { + pub(crate) pin: bool, +} + +// single threaded version +impl RustStream for StreamData { + fn init_arrays(&mut self) { + if self.device.pin { + core_affinity::set_for_current( + match core_affinity::get_core_ids().as_ref().map(|x| x.first()) { + Some(Some(x)) => *x, + _ => CoreId { id: 0 }, + }, + ); + } + self.a.fill(self.init.0); + self.b.fill(self.init.1); + self.c.fill(self.init.2); + } + + fn copy(&mut self) { + for i in 0..self.size { + self.c[i] = self.a[i]; + } + } + + fn mul(&mut self) { + for i in 0..self.size { + self.b[i] = self.scalar * self.c[i]; + } + } + + fn add(&mut self) { + for i in 0..self.size { + self.c[i] = self.a[i] + self.b[i]; + } + } + + fn triad(&mut self) { + for i in 0..self.size { + self.a[i] = self.b[i] + self.scalar * self.c[i]; + } + } + + fn nstream(&mut self) { + for i in 0..self.size { + self.a[i] += self.b[i] * self.scalar * self.c[i]; + } + } + + fn dot(&mut self) -> T { + let mut sum = T::default(); + for i in 0..self.size { + sum += self.a[i] * self.b[i]; + } + sum + } +} diff --git a/rust-stream/src/rayon_stream.rs b/rust-stream/src/rayon_stream.rs new file mode 100644 index 0000000..d25d115 --- /dev/null +++ b/rust-stream/src/rayon_stream.rs @@ -0,0 +1,77 @@ +use std::iter::Sum; + +use rayon::prelude::*; +use rayon::ThreadPool; + +use crate::stream::{AllocatorType, ArrayType, RustStream, StreamData}; + +pub struct RayonDevice { + pub(crate) pool: ThreadPool, +} + +// Rayon version, it should be semantically equal to the single threaded version +impl RustStream + for StreamData +{ + fn init_arrays(&mut self) { + let init = self.init; + self.a.par_iter_mut().for_each(|v| *v = init.0); + self.b.par_iter_mut().for_each(|v| *v = init.1); + self.c.par_iter_mut().for_each(|v| *v = init.2); + } + + fn copy(&mut self) { + let a = &self.a; + let c = &mut self.c; + self.device.pool.install(|| { + (*c).par_iter_mut().enumerate().for_each(|(i, c)| *c = a[i]); + }); + } + + fn mul(&mut self) { + let scalar = self.scalar; + let c = &self.c; + let b = &mut self.b; + self + .device + .pool + .install(|| (*b).par_iter_mut().enumerate().for_each(|(i, b)| *b = scalar * c[i])); + } + + fn add(&mut self) { + let a = &self.a; + let b = &self.b; + let c = &mut self.c; + self.device.pool.install(|| (*c).par_iter_mut().enumerate().for_each(|(i, c)| *c = a[i] + b[i])) + } + + fn triad(&mut self) { + let scalar = self.scalar; + let a = &mut self.a; + let b = &self.b; + let c = &self.c; + self + .device + .pool + .install(|| (*a).par_iter_mut().enumerate().for_each(|(i, a)| *a = b[i] + scalar * c[i])) + } + + fn nstream(&mut self) { + let scalar = self.scalar; + let a = &mut self.a; + let b = &self.b; + let c = &self.c; + self + .device + .pool + .install(|| (*a).par_iter_mut().enumerate().for_each(|(i, a)| *a += b[i] + scalar * c[i])) + } + + fn dot(&mut self) -> T { + let a = &self.a; + let b = &self.b; + self.device.pool.install(|| { + (0..self.size).into_par_iter().fold(|| T::default(), |acc, i| acc + a[i] * b[i]).sum::() + }) + } +} diff --git a/rust-stream/src/stream.rs b/rust-stream/src/stream.rs new file mode 100644 index 0000000..1568d4a --- /dev/null +++ b/rust-stream/src/stream.rs @@ -0,0 +1,166 @@ +use num_traits::real::Real; +use num_traits::{NumAssign, Signed}; +use std::alloc::Allocator; +use std::fmt::Debug; +use std::time::{Duration, Instant}; + +pub trait AllocatorType: Allocator + Copy + Clone + Default + Debug {} +impl AllocatorType for T {} + +pub struct StreamData { + pub device: D, + pub size: usize, + pub scalar: T, + pub init: (T, T, T), + pub a: Vec, + pub b: Vec, + pub c: Vec, + pub needs_dealloc: bool, +} + +#[inline(always)] +fn timed(f: F) -> Duration { + let start = Instant::now(); + f(); + start.elapsed() +} + +#[inline(always)] +fn timed_mut T>(f: &mut F) -> (Duration, T) { + let start = Instant::now(); + let x = f(); + (start.elapsed(), x) +} + +pub struct AllTiming { + pub copy: T, + pub mul: T, + pub add: T, + pub triad: T, + pub dot: T, +} + +pub trait ArrayType: Real + NumAssign + Signed + Default + Debug {} +impl ArrayType for T {} + +impl StreamData { + pub fn new_in( + size: usize, + scalar: T, + init: (T, T, T), + device: D, + allocator: A, + malloc: bool, // + initialise: bool, // + ) -> StreamData { + let mk_vec = || { + if malloc { + extern crate libc; + use std::mem; + unsafe { + // we do the typical C malloc with a NULL check here + let bytes = mem::size_of::() * size; + let ptr = libc::malloc(bytes as libc::size_t) as *mut T; + if ptr.is_null() { + panic!( + "Cannot allocate {} bytes in `sizeof(T) * size` (T = {}, size = {})", + bytes, + mem::size_of::(), + size + ); + } + let mut xs = Vec::from_raw_parts_in(ptr, size, size, allocator); + if initialise { + xs.fill(T::default()); + } + xs + } + } else { + if initialise { + let mut xs = Vec::new_in(allocator); + xs.resize(size, T::default()); + xs + } else { + // try not to touch the vec after allocation + let mut xs = Vec::with_capacity_in(size, allocator); + unsafe { + xs.set_len(size); + } + xs + } + } + }; + + StreamData { + device, + size, + scalar, + init, + a: mk_vec(), + b: mk_vec(), + c: mk_vec(), + needs_dealloc: malloc, + } + } + pub fn clean_up(self) { + if self.needs_dealloc { + unsafe { + extern crate libc; + let free_ts = move |xs: Vec| { + // make sure we don't call dealloc for vec anymore + // XXX it's important we don't free xs.as_mut_ptr() here and use xs.into_raw_parts_with_alloc() + // as that function handles drops semantic for us + // if we free the the raw ptr directly, the compiler will still drop the vec and then segfault + let (ptr, _, _, _) = xs.into_raw_parts_with_alloc(); + libc::free(ptr as *mut libc::c_void); + }; + free_ts(self.a); + free_ts(self.b); + free_ts(self.c); + } + } + } +} + +pub trait RustStream { + fn init_arrays(&mut self); + fn copy(&mut self); + fn mul(&mut self); + fn add(&mut self); + fn triad(&mut self); + fn nstream(&mut self); + fn dot(&mut self) -> T; + + fn run_all(&mut self, n: usize) -> (AllTiming>, T) { + let mut timings: AllTiming> = AllTiming { + copy: vec![Duration::default(); n], + mul: vec![Duration::default(); n], + add: vec![Duration::default(); n], + triad: vec![Duration::default(); n], + dot: vec![Duration::default(); n], + }; + let mut last_sum = T::default(); + for i in 0..n { + timings.copy[i] = timed(|| self.copy()); + timings.mul[i] = timed(|| self.mul()); + timings.add[i] = timed(|| self.add()); + timings.triad[i] = timed(|| self.triad()); + let (dot, sum) = timed_mut(&mut || self.dot()); + timings.dot[i] = dot; + last_sum = sum; + } + (timings, last_sum) + } + + fn run_triad(&mut self, n: usize) -> Duration { + timed(|| { + for _ in 0..n { + self.triad(); + } + }) + } + + fn run_nstream(&mut self, n: usize) -> Vec { + (0..n).map(|_| timed(|| self.nstream())).collect::>() + } +} From ce4d6cfbfb0ecb898acf8218c836b6126dcb3bd9 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Wed, 16 Jun 2021 01:11:40 +0100 Subject: [PATCH 35/78] Add integration tests and CI Fix wrong nstream in plain_stream --- .github/workflows/main.yaml | 15 + rust-stream/Cargo.lock | 56 ++++ rust-stream/Cargo.toml | 3 + rust-stream/src/lib.rs | 430 ++++++++++++++++++++++++++ rust-stream/src/main.rs | 413 +------------------------ rust-stream/src/plain_stream.rs | 2 +- rust-stream/tests/integration_test.rs | 17 + 7 files changed, 524 insertions(+), 412 deletions(-) create mode 100644 rust-stream/src/lib.rs create mode 100644 rust-stream/tests/integration_test.rs diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 20e1034..274df60 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -3,6 +3,21 @@ on: [push, pull_request] jobs: + test-rust: + runs-on: ubuntu-18.04 + defaults: + run: + working-directory: ./rust-stream + steps: + - uses: actions/checkout@v2 + - name: Setup project + run: rustup install nightly + - name: Compile project + run: cargo +nightly build --release + - name: Test project + run: cargo +nightly test --release + - name: Test run project + run: ./target/release/rust-stream --arraysize 2048 test: runs-on: ubuntu-18.04 steps: diff --git a/rust-stream/Cargo.lock b/rust-stream/Cargo.lock index eec5b71..7c5ec13 100644 --- a/rust-stream/Cargo.lock +++ b/rust-stream/Cargo.lock @@ -326,6 +326,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -402,6 +411,19 @@ dependencies = [ "bitflags", ] +[[package]] +name = "rstest" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "041bb0202c14f6a158bbbf086afb03d0c6e975c2dec7d4912f8061ed44f290af" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + [[package]] name = "rust-stream" version = "3.4.0" @@ -413,11 +435,21 @@ dependencies = [ "num-traits", "num_cpus", "rayon", + "rstest", "rustversion", "structopt", "tabular", ] +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver", +] + [[package]] name = "rustversion" version = "1.0.5" @@ -430,6 +462,24 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "signal-hook" version = "0.1.17" @@ -515,6 +565,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + [[package]] name = "unicode-segmentation" version = "1.7.1" diff --git a/rust-stream/Cargo.toml b/rust-stream/Cargo.toml index 55db62b..f0365a6 100644 --- a/rust-stream/Cargo.toml +++ b/rust-stream/Cargo.toml @@ -18,6 +18,9 @@ libc = "0.2.97" core_affinity = "0.5.10" colour = "0.6.0" +[dev-dependencies] +rstest = "0.10.0" + [build-dependencies] rustversion = "1.0" diff --git a/rust-stream/src/lib.rs b/rust-stream/src/lib.rs new file mode 100644 index 0000000..953858e --- /dev/null +++ b/rust-stream/src/lib.rs @@ -0,0 +1,430 @@ +#![feature(allocator_api)] +#![feature(vec_into_raw_parts)] + +use std::alloc::System; +use std::fmt::{Debug, Display}; +use std::iter::Sum; +use std::mem::size_of; +use std::time::Duration; + +use num_traits::abs; +use structopt::StructOpt; +use tabular::{Row, Table}; + +use crate::crossbeam_stream::ThreadedDevice; +use crate::plain_stream::SerialDevice; +use crate::rayon_stream::RayonDevice; +use crate::stream::{AllocatorType, ArrayType, RustStream, StreamData}; + +mod crossbeam_stream; +mod plain_stream; +mod rayon_stream; +mod stream; + +#[derive(Debug, StructOpt)] +struct Options { + /// List available devices + #[structopt(long)] + list: bool, + /// Select device at + #[structopt(long, default_value = "0")] + device: usize, + /// Run the test times (NUM >= 2) + #[structopt(long, short = "n", default_value = "100")] + numtimes: usize, + /// Use elements in the array + #[structopt(long, short = "s", default_value = "33554432")] + arraysize: usize, + /// Use floats (rather than doubles) + #[structopt(long)] + float: bool, + /// Only run triad + #[structopt(long)] + triad_only: bool, + /// Only run nstream + #[structopt(long)] + nstream_only: bool, + /// Output as csv table + #[structopt(long)] + csv: bool, + /// Use MiB=2^20 for bandwidth calculation (default MB=10^6) + #[structopt(long)] + mibibytes: bool, + /// Use libc malloc instead of the Rust's allocator for benchmark array allocation + #[structopt(name = "malloc", long)] + malloc: bool, + /// Initialise each benchmark array at allocation time on the main thread + #[structopt(name = "init", long)] + init: bool, + /// Pin threads to distinct cores, this has NO effect in Rayon devices + #[structopt(long)] + pin: bool, +} + +#[derive(PartialEq)] +enum Benchmark { + All, + Triad, + NStream, +} + +fn check_solution, D, A: AllocatorType>( + benchmark: Benchmark, numtimes: usize, vec: &StreamData, dot_sum: Option, +) -> bool { + let (mut gold_a, mut gold_b, mut gold_c) = vec.init; + for _ in 0..numtimes { + match benchmark { + Benchmark::All => { + gold_c = gold_a; + gold_b = vec.scalar * gold_c; + gold_c = gold_a + gold_b; + gold_a = gold_b + vec.scalar * gold_c; + } + Benchmark::Triad => { + gold_a = gold_b + vec.scalar * gold_c; + } + Benchmark::NStream => { + gold_a += gold_b + vec.scalar * gold_c; + } + }; + } + let tolerance = T::epsilon().into() * 100.0f64; + let validate_xs = |name: &str, xs: &Vec, from: T| { + let error = (xs.iter().map(|x| abs(*x - from)).sum::()).into() / xs.len() as f64; + let fail = error > tolerance; + if fail { + eprintln!("Validation failed on {}[]. Average error {} ", name, error); + } + !fail + }; + let a_ok = validate_xs("a", &vec.a, gold_a); + let b_ok = validate_xs("b", &vec.b, gold_b); + let c_ok = validate_xs("c", &vec.c, gold_c); + let dot_ok = dot_sum.map_or(true, |sum| { + let gold_sum = (gold_a * gold_b).into() * vec.size as f64; + let error = abs((sum.into() - gold_sum) / gold_sum); + let fail = error > 1.0e-8; + if fail { + eprintln!( + "Validation failed on sum. Error {} \nSum was {} but should be {}", + error, sum, gold_sum + ); + } + !fail + }); + + a_ok && b_ok && c_ok && dot_ok +} + +fn run_cpu + Display, D, A: AllocatorType>( + option: &Options, mut stream: StreamData, +) -> bool +where + StreamData: RustStream, +{ + let benchmark = match (option.nstream_only, option.triad_only) { + (true, false) => Benchmark::NStream, + (false, true) => Benchmark::Triad, + (false, false) => Benchmark::All, + (true, true) => { + panic!("Both triad and nstream are enabled, pick one or omit both to run all benchmarks") + } + }; + + let array_bytes = option.arraysize * size_of::(); + let total_bytes = array_bytes * 3; + let (mega_scale, mega_suffix, giga_scale, giga_suffix) = if !option.mibibytes { + (1.0e-6, "MB", 1.0e-9, "GB") + } else { + (2f64.powi(-20), "MiB", 2f64.powi(-30), "GiB") + }; + + if !option.csv { + println!( + "Running {} {} times", + match benchmark { + Benchmark::All => "kernels", + Benchmark::Triad => "triad", + Benchmark::NStream => "nstream", + }, + option.numtimes + ); + + if benchmark == Benchmark::Triad { + println!("Number of elements: {}", option.arraysize); + } + + println!("Precision: {}", if option.float { "float" } else { "double" }); + println!( + "Array size: {:.1} {} (={:.1} {})", + mega_scale * array_bytes as f64, + mega_suffix, + giga_scale * array_bytes as f64, + giga_suffix + ); + println!( + "Total size: {:.1} {} (={:.1} {})", + mega_scale * total_bytes as f64, + mega_suffix, + giga_scale * total_bytes as f64, + giga_suffix + ); + } + + stream.init_arrays(); + + let tabulate = |xs: &Vec, name: &str, t_size: usize| -> Vec<(&str, String)> { + let tail = &xs[1..]; // tail only + // do stats + let max = tail.iter().max().map(|d| d.as_secs_f64()); + let min = tail.iter().min().map(|d| d.as_secs_f64()); + match (min, max) { + (Some(min), Some(max)) => { + let avg: f64 = tail.iter().map(|d| d.as_secs_f64()).sum::() / tail.len() as f64; + let mbps = mega_scale * (t_size as f64) / min; + if option.csv { + vec![ + ("function", name.to_string()), + ("num_times", option.numtimes.to_string()), + ("n_elements", option.arraysize.to_string()), + ("sizeof", t_size.to_string()), + ( + if option.mibibytes { "max_mibytes_per_sec" } else { "max_mbytes_per_sec" }, + mbps.to_string(), + ), + ("min_runtime", min.to_string()), + ("max_runtime", max.to_string()), + ("avg_runtime", avg.to_string()), + ] + } else { + vec![ + ("Function", name.to_string()), + (if option.mibibytes { "MiBytes/sec" } else { "MBytes/sec" }, format!("{:.3}", mbps)), + ("Min (sec)", format!("{:.5}", min)), + ("Max", format!("{:.5}", max)), + ("Average", format!("{:.5}", avg)), + ] + } + } + (_, _) => panic!("No min/max element for {}(size={})", name, t_size), + } + }; + + let tabulate_all = |xs: Vec>| { + match xs.as_slice() { + [head, ..] => { + if option.csv { + println!("{}", head.iter().map(|(col, _)| *col).collect::>().join(",")); + for kvs in xs { + println!("{}", kvs.iter().map(|(_, val)| val.clone()).collect::>().join(",")); + } + } else { + let mut table = Table::new(&vec!["{:<}"; head.len()].join(" ")); + table.add_row(head.iter().fold(Row::new(), |row, (col, _)| row.with_cell(col))); + for kvs in xs { + table.add_row(kvs.iter().fold(Row::new(), |row, (_, val)| row.with_cell(val))); + } + print!("{}", table); + } + } + _ => panic!("Empty tabulation"), + }; + }; + + let solutions_correct = match benchmark { + Benchmark::All => { + let (results, sum) = stream.run_all(option.numtimes); + let correct = check_solution(benchmark, option.numtimes, &stream, Some(sum)); + tabulate_all(vec![ + tabulate(&results.copy, "Copy", 2 * array_bytes), + tabulate(&results.mul, "Mul", 2 * array_bytes), + tabulate(&results.add, "Add", 3 * array_bytes), + tabulate(&results.triad, "Triad", 3 * array_bytes), + tabulate(&results.dot, "Dot", 2 * array_bytes), + ]); + correct + } + Benchmark::NStream => { + let results = stream.run_nstream(option.numtimes); + let correct = check_solution(benchmark, option.numtimes, &stream, None); + tabulate_all(vec![tabulate(&results, "Nstream", 4 * array_bytes)]); + correct + } + Benchmark::Triad => { + let results = stream.run_triad(option.numtimes); + let correct = check_solution(benchmark, option.numtimes, &stream, None); + let total_bytes = 3 * array_bytes * option.numtimes; + let bandwidth = giga_scale * (total_bytes as f64 / results.as_secs_f64()); + println!("Runtime (seconds): {:.5}", results.as_secs_f64()); + println!("Bandwidth ({}/s): {:.3} ", giga_suffix, bandwidth); + correct + } + }; + &stream.clean_up(); + solutions_correct +} + +const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); + +static START_A: f32 = 0.1; +static START_B: f32 = 0.2; +static START_C: f32 = 0.0; +static START_SCALAR: f32 = 0.4; + +static FLOAT_INIT_SCALAR: f32 = START_SCALAR; +static FLOAT_INIT: (f32, f32, f32) = (START_A, START_B, START_C); + +static DOUBLE_START_SCALAR: f64 = START_SCALAR as f64; +static DOUBLE_INIT: (f64, f64, f64) = (START_A as f64, START_B as f64, START_C as f64); + +pub fn run(args: &Vec) -> bool { + println!("`{:?}`", args); + + let options: Options = Options::from_iter(args); + + if options.numtimes < 2 { + panic!("numtimes must be >= 2") + } + + let alloc = System; + let alloc_name = if options.malloc { "libc-malloc" } else { "rust-system" }; + + let rayon_device = &|| { + let dev = RayonDevice { pool: rayon::ThreadPoolBuilder::default().build().unwrap() }; + if !options.csv { + println!("Using {} thread(s), alloc={}", dev.pool.current_num_threads(), alloc_name); + if options.pin { + colour::e_yellow_ln!("Pinning threads have no effect on Rayon!") + } + } + if options.float { + run_cpu( + &options, + StreamData::new_in( + options.arraysize, + FLOAT_INIT_SCALAR, + FLOAT_INIT, + dev, + alloc, + options.malloc, + options.init, + ), + ) + } else { + run_cpu( + &options, + StreamData::new_in( + options.arraysize, + DOUBLE_START_SCALAR, + DOUBLE_INIT, + dev, + alloc, + options.malloc, + options.init, + ), + ) + } + }; + + let crossbeam_device = &|| { + let ncores = num_cpus::get(); + let dev = ThreadedDevice::new(ncores, options.pin); + if !options.csv { + println!("Using {} thread(s), pin={}, alloc={}", ncores, options.pin, alloc_name) + } + if options.float { + run_cpu( + &options, + StreamData::new_in( + options.arraysize, + FLOAT_INIT_SCALAR, + FLOAT_INIT, + dev, + alloc, + options.malloc, + options.init, + ), + ) + } else { + run_cpu( + &options, + StreamData::new_in( + options.arraysize, + DOUBLE_START_SCALAR, + DOUBLE_INIT, + dev, + alloc, + options.malloc, + options.init, + ), + ) + } + }; + let st_device = &|| { + let dev = SerialDevice { pin: options.pin }; + if !options.csv { + println!("Using 1 thread, pin={}, alloc={}", options.pin, alloc_name); + } + if options.float { + run_cpu( + &options, + StreamData::new_in( + options.arraysize, + FLOAT_INIT_SCALAR, + FLOAT_INIT, + dev, + alloc, + options.malloc, + options.init, + ), + ) + } else { + run_cpu( + &options, + StreamData::new_in( + options.arraysize, + DOUBLE_START_SCALAR, + DOUBLE_INIT, + dev, + alloc, + options.malloc, + options.init, + ), + ) + } + }; + let devices: Vec<(String, &'_ dyn Fn() -> bool)> = vec![ + ("CPU (Rayon)".to_string(), rayon_device), + (format!("CPU (Crossbeam, pinning={})", options.pin), crossbeam_device), + ("CPU (Single threaded)".to_string(), st_device), + ]; + + if options.list { + devices.iter().enumerate().for_each(|(i, (name, _))| { + println!("[{}] {}", i, name); + }); + true + } else { + match devices.get(options.device) { + Some((name, run)) => { + if !&options.csv { + println!( + "BabelStream\n\ + Version: {}\n\ + Implementation: Rust; {}", + VERSION.unwrap_or("unknown"), + name + ); + if options.init { + println!("Initialising arrays on main thread"); + } + } + run() + } + None => { + eprintln!("Device index {} not available", options.device); + false + } + } + } +} diff --git a/rust-stream/src/main.rs b/rust-stream/src/main.rs index 8f8f43c..8c99087 100644 --- a/rust-stream/src/main.rs +++ b/rust-stream/src/main.rs @@ -1,414 +1,5 @@ -#![feature(allocator_api)] -#![feature(vec_into_raw_parts)] - -use std::alloc::System; -use std::fmt::{Debug, Display}; -use std::iter::Sum; -use std::mem::size_of; -use std::time::Duration; - -use num_traits::abs; -use structopt::StructOpt; -use tabular::{Row, Table}; - -use crate::crossbeam_stream::ThreadedDevice; -use crate::plain_stream::SerialDevice; -use crate::rayon_stream::RayonDevice; -use crate::stream::{AllocatorType, ArrayType, RustStream, StreamData}; - -mod crossbeam_stream; -mod plain_stream; -mod rayon_stream; -mod stream; - -#[derive(Debug, StructOpt)] -struct Options { - /// List available devices - #[structopt(long)] - list: bool, - /// Select device at - #[structopt(long, default_value = "0")] - device: usize, - /// Run the test times (NUM >= 2) - #[structopt(long, short = "n", default_value = "100")] - numtimes: usize, - /// Use elements in the array - #[structopt(long, short = "s", default_value = "33554432")] - arraysize: usize, - /// Use floats (rather than doubles) - #[structopt(long)] - float: bool, - /// Only run triad - #[structopt(long)] - triad_only: bool, - /// Only run nstream - #[structopt(long)] - nstream_only: bool, - /// Output as csv table - #[structopt(long)] - csv: bool, - /// Use MiB=2^20 for bandwidth calculation (default MB=10^6) - #[structopt(long)] - mibibytes: bool, - /// Use libc malloc instead of the Rust's allocator for benchmark array allocation - #[structopt(name = "malloc", long)] - malloc: bool, - /// Initialise each benchmark array at allocation time on the main thread - #[structopt(name = "init", long)] - init: bool, - /// Pin threads to distinct cores, this has NO effect in Rayon devices - #[structopt(long)] - pin: bool, -} - -#[derive(PartialEq)] -enum Benchmark { - All, - Triad, - NStream, -} - -fn check_solution, D, A: AllocatorType>( - benchmark: Benchmark, numtimes: usize, vec: &StreamData, dot_sum: Option, -) { - let (mut gold_a, mut gold_b, mut gold_c) = vec.init; - for _ in 0..numtimes { - match benchmark { - Benchmark::All => { - gold_c = gold_a; - gold_b = vec.scalar * gold_c; - gold_c = gold_a + gold_b; - gold_a = gold_b + vec.scalar * gold_c; - } - Benchmark::Triad => { - gold_a = gold_b + vec.scalar * gold_c; - } - Benchmark::NStream => { - gold_a += gold_b + vec.scalar * gold_c; - } - }; - } - let tolerance = T::epsilon().into() * 100.0f64; - let validate_xs = |name: &str, xs: &Vec, from: T| { - let error = (xs.iter().map(|x| abs(*x - from)).sum::()).into() / xs.len() as f64; - if error > tolerance { - eprintln!("Validation failed on {}[]. Average error {} ", name, error) - } - }; - validate_xs("a", &vec.a, gold_a); - validate_xs("b", &vec.b, gold_b); - validate_xs("c", &vec.c, gold_c); - - if let Some(sum) = dot_sum { - let gold_sum = (gold_a * gold_b).into() * vec.size as f64; - let error = abs((sum.into() - gold_sum) / gold_sum); - if error > 1.0e-8 { - eprintln!( - "Validation failed on sum. Error {} \nSum was {} but should be {}", - error, sum, gold_sum - ); - } - } -} - -fn run_cpu + Display, D, A: AllocatorType>( - option: &Options, mut stream: StreamData, -) where - StreamData: RustStream, -{ - let benchmark = match (option.nstream_only, option.triad_only) { - (true, false) => Benchmark::NStream, - (false, true) => Benchmark::Triad, - (false, false) => Benchmark::All, - (true, true) => { - panic!("Both triad and nstream are enabled, pick one or omit both to run all benchmarks") - } - }; - - let array_bytes = option.arraysize * size_of::(); - let total_bytes = array_bytes * 3; - let (mega_scale, mega_suffix, giga_scale, giga_suffix) = if !option.mibibytes { - (1.0e-6, "MB", 1.0e-9, "GB") - } else { - (2f64.powi(-20), "MiB", 2f64.powi(-30), "GiB") - }; - - if !option.csv { - println!( - "Running {} {} times", - match benchmark { - Benchmark::All => "kernels", - Benchmark::Triad => "triad", - Benchmark::NStream => "nstream", - }, - option.numtimes - ); - - if benchmark == Benchmark::Triad { - println!("Number of elements: {}", option.arraysize); - } - - println!("Precision: {}", if option.float { "float" } else { "double" }); - println!( - "Array size: {:.1} {} (={:.1} {})", - mega_scale * array_bytes as f64, - mega_suffix, - giga_scale * array_bytes as f64, - giga_suffix - ); - println!( - "Total size: {:.1} {} (={:.1} {})", - mega_scale * total_bytes as f64, - mega_suffix, - giga_scale * total_bytes as f64, - giga_suffix - ); - } - - stream.init_arrays(); - - let tabulate = |xs: &Vec, name: &str, t_size: usize| -> Vec<(&str, String)> { - let tail = &xs[1..]; // tail only - // do stats - let max = tail.iter().max().map(|d| d.as_secs_f64()); - let min = tail.iter().min().map(|d| d.as_secs_f64()); - match (min, max) { - (Some(min), Some(max)) => { - let avg: f64 = tail.iter().map(|d| d.as_secs_f64()).sum::() / tail.len() as f64; - let mbps = mega_scale * (t_size as f64) / min; - if option.csv { - vec![ - ("function", name.to_string()), - ("num_times", option.numtimes.to_string()), - ("n_elements", option.arraysize.to_string()), - ("sizeof", t_size.to_string()), - ( - if option.mibibytes { "max_mibytes_per_sec" } else { "max_mbytes_per_sec" }, - mbps.to_string(), - ), - ("min_runtime", min.to_string()), - ("max_runtime", max.to_string()), - ("avg_runtime", avg.to_string()), - ] - } else { - vec![ - ("Function", name.to_string()), - (if option.mibibytes { "MiBytes/sec" } else { "MBytes/sec" }, format!("{:.3}", mbps)), - ("Min (sec)", format!("{:.5}", min)), - ("Max", format!("{:.5}", max)), - ("Average", format!("{:.5}", avg)), - ] - } - } - (_, _) => panic!("No min/max element for {}(size={})", name, t_size), - } - }; - - let tabulate_all = |xs: Vec>| { - match xs.as_slice() { - [head, ..] => { - if option.csv { - println!("{}", head.iter().map(|(col, _)| *col).collect::>().join(",")); - for kvs in xs { - println!("{}", kvs.iter().map(|(_, val)| val.clone()).collect::>().join(",")); - } - } else { - let mut table = Table::new(&vec!["{:<}"; head.len()].join(" ")); - table.add_row(head.iter().fold(Row::new(), |row, (col, _)| row.with_cell(col))); - for kvs in xs { - table.add_row(kvs.iter().fold(Row::new(), |row, (_, val)| row.with_cell(val))); - } - print!("{}", table); - } - } - _ => panic!("Empty tabulation"), - }; - }; - - match benchmark { - Benchmark::All => { - let (results, sum) = stream.run_all(option.numtimes); - check_solution(benchmark, option.numtimes, &stream, Some(sum)); - tabulate_all(vec![ - tabulate(&results.copy, "Copy", 2 * array_bytes), - tabulate(&results.mul, "Mul", 2 * array_bytes), - tabulate(&results.add, "Add", 3 * array_bytes), - tabulate(&results.triad, "Triad", 3 * array_bytes), - tabulate(&results.dot, "Dot", 2 * array_bytes), - ]) - } - Benchmark::NStream => { - let results = stream.run_nstream(option.numtimes); - check_solution(benchmark, option.numtimes, &stream, None); - tabulate_all(vec![tabulate(&results, "Nstream", 4 * array_bytes)]); - } - Benchmark::Triad => { - let results = stream.run_triad(option.numtimes); - let total_bytes = 3 * array_bytes * option.numtimes; - let bandwidth = giga_scale * (total_bytes as f64 / results.as_secs_f64()); - - println!("Runtime (seconds): {:.5}", results.as_secs_f64()); - println!("Bandwidth ({}/s): {:.3} ", giga_suffix, bandwidth); - } - }; - &stream.clean_up(); -} - -const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); - -static START_A: f32 = 0.1; -static START_B: f32 = 0.2; -static START_C: f32 = 0.0; -static START_SCALAR: f32 = 0.4; - -static FLOAT_INIT_SCALAR: f32 = START_SCALAR; -static FLOAT_INIT: (f32, f32, f32) = (START_A, START_B, START_C); - -static DOUBLE_START_SCALAR: f64 = START_SCALAR as f64; -static DOUBLE_INIT: (f64, f64, f64) = (START_A as f64, START_B as f64, START_C as f64); - fn main() { - let options: Options = Options::from_args(); - - if options.numtimes < 2 { - panic!("numtimes must be >= 2") - } - - let alloc = System; - let alloc_name = if options.malloc { "libc-malloc" } else { "rust-system" }; - - let rayon_device = &|| { - let dev = RayonDevice { pool: rayon::ThreadPoolBuilder::default().build().unwrap() }; - if !options.csv { - println!("Using {} thread(s), alloc={}", dev.pool.current_num_threads(), alloc_name); - if options.pin { - colour::e_yellow_ln!("Pinning threads have no effect on Rayon!") - } - } - if options.float { - run_cpu( - &options, - StreamData::new_in( - options.arraysize, - FLOAT_INIT_SCALAR, - FLOAT_INIT, - dev, - alloc, - options.malloc, - options.init, - ), - ); - } else { - run_cpu( - &options, - StreamData::new_in( - options.arraysize, - DOUBLE_START_SCALAR, - DOUBLE_INIT, - dev, - alloc, - options.malloc, - options.init, - ), - ); - } - }; - - let crossbeam_device = &|| { - let ncores = num_cpus::get(); - let dev = ThreadedDevice::new(ncores, options.pin); - if !options.csv { - println!("Using {} thread(s), pin={}, alloc={}", ncores, options.pin, alloc_name) - } - if options.float { - run_cpu( - &options, - StreamData::new_in( - options.arraysize, - FLOAT_INIT_SCALAR, - FLOAT_INIT, - dev, - alloc, - options.malloc, - options.init, - ), - ); - } else { - run_cpu( - &options, - StreamData::new_in( - options.arraysize, - DOUBLE_START_SCALAR, - DOUBLE_INIT, - dev, - alloc, - options.malloc, - options.init, - ), - ); - } - }; - let st_device = &|| { - let dev = SerialDevice { pin: options.pin }; - if !options.csv { - println!("Using 1 thread, pin={}, alloc={}", options.pin, alloc_name); - } - if options.float { - run_cpu( - &options, - StreamData::new_in( - options.arraysize, - FLOAT_INIT_SCALAR, - FLOAT_INIT, - dev, - alloc, - options.malloc, - options.init, - ), - ); - } else { - run_cpu( - &options, - StreamData::new_in( - options.arraysize, - DOUBLE_START_SCALAR, - DOUBLE_INIT, - dev, - alloc, - options.malloc, - options.init, - ), - ); - } - }; - let devices: Vec<(String, &'_ dyn Fn())> = vec![ - ("CPU (Rayon)".to_string(), rayon_device), - (format!("CPU (Crossbeam, pinning={})", options.pin), crossbeam_device), - ("CPU (Single threaded)".to_string(), st_device), - ]; - - if options.list { - devices.iter().enumerate().for_each(|(i, (name, _))| { - println!("[{}] {}", i, name); - }) - } else { - match devices.get(options.device) { - Some((name, run)) => { - if !&options.csv { - println!( - "BabelStream\n\ - Version: {}\n\ - Implementation: Rust; {}", - VERSION.unwrap_or("unknown"), - name - ); - if options.init { - println!("Initialising arrays on main thread"); - } - } - run(); - } - None => eprintln!("Device index {} not available", options.device), - } + if !rust_stream::run(&std::env::args().collect::>()) { + std::process::exit(1); } } diff --git a/rust-stream/src/plain_stream.rs b/rust-stream/src/plain_stream.rs index 7a1fb01..135a7bc 100644 --- a/rust-stream/src/plain_stream.rs +++ b/rust-stream/src/plain_stream.rs @@ -47,7 +47,7 @@ impl RustStream for StreamData>(); + assert!(rust_stream::run(&args)); +} From e3bd58378f812db8314b579b2d3685d11b70e9c0 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Wed, 16 Jun 2021 01:16:10 +0100 Subject: [PATCH 36/78] Don't debug print args --- rust-stream/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rust-stream/src/lib.rs b/rust-stream/src/lib.rs index 953858e..0f34c02 100644 --- a/rust-stream/src/lib.rs +++ b/rust-stream/src/lib.rs @@ -278,7 +278,6 @@ static DOUBLE_START_SCALAR: f64 = START_SCALAR as f64; static DOUBLE_INIT: (f64, f64, f64) = (START_A as f64, START_B as f64, START_C as f64); pub fn run(args: &Vec) -> bool { - println!("`{:?}`", args); let options: Options = Options::from_iter(args); From 4e6c56729bfa77e1839b3069c2075e4fa3cfab98 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Wed, 30 Jun 2021 18:09:54 +0100 Subject: [PATCH 37/78] Inline AMDGPU's hard_wait Show the selected implementation and not a constant "threaded" --- JuliaStream.jl/src/AMDGPUStream.jl | 25 ++++++++++++++----------- JuliaStream.jl/src/Stream.jl | 3 +-- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/JuliaStream.jl/src/AMDGPUStream.jl b/JuliaStream.jl/src/AMDGPUStream.jl index 80f69b4..cb54904 100644 --- a/JuliaStream.jl/src/AMDGPUStream.jl +++ b/JuliaStream.jl/src/AMDGPUStream.jl @@ -56,11 +56,6 @@ function make_stream( return data end -function hard_wait(kernel) - # soft wait causes HSA_REFCOUNT overflow issues - AMDGPU.wait(kernel, soft = false) -end - function init_arrays!(data::ROCData{T}, init::Tuple{T,T,T}) where {T} AMDGPU.fill!(data.a, init[1]) AMDGPU.fill!(data.b, init[2]) @@ -73,7 +68,10 @@ function copy!(data::ROCData{T}) where {T} @inbounds c[i] = a[i] return end - hard_wait(@roc groupsize = TBSize gridsize = gridsize(data) kernel(data.a, data.c)) + AMDGPU.wait( + soft = false, # soft wait causes HSA_REFCOUNT overflow issues + @roc groupsize = TBSize gridsize = gridsize(data) kernel(data.a, data.c) + ) end function mul!(data::ROCData{T}) where {T} @@ -82,7 +80,8 @@ function mul!(data::ROCData{T}) where {T} @inbounds b[i] = scalar * c[i] return end - hard_wait( + AMDGPU.wait( + soft = false, # soft wait causes HSA_REFCOUNT overflow issues @roc groupsize = TBSize gridsize = gridsize(data) kernel(data.b, data.c, data.scalar) ) end @@ -93,7 +92,8 @@ function add!(data::ROCData{T}) where {T} @inbounds c[i] = a[i] + b[i] return end - hard_wait( + AMDGPU.wait( + soft = false, # soft wait causes HSA_REFCOUNT overflow issues @roc groupsize = TBSize gridsize = gridsize(data) kernel(data.a, data.b, data.c) ) end @@ -104,7 +104,8 @@ function triad!(data::ROCData{T}) where {T} @inbounds a[i] = b[i] + (scalar * c[i]) return end - hard_wait( + AMDGPU.wait( + soft = false, # soft wait causes HSA_REFCOUNT overflow issues @roc groupsize = TBSize gridsize = gridsize(data) kernel( data.a, data.b, @@ -120,7 +121,8 @@ function nstream!(data::ROCData{T}) where {T} @inbounds a[i] += b[i] + scalar * c[i] return end - hard_wait( + AMDGPU.wait( + soft = false, # soft wait causes HSA_REFCOUNT overflow issues @roc groupsize = TBSize gridsize = gridsize(data) kernel( data.a, data.b, @@ -160,7 +162,8 @@ function dot(data::ROCData{T}) where {T} return end partial_sum = ROCArray{T}(undef, DotBlocks) - hard_wait( + AMDGPU.wait( + soft = false, # soft wait causes HSA_REFCOUNT overflow issues @roc groupsize = TBSize gridsize = TBSize * DotBlocks kernel( data.a, data.b, diff --git a/JuliaStream.jl/src/Stream.jl b/JuliaStream.jl/src/Stream.jl index cce846b..590ab2d 100644 --- a/JuliaStream.jl/src/Stream.jl +++ b/JuliaStream.jl/src/Stream.jl @@ -99,7 +99,6 @@ end @with_kw mutable struct Config list::Bool = false - impl::String = "threaded" device::Int = 1 numtimes::Int = 100 arraysize::Int = 33554432 @@ -201,7 +200,7 @@ function main() if !config.csv println("""BabelStream Version: $Version - Implementation: Julia; $(config.impl)""") + Implementation: Julia; $(PROGRAM_FILE)""") println("Running kernels $(config.numtimes) times") if benchmark == Triad println("Number of elements: $(config.arraysize)") From d675875dcd169e1a0748234eac6013b1975886b8 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Wed, 30 Jun 2021 19:03:39 +0100 Subject: [PATCH 38/78] Switch back to -p for DistributedStream --- JuliaStream.jl/README.md | 8 ++++---- JuliaStream.jl/src/DistributedStream.jl | 7 +++---- JuliaStream.jl/src/StreamData.jl | 13 ++++++------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/JuliaStream.jl/README.md b/JuliaStream.jl/README.md index 9126167..851a59d 100644 --- a/JuliaStream.jl/README.md +++ b/JuliaStream.jl/README.md @@ -13,7 +13,7 @@ This is an implementation of BabelStream in Julia which contains the following v Prerequisites - * Julia 1.6+ + * Julia >= 1.6+ With Julia on path, run the benchmark with: @@ -24,7 +24,7 @@ With Julia on path, run the benchmark with: ``` **Important:** - * Julia is 1-indexed, so N > 1 in `--device N` - * Thread count for `ThreadedStream` must be set via the `JULIA_NUM_THREADS` environment variable (e.g `export JULIA_NUM_THREADS=$(nproc)`) otherwise it defaults to 1 - * `DistributedStream` uses `addprocs()` call directly which defaults to `$(nproc)`, **do not use the `-p ` flag** as per the [documentation](https://docs.julialang.org/en/v1/manual/distributed-computing). + * Julia is 1-indexed, so N >= 1 in `--device N`. + * Thread count for `ThreadedStream` must be set via the `JULIA_NUM_THREADS` environment variable (e.g `export JULIA_NUM_THREADS=$(nproc)`) otherwise it defaults to 1. + * Worker count for `DistributedStream` is set with `-p ` as per the [documentation](https://docs.julialang.org/en/v1/manual/distributed-computing). * Certain implementations such as CUDA and AMDGPU will do hardware detection at runtime and may download and/or compile further software packages for the platform. diff --git a/JuliaStream.jl/src/DistributedStream.jl b/JuliaStream.jl/src/DistributedStream.jl index 361c737..2aa7ae7 100644 --- a/JuliaStream.jl/src/DistributedStream.jl +++ b/JuliaStream.jl/src/DistributedStream.jl @@ -1,10 +1,9 @@ using Distributed -include("Stream.jl") - -addprocs() - +@everywhere using Pkg +@everywhere Pkg.activate("."; io=devnull) # don't spam `Activating environment at...` @everywhere include("StreamData.jl") +@everywhere include("Stream.jl") @everywhere using SharedArrays @everywhere const SharedArrayData = StreamData{T,SharedArray{T}} where {T} diff --git a/JuliaStream.jl/src/StreamData.jl b/JuliaStream.jl/src/StreamData.jl index 07498fe..55e055a 100644 --- a/JuliaStream.jl/src/StreamData.jl +++ b/JuliaStream.jl/src/StreamData.jl @@ -1,8 +1,7 @@ - struct StreamData{T,C<:AbstractArray{T}} - a::C - b::C - c::C - scalar::T - size::Int - end \ No newline at end of file + a::C + b::C + c::C + scalar::T + size::Int +end From 418315543ccaf46d70eb5999902fc4ef3c06e9cc Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Wed, 30 Jun 2021 19:09:37 +0100 Subject: [PATCH 39/78] Use -p 2 and no arg for JuliaStream in CI --- .github/workflows/main.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index d54850e..2427ed1 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -18,9 +18,12 @@ jobs: - name: Test run ThreadedStream.jl if: ${{ ! cancelled() }} run: julia --threads 2 --project src/ThreadedStream.jl --arraysize 2048 - - name: Test run DistributedStream.jl + - name: Test run DistributedStream.jl (no flag) if: ${{ ! cancelled() }} run: julia --project src/DistributedStream.jl --arraysize 2048 + - name: Test run DistributedStream.jl (-p 2) + if: ${{ ! cancelled() }} + run: julia -p 2 --project src/DistributedStream.jl --arraysize 2048 - name: Test run CUDAStream.jl if: ${{ ! cancelled() }} run: julia --project src/CUDAStream.jl --list From 2e957d3f604b2aa3109baada0f132f024dca906e Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Wed, 30 Jun 2021 19:20:37 +0100 Subject: [PATCH 40/78] Inline blocks in CUDAStream --- JuliaStream.jl/src/CUDAStream.jl | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/JuliaStream.jl/src/CUDAStream.jl b/JuliaStream.jl/src/CUDAStream.jl index c4d0510..dbf21d7 100644 --- a/JuliaStream.jl/src/CUDAStream.jl +++ b/JuliaStream.jl/src/CUDAStream.jl @@ -10,10 +10,6 @@ function devices() map(d -> "$(CUDA.name(d)) ($(repr(d)))", CUDA.devices()) end -function blocks(data::CuData{T})::Int where {T} - return data.size ÷ TBSize -end - function make_stream( arraysize::Int, scalar::T, @@ -42,7 +38,7 @@ function make_stream( ) if !silent println("Using CUDA device: $(CUDA.name(selected)) ($(repr(selected)))") - println("Kernel parameters: <<<$(blocks(data)),$(TBSize)>>>") + println("Kernel parameters: <<<$(data.size ÷ TBSize),$(TBSize)>>>") end return data end @@ -59,7 +55,7 @@ function copy!(data::CuData{T}) where {T} @inbounds c[i] = a[i] return end - @cuda blocks = blocks(data) threads = TBSize kernel(data.a, data.c) + @cuda blocks = data.size ÷ TBSize threads = TBSize kernel(data.a, data.c) CUDA.synchronize() end @@ -69,7 +65,7 @@ function mul!(data::CuData{T}) where {T} @inbounds b[i] = scalar * c[i] return end - @cuda blocks = blocks(data) threads = TBSize kernel(data.b, data.c, data.scalar) + @cuda blocks = data.size ÷ TBSize threads = TBSize kernel(data.b, data.c, data.scalar) CUDA.synchronize() end @@ -79,7 +75,7 @@ function add!(data::CuData{T}) where {T} @inbounds c[i] = a[i] + b[i] return end - @cuda blocks = blocks(data) threads = TBSize kernel(data.a, data.b, data.c) + @cuda blocks = data.size ÷ TBSize threads = TBSize kernel(data.a, data.b, data.c) CUDA.synchronize() end @@ -89,7 +85,12 @@ function triad!(data::CuData{T}) where {T} @inbounds a[i] = b[i] + (scalar * c[i]) return end - @cuda blocks = blocks(data) threads = TBSize kernel(data.a, data.b, data.c, data.scalar) + @cuda blocks = data.size ÷ TBSize threads = TBSize kernel( + data.a, + data.b, + data.c, + data.scalar, + ) CUDA.synchronize() end @@ -99,7 +100,12 @@ function nstream!(data::CuData{T}) where {T} @inbounds a[i] += b[i] + scalar * c[i] return end - @cuda blocks = blocks(data) threads = TBSize kernel(data.a, data.b, data.c, data.scalar) + @cuda blocks = data.size ÷ TBSize threads = TBSize kernel( + data.a, + data.b, + data.c, + data.scalar, + ) CUDA.synchronize() end From 7c1e04a42b9b03b0e5c5d0b07c0ef9f4bdd59353 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Wed, 30 Jun 2021 19:31:42 +0100 Subject: [PATCH 41/78] Add comment about blockIdx/workgroupIdx in Julia --- JuliaStream.jl/src/AMDGPUStream.jl | 12 ++++++------ JuliaStream.jl/src/CUDAStream.jl | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/JuliaStream.jl/src/AMDGPUStream.jl b/JuliaStream.jl/src/AMDGPUStream.jl index cb54904..9a9cd9a 100644 --- a/JuliaStream.jl/src/AMDGPUStream.jl +++ b/JuliaStream.jl/src/AMDGPUStream.jl @@ -64,7 +64,7 @@ end function copy!(data::ROCData{T}) where {T} function kernel(a, c) - i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x + i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x # only workgroupIdx starts at 1 @inbounds c[i] = a[i] return end @@ -76,7 +76,7 @@ end function mul!(data::ROCData{T}) where {T} function kernel(b, c, scalar) - i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x + i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x # only workgroupIdx starts at 1 @inbounds b[i] = scalar * c[i] return end @@ -88,7 +88,7 @@ end function add!(data::ROCData{T}) where {T} function kernel(a, b, c) - i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x + i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x # only workgroupIdx starts at 1 @inbounds c[i] = a[i] + b[i] return end @@ -100,7 +100,7 @@ end function triad!(data::ROCData{T}) where {T} function kernel(a, b, c, scalar) - i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x + i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x # only workgroupIdx starts at 1 @inbounds a[i] = b[i] + (scalar * c[i]) return end @@ -117,7 +117,7 @@ end function nstream!(data::ROCData{T}) where {T} function kernel(a, b, c, scalar) - i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x + i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x # only workgroupIdx starts at 1 @inbounds a[i] += b[i] + scalar * c[i] return end @@ -139,7 +139,7 @@ function dot(data::ROCData{T}) where {T} @inbounds tb_sum[local_i] = 0.0 # do dot first - i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x + i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x # only workgroupIdx starts at 1 while i <= size @inbounds tb_sum[local_i] += a[i] * b[i] i += TBSize * DotBlocks # XXX don't use (workgroupDim().x * gridDimWG().x) here diff --git a/JuliaStream.jl/src/CUDAStream.jl b/JuliaStream.jl/src/CUDAStream.jl index dbf21d7..7d671a5 100644 --- a/JuliaStream.jl/src/CUDAStream.jl +++ b/JuliaStream.jl/src/CUDAStream.jl @@ -51,7 +51,7 @@ end function copy!(data::CuData{T}) where {T} function kernel(a, c) - i = (blockIdx().x - 1) * blockDim().x + threadIdx().x + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 @inbounds c[i] = a[i] return end @@ -61,7 +61,7 @@ end function mul!(data::CuData{T}) where {T} function kernel(b, c, scalar) - i = (blockIdx().x - 1) * blockDim().x + threadIdx().x + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 @inbounds b[i] = scalar * c[i] return end @@ -71,7 +71,7 @@ end function add!(data::CuData{T}) where {T} function kernel(a, b, c) - i = (blockIdx().x - 1) * blockDim().x + threadIdx().x + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 @inbounds c[i] = a[i] + b[i] return end @@ -81,7 +81,7 @@ end function triad!(data::CuData{T}) where {T} function kernel(a, b, c, scalar) - i = (blockIdx().x - 1) * blockDim().x + threadIdx().x + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 @inbounds a[i] = b[i] + (scalar * c[i]) return end @@ -96,7 +96,7 @@ end function nstream!(data::CuData{T}) where {T} function kernel(a, b, c, scalar) - i = (blockIdx().x - 1) * blockDim().x + threadIdx().x + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 @inbounds a[i] += b[i] + scalar * c[i] return end @@ -117,7 +117,7 @@ function dot(data::CuData{T}) where {T} @inbounds tb_sum[local_i] = 0.0 # do dot first - i = (blockIdx().x - 1) * blockDim().x + threadIdx().x + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 while i <= size @inbounds tb_sum[local_i] += a[i] * b[i] i += blockDim().x * gridDim().x From ab41475f1016e375c39da47e9a083fd89b854b56 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 1 Jul 2021 05:59:48 +0100 Subject: [PATCH 42/78] Initial Java implementation --- .github/workflows/main.yaml | 12 + java-stream/.gitignore | 128 ++++++ java-stream/.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 47610 bytes .../.mvn/wrapper/maven-wrapper.properties | 1 + java-stream/README.md | 172 +++++++ java-stream/mvnw | 225 +++++++++ java-stream/mvnw.cmd | 143 ++++++ java-stream/pom.xml | 133 ++++++ .../main/java/javastream/FractionalMaths.java | 45 ++ .../src/main/java/javastream/JavaStream.java | 172 +++++++ .../src/main/java/javastream/Main.java | 427 ++++++++++++++++++ .../javastream/aparapi/AparapiStreams.java | 129 ++++++ .../aparapi/GenericAparapiStreamKernel.java | 68 +++ .../aparapi/SpecialisedDoubleKernel.java | 74 +++ .../aparapi/SpecialisedFloatKernel.java | 75 +++ .../javastream/jdk/GenericPlainStream.java | 92 ++++ .../java/javastream/jdk/GenericStream.java | 86 ++++ .../main/java/javastream/jdk/JdkStreams.java | 26 ++ .../main/java/javastream/jdk/PlainStream.java | 26 ++ .../jdk/SpecialisedDoubleStream.java | 84 ++++ .../jdk/SpecialisedFloatStream.java | 84 ++++ .../jdk/SpecialisedPlainDoubleStream.java | 84 ++++ .../jdk/SpecialisedPlainFloatStream.java | 84 ++++ .../tornadovm/GenericTornadoVMStream.java | 98 ++++ .../tornadovm/SpecialisedDouble.java | 89 ++++ .../tornadovm/SpecialisedFloat.java | 88 ++++ .../tornadovm/TornadoVMStreams.java | 42 ++ .../src/test/java/javastream/SmokeTest.java | 93 ++++ 28 files changed, 2780 insertions(+) create mode 100644 java-stream/.gitignore create mode 100644 java-stream/.mvn/wrapper/maven-wrapper.jar create mode 100644 java-stream/.mvn/wrapper/maven-wrapper.properties create mode 100644 java-stream/README.md create mode 100644 java-stream/mvnw create mode 100644 java-stream/mvnw.cmd create mode 100644 java-stream/pom.xml create mode 100644 java-stream/src/main/java/javastream/FractionalMaths.java create mode 100644 java-stream/src/main/java/javastream/JavaStream.java create mode 100644 java-stream/src/main/java/javastream/Main.java create mode 100644 java-stream/src/main/java/javastream/aparapi/AparapiStreams.java create mode 100644 java-stream/src/main/java/javastream/aparapi/GenericAparapiStreamKernel.java create mode 100644 java-stream/src/main/java/javastream/aparapi/SpecialisedDoubleKernel.java create mode 100644 java-stream/src/main/java/javastream/aparapi/SpecialisedFloatKernel.java create mode 100644 java-stream/src/main/java/javastream/jdk/GenericPlainStream.java create mode 100644 java-stream/src/main/java/javastream/jdk/GenericStream.java create mode 100644 java-stream/src/main/java/javastream/jdk/JdkStreams.java create mode 100644 java-stream/src/main/java/javastream/jdk/PlainStream.java create mode 100644 java-stream/src/main/java/javastream/jdk/SpecialisedDoubleStream.java create mode 100644 java-stream/src/main/java/javastream/jdk/SpecialisedFloatStream.java create mode 100644 java-stream/src/main/java/javastream/jdk/SpecialisedPlainDoubleStream.java create mode 100644 java-stream/src/main/java/javastream/jdk/SpecialisedPlainFloatStream.java create mode 100644 java-stream/src/main/java/javastream/tornadovm/GenericTornadoVMStream.java create mode 100644 java-stream/src/main/java/javastream/tornadovm/SpecialisedDouble.java create mode 100644 java-stream/src/main/java/javastream/tornadovm/SpecialisedFloat.java create mode 100644 java-stream/src/main/java/javastream/tornadovm/TornadoVMStreams.java create mode 100644 java-stream/src/test/java/javastream/SmokeTest.java diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 20e1034..2f93a4a 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -3,6 +3,18 @@ on: [push, pull_request] jobs: + test-java: + runs-on: ubuntu-18.04 + defaults: + run: + working-directory: ./java-stream + steps: + - uses: actions/checkout@v2 + - name: Test build project + run: ./mvnw clean package + - name: Test run + if: ${{ ! cancelled() }} + run: java -jar target/java-stream.jar --arraysize 2048 test: runs-on: ubuntu-18.04 steps: diff --git a/java-stream/.gitignore b/java-stream/.gitignore new file mode 100644 index 0000000..2ed994a --- /dev/null +++ b/java-stream/.gitignore @@ -0,0 +1,128 @@ +## File-based project format: +.idea +*.iws +*.iml + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +### VisualStudioCode template +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +### Linux template +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk +### Maven template +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) +!/.mvn/wrapper/maven-wrapper.jar +### Java template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +### macOS template +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +!.mvn/**/* + +settings.xml diff --git a/java-stream/.mvn/wrapper/maven-wrapper.jar b/java-stream/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..9cc84ea9b4d95453115d0c26488d6a78694e0bc6 GIT binary patch literal 47610 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 literal 0 HcmV?d00001 diff --git a/java-stream/.mvn/wrapper/maven-wrapper.properties b/java-stream/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..56bb016 --- /dev/null +++ b/java-stream/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip \ No newline at end of file diff --git a/java-stream/README.md b/java-stream/README.md new file mode 100644 index 0000000..6c233da --- /dev/null +++ b/java-stream/README.md @@ -0,0 +1,172 @@ +java-stream +=========== + +This is an implementation of BabelStream in Java 8 which contains the following implementations: + +* `jdk-plain` - Single threaded `for` +* `jdk-stream` - Threaded implementation using JDK8's parallel stream API +* `tornadovm` - A [TornadoVM](https://github.com/beehive-lab/TornadoVM) implementation for + PTX/OpenCL +* `aparapi` - A [Aparapi](https://git.qoto.org/aparapi/aparapi) implementation for OpenCL + +### Build & Run + +Prerequisites + +* JDK >= 8 + +To run the benchmark, first create a binary: + +```shell +> cd java-stream +> ./mvnw clean package +``` + +The binary will be located at `./target/java-stream.jar`. Run it with: + +```shell +> java -version  ✔  11.0.11+9 ☕  tom@soraws-uk  05:03:20 +openjdk version "11.0.11" 2021-04-20 +OpenJDK Runtime Environment GraalVM CE 21.1.0 (build 11.0.11+8-jvmci-21.1-b05) +OpenJDK 64-Bit Server VM GraalVM CE 21.1.0 (build 11.0.11+8-jvmci-21.1-b05, mixed mode) +> java -jar target/java-stream.jar --help +``` + +For best results, benchmark with the following JVM flags: + +``` +-XX:-UseOnStackReplacement # disable OSR, not useful for this benchmark as we are measuring peak performance +-XX:-TieredCompilation # disable C1, go straight to C2 +-XX:ReservedCodeCacheSize=512m # don't flush compiled code out of cache at any point +``` + +Worked example: + +```shell +> java -XX:-UseOnStackReplacement -XX:-TieredCompilation -XX:ReservedCodeCacheSize=512m -jar target/java-stream.jar +BabelStream +Version: 3.4 +Implementation: jdk-stream; (Java 11.0.11;Red Hat, Inc.; home=/usr/lib/jvm/java-11-openjdk-11.0.11.0.9-4.fc33.x86_64) +Running all 100 times +Precision: double +Array size: 268.4 MB (=0.3 GB) +Total size: 805.3 MB (=0.8 GB) +Function MBytes/sec Min (sec) Max Average +Copy 17145.538 0.03131 0.04779 0.03413 +Mul 16759.092 0.03203 0.04752 0.03579 +Add 19431.954 0.04144 0.05866 0.04503 +Triad 19763.970 0.04075 0.05388 0.04510 +Dot 26646.894 0.02015 0.03013 0.02259 +``` + +If your OpenCL/CUDA installation is not at the default location, TornadoVM and Aparapi may fail to +detect your devices. In those cases, you may specify the library directly, for example: + +```shell +> LD_PRELOAD=/opt/rocm-4.0.0/opencl/lib/libOpenCL.so.1.2 java -jar target/java-stream.jar ... +``` + +### Instructions for TornadoVM + +The TornadoVM implementation requires you to run the binary with a patched JVM. Follow the +official [instructions](https://github.com/beehive-lab/TornadoVM/blob/master/assembly/src/docs/10_INSTALL_WITH_GRAALVM.md) +or use the following simplified instructions: + +Prerequisites + +* CMake >= 3.6 +* GCC or clang/LLVM (GCC >= 5.5) +* Python >= 2.7 +* Maven >= 3.6.3 +* OpenCL headers >= 1.2 and/or CUDA SDK >= 9.0 + +First, get a copy of the TornadoVM source: + +```shell +> cd +> git clone https://github.com/beehive-lab/TornadoVM tornadovm +``` + +Take note of the required GraalVM version +in `tornadovm/assembly/src/docs/10_INSTALL_WITH_GRAALVM.md`. We'll use `21.1.0` in this example. +Now, obtain a copy of GraalVM and make sure the version matches the one required by TornadoVM: + +```shell +> wget https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.1.0/graalvm-ce-java11-linux-amd64-21.1.0.tar.gz +> tar -xf graalvm-ce-java11-linux-amd64-21.1.0.tar.gz +``` + +Next, create `~/tornadovm/etc/sources.env` and populate the file with the following: + +```shell +#!/bin/bash +export JAVA_HOME= +export PATH=$PWD/bin/bin:$PATH +export TORNADO_SDK=$PWD/bin/sdk +export CMAKE_ROOT=/usr # path to CMake binary +``` + +Proceed to compile TornadoVM: + +```shell +> cd ~/tornadovm +> . etc/sources.env +> make graal-jdk-11-plus BACKEND={ptx,opencl} +``` + +To test your build, source the environment file: + +```shell +> source ~/tornadovm/etc/sources.env +> LD_PRELOAD=/opt/rocm-4.0.0/opencl/lib/libOpenCL.so.1.2 tornado --devices +Number of Tornado drivers: 1 +Total number of OpenCL devices : 3 +Tornado device=0:0 + AMD Accelerated Parallel Processing -- gfx1012 + Global Memory Size: 4.0 GB + Local Memory Size: 64.0 KB + Workgroup Dimensions: 3 + Max WorkGroup Configuration: [1024, 1024, 1024] + Device OpenCL C version: OpenCL C 2.0 + +Tornado device=0:1 + Portable Computing Language -- pthread-AMD Ryzen 9 3900X 12-Core Processor + Global Memory Size: 60.7 GB + Local Memory Size: 8.0 MB + Workgroup Dimensions: 3 + Max WorkGroup Configuration: [4096, 4096, 4096] + Device OpenCL C version: OpenCL C 1.2 pocl + +Tornado device=0:2 + NVIDIA CUDA -- NVIDIA GeForce GT 710 + Global Memory Size: 981.3 MB + Local Memory Size: 48.0 KB + Workgroup Dimensions: 3 + Max WorkGroup Configuration: [1024, 1024, 64] + Device OpenCL C version: OpenCL C 1.2 +``` + +You can now use TornadoVM to run java-stream: + +```shell +> tornado -jar ~/java-stream/target/java-stream.jar --impl tornadovm --arraysize 65536  1 ✘  11.0.11+9 ☕  tom@soraws-uk  05:31:34 +BabelStream +Version: 3.4 +Implementation: tornadovm; (Java 11.0.11;GraalVM Community; home=~/graalvm-ce-java11-21.1.0) +Running all 100 times +Precision: double +Array size: 0.5 MB (=0.0 GB) +Total size: 1.6 MB (=0.0 GB) +Using TornadoVM device: + - Name : NVIDIA GeForce GT 710 CL_DEVICE_TYPE_GPU (available) + - Id : opencl-0-0 + - Platform : NVIDIA CUDA + - Backend : OpenCL +Function MBytes/sec Min (sec) Max Average +Copy 8791.100 0.00012 0.00079 0.00015 +Mul 8774.107 0.00012 0.00061 0.00014 +Add 9903.313 0.00016 0.00030 0.00018 +Triad 9861.031 0.00016 0.00030 0.00018 +Dot 2799.465 0.00037 0.00056 0.00041 +``` + diff --git a/java-stream/mvnw b/java-stream/mvnw new file mode 100644 index 0000000..5bf251c --- /dev/null +++ b/java-stream/mvnw @@ -0,0 +1,225 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +echo $MAVEN_PROJECTBASEDIR +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/java-stream/mvnw.cmd b/java-stream/mvnw.cmd new file mode 100644 index 0000000..019bd74 --- /dev/null +++ b/java-stream/mvnw.cmd @@ -0,0 +1,143 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/java-stream/pom.xml b/java-stream/pom.xml new file mode 100644 index 0000000..6a1e4f0 --- /dev/null +++ b/java-stream/pom.xml @@ -0,0 +1,133 @@ + + + + 4.0.0 + + java-stream + javastream + 3.4.0 + + + UTF-8 + UTF-8 + 5.7.2 + + + + + universityOfManchester-graal + https://raw.githubusercontent.com/beehive-lab/tornado/maven-tornadovm + + + + + + + com.beust + jcommander + 1.81 + + + + tornado + tornado-api + 0.9 + + + + com.aparapi + aparapi + 2.0.0 + + + + org.scala-lang + scala-library + + + + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit.version} + test + + + + + + + + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + -Xlint:all + true + true + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + + + maven-shade-plugin + 3.2.4 + + + package + + shade + + + + + javastream.Main + + + + + *:* + + META-INF/*.MF + + + + ${project.artifactId} + + + + + + + com.coveo + fmt-maven-plugin + 2.11 + + + + format + + + + + + + + + \ No newline at end of file diff --git a/java-stream/src/main/java/javastream/FractionalMaths.java b/java-stream/src/main/java/javastream/FractionalMaths.java new file mode 100644 index 0000000..982a28a --- /dev/null +++ b/java-stream/src/main/java/javastream/FractionalMaths.java @@ -0,0 +1,45 @@ +package javastream; + +/** + * This class represents our Fractional typeclass. Java's type system isn't unified so we have to do + * insane things for parametric operations on fractional types. + */ +@SuppressWarnings("unchecked") +public final class FractionalMaths { + + private FractionalMaths() { + throw new AssertionError(); + } + + public static T from(Class evidence, Number n) { + if (evidence == Double.TYPE || evidence == Double.class) + return (T) Double.valueOf(n.doubleValue()); + else if (evidence == Float.TYPE || evidence == Float.class) + return (T) Float.valueOf(n.floatValue()); + throw new IllegalArgumentException(); + } + + public static T plus(T x, T y) { + if (x instanceof Double) return (T) Double.valueOf(x.doubleValue() + y.doubleValue()); + else if (x instanceof Float) return (T) Float.valueOf(x.floatValue() + y.floatValue()); + throw new IllegalArgumentException(); + } + + static T minus(T x, T y) { + if (x instanceof Double) return (T) Double.valueOf(x.doubleValue() - y.doubleValue()); + else if (x instanceof Float) return (T) Float.valueOf(x.floatValue() - y.floatValue()); + throw new IllegalArgumentException(); + } + + public static T times(T x, T y) { + if (x instanceof Double) return (T) Double.valueOf(x.doubleValue() * y.doubleValue()); + else if (x instanceof Float) return (T) Float.valueOf(x.floatValue() * y.floatValue()); + throw new IllegalArgumentException(); + } + + static T divide(T x, T y) { + if (x instanceof Double) return (T) Double.valueOf(x.doubleValue() / y.doubleValue()); + else if (x instanceof Float) return (T) Float.valueOf(x.floatValue() / y.floatValue()); + throw new IllegalArgumentException(); + } +} diff --git a/java-stream/src/main/java/javastream/JavaStream.java b/java-stream/src/main/java/javastream/JavaStream.java new file mode 100644 index 0000000..7ab96cb --- /dev/null +++ b/java-stream/src/main/java/javastream/JavaStream.java @@ -0,0 +1,172 @@ +package javastream; + +import java.time.Duration; +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import javastream.Main.Config; + +public abstract class JavaStream { + + public static final class Data { + final T[] a, b, c; + + public Data(T[] a, T[] b, T[] c) { + this.a = Objects.requireNonNull(a); + this.b = Objects.requireNonNull(b); + this.c = Objects.requireNonNull(c); + } + } + + static final class Timings { + final List copy = new ArrayList<>(); + final List mul = new ArrayList<>(); + final List add = new ArrayList<>(); + final List triad = new ArrayList<>(); + final List dot = new ArrayList<>(); + } + + protected final Config config; + + protected JavaStream(Config config) { + this.config = config; + } + + protected abstract List listDevices(); + + protected abstract void initArrays(); + + protected abstract void copy(); + + protected abstract void mul(); + + protected abstract void add(); + + protected abstract void triad(); + + protected abstract void nstream(); + + protected abstract T dot(); + + protected abstract Data data(); + + public static class EnumeratedStream extends JavaStream { + + protected final JavaStream actual; + private final Entry, JavaStream>>[] options; + + @SafeVarargs + @SuppressWarnings("varargs") + public EnumeratedStream( + Config config, Entry, JavaStream>>... options) { + super(config); + this.actual = options[config.options.device].getValue().apply(config); + this.options = options; + } + + @Override + protected List listDevices() { + return Arrays.stream(options).map(Entry::getKey).collect(Collectors.toList()); + } + + @Override + public void initArrays() { + actual.initArrays(); + } + + @Override + public void copy() { + actual.copy(); + } + + @Override + public void mul() { + actual.mul(); + } + + @Override + public void add() { + actual.add(); + } + + @Override + public void triad() { + actual.triad(); + } + + @Override + public void nstream() { + actual.nstream(); + } + + @Override + public T dot() { + return actual.dot(); + } + + @Override + public Data data() { + return actual.data(); + } + } + + public static Double[] boxed(double[] xs) { + return Arrays.stream(xs).boxed().toArray(Double[]::new); + } + + public static Float[] boxed(float[] xs) { + return IntStream.range(0, xs.length).mapToObj(i -> xs[i]).toArray(Float[]::new); + } + + private static AbstractMap.SimpleImmutableEntry timed(Supplier f) { + long start = System.nanoTime(); + T r = f.get(); + long end = System.nanoTime(); + return new AbstractMap.SimpleImmutableEntry<>(Duration.ofNanos(end - start), r); + } + + private static Duration timed(Runnable f) { + long start = System.nanoTime(); + f.run(); + long end = System.nanoTime(); + return Duration.ofNanos(end - start); + } + + final SimpleImmutableEntry, T> runAll(int times) { + Timings timings = new Timings<>(); + T lastSum = null; + for (int i = 0; i < times; i++) { + timings.copy.add(timed(this::copy)); + timings.mul.add(timed(this::mul)); + timings.add.add(timed(this::add)); + timings.triad.add(timed(this::triad)); + SimpleImmutableEntry dot = timed(this::dot); + timings.dot.add(dot.getKey()); + lastSum = dot.getValue(); + } + return new SimpleImmutableEntry<>(timings, lastSum); + } + + final Duration runTriad(int times) { + return timed( + () -> { + for (int i = 0; i < times; i++) { + triad(); + } + }); + } + + final List runNStream(int times) { + return IntStream.range(0, times) + .mapToObj(i -> timed(this::nstream)) + .collect(Collectors.toList()); + } +} diff --git a/java-stream/src/main/java/javastream/Main.java b/java-stream/src/main/java/javastream/Main.java new file mode 100644 index 0000000..4c8ed6c --- /dev/null +++ b/java-stream/src/main/java/javastream/Main.java @@ -0,0 +1,427 @@ +package javastream; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; + +import java.time.Duration; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.Arrays; +import java.util.DoubleSummaryStatistics; +import java.util.List; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; + +import javastream.JavaStream.Data; +import javastream.JavaStream.Timings; +import javastream.aparapi.AparapiStreams; +import javastream.jdk.JdkStreams; +import javastream.jdk.PlainStream; +import javastream.tornadovm.TornadoVMStreams; + +import static javastream.FractionalMaths.divide; +import static javastream.FractionalMaths.from; +import static javastream.FractionalMaths.minus; +import static javastream.FractionalMaths.plus; +import static javastream.FractionalMaths.times; + +public class Main { + + enum Benchmark { + NSTREAM, + TRIAD, + ALL + } + + public static class Options { + + @Parameter(names = "--list", description = "List available devices for all implementations") + boolean list = false; + + @Parameter( + names = "--device", + description = "Select device at , see --list for options") + public int device = 0; + + @Parameter( + names = "--impl", + description = "Select implementation at , see --list for options") + public String impl = ""; + + @Parameter( + names = {"--numtimes", "-n"}, + description = "Run the test times (NUM >= 2)") + public int numtimes = 100; + + @Parameter( + names = {"--arraysize", "-s"}, + description = "Use elements in the array") + public int arraysize = 33554432; + + @Parameter(names = "--float", description = "Use floats (rather than doubles)") + public boolean useFloat = false; + + @Parameter(names = "--triad-only", description = "Only run triad") + public boolean triadOnly = false; + + @Parameter(names = "--nstream-only", description = "Only run nstream") + public boolean nstreamOnly = false; + + @Parameter(names = "--csv", description = "Output as csv table") + public boolean csv = false; + + @Parameter( + names = "--mibibytes", + description = "Use MiB=2^20 for bandwidth calculation (default MB=10^6)") + public boolean mibibytes = false; + + @Parameter(names = "--dot-tolerance", description = "Tolerance for dot kernel verification") + public double dotTolerance = 1.0e-8; + + public boolean isVerboseBenchmark() { + return !list && !csv; + } + } + + public static final class Config { + public final Options options; + public final Benchmark benchmark; + public final int typeSize; + public final Class evidence; + public final T ulp, scalar, initA, initB, initC; + + public Config( + Options options, + Benchmark benchmark, + int typeSize, + Class evidence, + T ulp, + T scalar, + T initA, + T initB, + T initC) { + this.options = Objects.requireNonNull(options); + this.benchmark = Objects.requireNonNull(benchmark); + this.typeSize = typeSize; + this.evidence = Objects.requireNonNull(evidence); + this.ulp = Objects.requireNonNull(ulp); + this.scalar = Objects.requireNonNull(scalar); + this.initA = Objects.requireNonNull(initA); + this.initB = Objects.requireNonNull(initB); + this.initC = Objects.requireNonNull(initC); + } + } + + static final class Implementation { + final String name; + final Function, JavaStream> makeFloat; + final Function, JavaStream> makeDouble; + + Implementation( + String name, + Function, JavaStream> makeFloat, + Function, JavaStream> makeDouble) { + this.name = Objects.requireNonNull(name); + this.makeFloat = Objects.requireNonNull(makeFloat); + this.makeDouble = Objects.requireNonNull(makeDouble); + } + } + + static boolean run( + String name, Config config, Function, JavaStream> mkStream) { + + Options opt = config.options; + + int arrayBytes = opt.arraysize * config.typeSize; + int totalBytes = arrayBytes * 3; + + String megaSuffix = opt.mibibytes ? "MiB" : "MB"; + String gigaSuffix = opt.mibibytes ? "GiB" : "GB"; + + double megaScale = opt.mibibytes ? Math.pow(2.0, -20) : 1.0e-6; + double gigaScale = opt.mibibytes ? Math.pow(2.0, -30) : 1.0e-9; + + if (!opt.csv) { + + String vendor = System.getProperty("java.vendor"); + String ver = System.getProperty("java.version"); + String home = System.getProperty("java.home"); + + System.out.println("BabelStream"); + System.out.printf("Version: %s%n", VERSION); + System.out.printf( + "Implementation: %s (Java %s; %s; JAVA_HOME=%s)%n", name, ver, vendor, home); + final String benchmarkName; + switch (config.benchmark) { + case NSTREAM: + benchmarkName = "nstream"; + break; + case TRIAD: + benchmarkName = "triad"; + break; + case ALL: + benchmarkName = "all"; + break; + default: + throw new AssertionError("Unexpected value: " + config.benchmark); + } + System.out.println("Running " + benchmarkName + " " + opt.numtimes + " times"); + + if (config.benchmark == Benchmark.TRIAD) { + System.out.println("Number of elements: " + opt.arraysize); + } + + System.out.println("Precision: " + (opt.useFloat ? "float" : "double")); + System.out.printf( + "Array size: %.1f %s (=%.1f %s)%n", + (megaScale * arrayBytes), megaSuffix, (gigaScale * arrayBytes), gigaSuffix); + System.out.printf( + "Total size: %.1f %s (=%.1f %s)%n", + (megaScale * totalBytes), megaSuffix, (gigaScale * totalBytes), gigaSuffix); + } + + JavaStream stream = mkStream.apply(config); + + stream.initArrays(); + + final boolean ok; + switch (config.benchmark) { + case ALL: + Entry, T> results = stream.runAll(opt.numtimes); + ok = checkSolutions(stream.data(), config, Optional.of(results.getValue())); + Timings timings = results.getKey(); + tabulateCsv( + opt.csv, + mkCsvRow(timings.copy, "Copy", 2 * arrayBytes, megaScale, opt), + mkCsvRow(timings.mul, "Mul", 2 * arrayBytes, megaScale, opt), + mkCsvRow(timings.add, "Add", 3 * arrayBytes, megaScale, opt), + mkCsvRow(timings.triad, "Triad", 3 * arrayBytes, megaScale, opt), + mkCsvRow(timings.dot, "Dot", 2 * arrayBytes, megaScale, opt)); + break; + case NSTREAM: + List nstreamResults = stream.runNStream(opt.numtimes); + ok = checkSolutions(stream.data(), config, Optional.empty()); + tabulateCsv(opt.csv, mkCsvRow(nstreamResults, "Nstream", 4 * arrayBytes, megaScale, opt)); + break; + case TRIAD: + Duration triadResult = stream.runTriad(opt.numtimes); + ok = checkSolutions(stream.data(), config, Optional.empty()); + int triadTotalBytes = 3 * arrayBytes * opt.numtimes; + double bandwidth = megaScale * (triadTotalBytes / durationToSeconds(triadResult)); + System.out.printf("Runtime (seconds): %.5f", durationToSeconds(triadResult)); + System.out.printf("Bandwidth (%s/s): %.3f ", gigaSuffix, bandwidth); + break; + default: + throw new AssertionError(); + } + return ok; + } + + private static boolean checkWithinTolerance( + String name, T[] xs, T gold, T tolerance) { + // it's ok to default to double for error calculation + double error = + Arrays.stream(xs) + .mapToDouble(x -> Math.abs(minus(x, gold).doubleValue())) + .summaryStatistics() + .getAverage(); + boolean failed = error > tolerance.doubleValue(); + if (failed) { + System.err.printf("Validation failed on %s. Average error %s%n", name, error); + } + return !failed; + } + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + static boolean checkSolutions( + Data data, Config config, Optional dotSum) { + T goldA = config.initA; + T goldB = config.initB; + T goldC = config.initC; + + for (int i = 0; i < config.options.numtimes; i++) { + switch (config.benchmark) { + case ALL: + goldC = goldA; + goldB = times(config.scalar, goldC); + goldC = plus(goldA, goldB); + goldA = plus(goldB, times(config.scalar, goldC)); + break; + case TRIAD: + goldA = plus(goldB, times(config.scalar, goldC)); + break; + case NSTREAM: + goldA = plus(goldA, plus(goldB, times(config.scalar, goldC))); + break; + } + } + + T tolerance = times(config.ulp, from(config.evidence, 100)); + boolean aValid = checkWithinTolerance("a", data.a, goldA, tolerance); + boolean bValid = checkWithinTolerance("b", data.b, goldB, tolerance); + boolean cValid = checkWithinTolerance("c", data.c, goldC, tolerance); + + final T finalGoldA = goldA; + final T finalGoldB = goldB; + boolean sumValid = + dotSum + .map( + actual -> { + T goldSum = + times( + times(finalGoldA, finalGoldB), + from(config.evidence, config.options.arraysize)); + double error = Math.abs(divide(minus(actual, goldSum), goldSum).doubleValue()); + boolean failed = error > config.options.dotTolerance; + if (failed) { + System.err.printf( + "Validation failed on sum. Error %s \nSum was %s but should be %s%n", + error, actual, goldSum); + } + return !failed; + }) + .orElse(true); + + return aValid && bValid && cValid && sumValid; + } + + private static double durationToSeconds(Duration d) { + return d.toNanos() / (double) TimeUnit.SECONDS.toNanos(1); + } + + private static List> mkCsvRow( + List xs, String name, int totalBytes, double megaScale, Options opt) { + DoubleSummaryStatistics stats = + xs.stream().skip(1).mapToDouble(Main::durationToSeconds).summaryStatistics(); + if (stats.getCount() <= 0) { + throw new IllegalArgumentException("No min/max for " + name + "(size=" + totalBytes + ")"); + } + double mbps = megaScale * (double) totalBytes / stats.getMin(); + return opt.csv + ? Arrays.asList( + new SimpleImmutableEntry<>("function", name), + new SimpleImmutableEntry<>("num_times", opt.numtimes + ""), + new SimpleImmutableEntry<>("n_elements", opt.arraysize + ""), + new SimpleImmutableEntry<>("sizeof", totalBytes + ""), + new SimpleImmutableEntry<>( + "max_m" + (opt.mibibytes ? "i" : "") + "bytes_per_sec", mbps + ""), + new SimpleImmutableEntry<>("min_runtime", stats.getMin() + ""), + new SimpleImmutableEntry<>("max_runtime", stats.getMax() + ""), + new SimpleImmutableEntry<>("avg_runtime", stats.getAverage() + "")) + : Arrays.asList( + new SimpleImmutableEntry<>("Function", name), + new SimpleImmutableEntry<>( + "M" + (opt.mibibytes ? "i" : "") + "Bytes/sec", String.format("%.3f", mbps)), + new SimpleImmutableEntry<>("Min (sec)", String.format("%.5f", stats.getMin())), + new SimpleImmutableEntry<>("Max", String.format("%.5f", stats.getMax())), + new SimpleImmutableEntry<>("Average", String.format("%.5f", stats.getAverage()))); + } + + private static String padSpace(String s, int length) { + if (length == 0) return s; + return String.format("%1$-" + length + "s", s); + } + + @SafeVarargs + @SuppressWarnings("varargs") + private static void tabulateCsv(boolean csv, List>... rows) { + if (rows.length == 0) throw new IllegalArgumentException("Empty tabulation"); + int padding = csv ? 0 : 12; + String sep = csv ? "," : ""; + System.out.println( + rows[0].stream().map(x -> padSpace(x.getKey(), padding)).collect(Collectors.joining(sep))); + for (List> row : rows) { + System.out.println( + row.stream().map(x -> padSpace(x.getValue(), padding)).collect(Collectors.joining(sep))); + } + } + + private static final String VERSION = "3.4"; + + private static final float START_SCALAR = 0.4f; + private static final float START_A = 0.1f; + private static final float START_B = 0.2f; + private static final float START_C = 0.0f; + + private static final List IMPLEMENTATIONS = + Arrays.asList( + new Implementation("jdk-stream", JdkStreams.FLOAT, JdkStreams.DOUBLE), + new Implementation("jdk-plain", PlainStream.FLOAT, PlainStream.DOUBLE), + new Implementation("tornadovm", TornadoVMStreams.FLOAT, TornadoVMStreams.DOUBLE), + new Implementation("aparapi", AparapiStreams.FLOAT, AparapiStreams.DOUBLE)); + + public static int run(String[] args) { + Options opt = new Options(); + JCommander.newBuilder().addObject(opt).build().parse(args); + + final Benchmark benchmark; + if (opt.nstreamOnly && opt.triadOnly) + throw new RuntimeException( + "Both triad and nstream are enabled, pick one or omit both to run all benchmarks"); + else if (opt.nstreamOnly) benchmark = Benchmark.NSTREAM; + else if (opt.triadOnly) benchmark = Benchmark.TRIAD; + else benchmark = Benchmark.ALL; + + final Config floatConfig = + new Config<>( + opt, + benchmark, + Float.BYTES, + Float.class, // XXX not Float.TYPE, we want the boxed one + Math.ulp(1.f), + START_SCALAR, + START_A, + START_B, + START_C); + final Config doubleConfig = + new Config<>( + opt, + benchmark, + Double.BYTES, + Double.class, // XXX not Double.TYPE, we want the boxed one + Math.ulp(1.d), + (double) START_SCALAR, + (double) START_A, + (double) START_B, + (double) START_C); + + if (opt.list) { + System.out.println("Set implementation with --impl and device with --device :"); + for (Implementation entry : IMPLEMENTATIONS) { + System.out.println("Implementation: " + entry.name); + try { + List devices = entry.makeDouble.apply(doubleConfig).listDevices(); + for (int i = 0; i < devices.size(); i++) { + System.out.println("\t[" + i + "] " + devices.get(i)); + } + } catch (Exception e) { + System.out.println("\t(Unsupported: " + e.getMessage() + ")"); + } + } + return 0; + } + + String implName = (opt.impl.isEmpty()) ? IMPLEMENTATIONS.get(0).name : opt.impl; + Implementation impl = + IMPLEMENTATIONS.stream() + .filter(x -> implName.compareToIgnoreCase(x.name) == 0) + .findFirst() + .orElseThrow( + () -> + new IllegalArgumentException("Implementation " + opt.impl + " does not exist")); + + boolean ok = + opt.useFloat + ? run(impl.name, floatConfig, impl.makeFloat) + : run(impl.name, doubleConfig, impl.makeDouble); + + return ok ? 0 : 1; + } + + public static void main(String[] args) { + System.exit(run(args)); + } +} diff --git a/java-stream/src/main/java/javastream/aparapi/AparapiStreams.java b/java-stream/src/main/java/javastream/aparapi/AparapiStreams.java new file mode 100644 index 0000000..ab2de52 --- /dev/null +++ b/java-stream/src/main/java/javastream/aparapi/AparapiStreams.java @@ -0,0 +1,129 @@ +package javastream.aparapi; + +import com.aparapi.device.Device; +import com.aparapi.device.Device.TYPE; +import com.aparapi.device.JavaDevice; +import com.aparapi.device.OpenCLDevice; +import com.aparapi.internal.kernel.KernelManager; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javastream.JavaStream; +import javastream.Main.Config; + +public final class AparapiStreams { + + private AparapiStreams() {} + + public static final Function, JavaStream> DOUBLE = + config -> new Generic<>(config, SpecialisedDoubleKernel::new); + + public static final Function, JavaStream> FLOAT = + config -> new Generic<>(config, SpecialisedFloatKernel::new); + + private static List enumerateDevices() { + + // JavaDevice.SEQUENTIAL doesn't work when arraysize > 1, so we omit it entirely + Stream cpuDevices = Stream.of(JavaDevice.ALTERNATIVE_ALGORITHM); + + Stream clDevices = + Stream.of(TYPE.values()).map(OpenCLDevice::listDevices).flatMap(Collection::stream); + + return Stream.concat(clDevices, cpuDevices).collect(Collectors.toList()); + } + + private static String deviceName(Device device) { + return device.toString(); + } + + private static final class Generic extends JavaStream { + + private final GenericAparapiStreamKernel kernels; + + Generic(Config config, GenericAparapiStreamKernel.Factory factory) { + super(config); + Device device = enumerateDevices().get(config.options.device); + + final int numGroups; + final int workGroupSize; + if (device instanceof JavaDevice) { + numGroups = Runtime.getRuntime().availableProcessors(); + workGroupSize = + config.typeSize * 2; // closest thing to CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE + + } else if (device instanceof OpenCLDevice) { + numGroups = ((OpenCLDevice) device).getMaxComputeUnits(); + workGroupSize = device.getMaxWorkGroupSize(); + } else { + throw new AssertionError("Unknown device type " + device.getClass()); + } + + if (config.options.isVerboseBenchmark()) { + System.out.println("Using Aparapi OpenCL device: " + device); + System.out.println(" - numGroups : " + numGroups); + System.out.println(" - workGroupSize : " + workGroupSize); + String showCL = System.getProperty("com.aparapi.enableShowGeneratedOpenCL"); + if (showCL == null || !showCL.equals("true")) { + System.out.println( + "(Add `-Dcom.aparapi.enableShowGeneratedOpenCL=true` to show generated OpenCL source)"); + } + } + + LinkedHashSet candidate = new LinkedHashSet<>(); + candidate.add(device); + + kernels = factory.create(config, numGroups, workGroupSize); + KernelManager.instance().setPreferredDevices(kernels, candidate); + } + + @Override + public List listDevices() { + return enumerateDevices().stream() + .map(AparapiStreams::deviceName) + .collect(Collectors.toList()); + } + + @Override + public void initArrays() { + kernels.init(); + } + + @Override + public void copy() { + kernels.copy(); + } + + @Override + public void mul() { + kernels.mul(); + } + + @Override + public void add() { + kernels.add(); + } + + @Override + public void triad() { + kernels.triad(); + } + + @Override + public void nstream() { + kernels.nstream(); + } + + @Override + public T dot() { + return kernels.dot(); + } + + @Override + public Data data() { + return kernels.syncAndDispose(); + } + } +} diff --git a/java-stream/src/main/java/javastream/aparapi/GenericAparapiStreamKernel.java b/java-stream/src/main/java/javastream/aparapi/GenericAparapiStreamKernel.java new file mode 100644 index 0000000..526b472 --- /dev/null +++ b/java-stream/src/main/java/javastream/aparapi/GenericAparapiStreamKernel.java @@ -0,0 +1,68 @@ +package javastream.aparapi; + +import com.aparapi.Kernel; +import com.aparapi.Range; +import javastream.JavaStream.Data; +import javastream.Main.Config; + +abstract class GenericAparapiStreamKernel extends Kernel { + + protected static final int FN_COPY = 1; + protected static final int FN_MUL = 2; + protected static final int FN_ADD = 3; + protected static final int FN_TRIAD = 4; + protected static final int FN_NSTREAM = 5; + protected static final int FN_DOT = 6; + protected final Config config; + protected final int arraysize, numGroups, workGroupSize; + + interface Factory { + GenericAparapiStreamKernel create(Config config, int numGroups, int workGroupSize); + } + + GenericAparapiStreamKernel(Config config, int numGroups, int workGroupSize) { + this.config = config; + this.arraysize = config.options.arraysize; + this.numGroups = numGroups; + this.workGroupSize = workGroupSize; + setExplicit(true); + } + + protected int function; + + public abstract void init(); + + public void copy() { + function = FN_COPY; + execute(arraysize); + } + + public void mul() { + function = FN_MUL; + execute(arraysize); + } + + public void add() { + function = FN_ADD; + execute(arraysize); + } + + public void triad() { + function = FN_TRIAD; + execute(arraysize); + } + + public void nstream() { + function = FN_NSTREAM; + execute(arraysize); + } + + protected Kernel partialDot() { + function = FN_DOT; + return execute(Range.create(numGroups * workGroupSize, workGroupSize)); + } + + abstract T dot(); + + abstract Data syncAndDispose(); +} diff --git a/java-stream/src/main/java/javastream/aparapi/SpecialisedDoubleKernel.java b/java-stream/src/main/java/javastream/aparapi/SpecialisedDoubleKernel.java new file mode 100644 index 0000000..56a59af --- /dev/null +++ b/java-stream/src/main/java/javastream/aparapi/SpecialisedDoubleKernel.java @@ -0,0 +1,74 @@ +package javastream.aparapi; + +import java.util.Arrays; +import javastream.JavaStream; +import javastream.JavaStream.Data; +import javastream.Main.Config; + +final class SpecialisedDoubleKernel extends GenericAparapiStreamKernel { + private final double scalar; + final double[] a, b, c; + private final double[] partialSum; + @Local private final double[] workGroupSum; + + SpecialisedDoubleKernel(Config config, int numGroups, int workGroupSize) { + super(config, numGroups, workGroupSize); + this.scalar = config.scalar; + this.a = new double[this.arraysize]; + this.b = new double[this.arraysize]; + this.c = new double[this.arraysize]; + + this.partialSum = new double[numGroups]; + this.workGroupSum = new double[workGroupSize]; + } + + @SuppressWarnings("DuplicatedCode") + @Override + public void run() { + int i = getGlobalId(); + if (function == FN_COPY) { + c[i] = a[i]; + } else if (function == FN_MUL) { + b[i] = scalar * c[i]; + } else if (function == FN_ADD) { + c[i] = a[i] + b[i]; + } else if (function == FN_TRIAD) { + a[i] = b[i] + scalar * c[i]; + } else if (function == FN_NSTREAM) { + a[i] += b[i] + scalar * c[i]; + } else if (function == FN_DOT) { + int localId = getLocalId(0); + workGroupSum[localId] = 0.0; + for (; i < arraysize; i += getGlobalSize(0)) workGroupSum[localId] += a[i] * b[i]; + for (int offset = getLocalSize(0) / 2; offset > 0; offset /= 2) { + localBarrier(); + if (localId < offset) { + workGroupSum[localId] += workGroupSum[localId + offset]; + } + } + if (localId == 0) partialSum[getGroupId(0)] = workGroupSum[localId]; + } + } + + @Override + public void init() { + Arrays.fill(a, config.initA); + Arrays.fill(b, config.initB); + Arrays.fill(c, config.initC); + put(a).put(b).put(c); + } + + @Override + public Double dot() { + partialDot().get(partialSum); + double sum = 0; + for (double v : partialSum) sum += v; + return sum; + } + + @Override + public Data syncAndDispose() { + get(a).get(b).get(c).dispose(); + return new Data<>(JavaStream.boxed(a), JavaStream.boxed(b), JavaStream.boxed(c)); + } +} diff --git a/java-stream/src/main/java/javastream/aparapi/SpecialisedFloatKernel.java b/java-stream/src/main/java/javastream/aparapi/SpecialisedFloatKernel.java new file mode 100644 index 0000000..6919f06 --- /dev/null +++ b/java-stream/src/main/java/javastream/aparapi/SpecialisedFloatKernel.java @@ -0,0 +1,75 @@ +package javastream.aparapi; + +import static javastream.JavaStream.boxed; + +import java.util.Arrays; +import javastream.JavaStream.Data; +import javastream.Main.Config; + +final class SpecialisedFloatKernel extends GenericAparapiStreamKernel { + private final float scalar; + final float[] a, b, c; + private final float[] partialSum; + @Local private final float[] workGroupSum; + + SpecialisedFloatKernel(Config config, int numGroups, int workGroupSize) { + super(config, numGroups, workGroupSize); + this.scalar = config.scalar; + this.a = new float[this.arraysize]; + this.b = new float[this.arraysize]; + this.c = new float[this.arraysize]; + + this.partialSum = new float[numGroups]; + this.workGroupSum = new float[workGroupSize]; + } + + @SuppressWarnings("DuplicatedCode") + @Override + public void run() { + int i = getGlobalId(); + if (function == FN_COPY) { + c[i] = a[i]; + } else if (function == FN_MUL) { + b[i] = scalar * c[i]; + } else if (function == FN_ADD) { + c[i] = a[i] + b[i]; + } else if (function == FN_TRIAD) { + a[i] = b[i] + scalar * c[i]; + } else if (function == FN_NSTREAM) { + a[i] += b[i] + scalar * c[i]; + } else if (function == FN_DOT) { + int localId = getLocalId(0); + workGroupSum[localId] = 0.f; + for (; i < arraysize; i += getGlobalSize(0)) workGroupSum[localId] += a[i] * b[i]; + for (int offset = getLocalSize(0) / 2; offset > 0; offset /= 2) { + localBarrier(); + if (localId < offset) { + workGroupSum[localId] += workGroupSum[localId + offset]; + } + } + if (localId == 0) partialSum[getGroupId(0)] = workGroupSum[localId]; + } + } + + @Override + public void init() { + Arrays.fill(a, config.initA); + Arrays.fill(b, config.initB); + Arrays.fill(c, config.initC); + put(a).put(b).put(c); + } + + @Override + public Float dot() { + partialDot().get(partialSum); + float sum = 0; + for (float v : partialSum) sum += v; + return sum; + } + + @Override + public Data syncAndDispose() { + get(a).get(b).get(c).dispose(); + return new Data<>(boxed(a), boxed(b), boxed(c)); + } +} diff --git a/java-stream/src/main/java/javastream/jdk/GenericPlainStream.java b/java-stream/src/main/java/javastream/jdk/GenericPlainStream.java new file mode 100644 index 0000000..7f210fa --- /dev/null +++ b/java-stream/src/main/java/javastream/jdk/GenericPlainStream.java @@ -0,0 +1,92 @@ +package javastream.jdk; + +import static javastream.FractionalMaths.from; +import static javastream.FractionalMaths.plus; +import static javastream.FractionalMaths.times; + +import java.lang.reflect.Array; +import java.util.Collections; +import java.util.List; +import javastream.JavaStream; +import javastream.Main.Config; + +final class GenericPlainStream extends JavaStream { + + private final T[] a; + private final T[] b; + private final T[] c; + + @SuppressWarnings("unchecked") + GenericPlainStream(Config config) { + super(config); + this.a = (T[]) Array.newInstance(config.evidence, config.options.arraysize); + this.b = (T[]) Array.newInstance(config.evidence, config.options.arraysize); + this.c = (T[]) Array.newInstance(config.evidence, config.options.arraysize); + } + + @Override + public List listDevices() { + return Collections.singletonList("JVM"); + } + + @Override + public void initArrays() { + for (int i = 0; i < config.options.arraysize; i++) { + a[i] = config.initA; + b[i] = config.initB; + c[i] = config.initC; + } + } + + @SuppressWarnings("ManualArrayCopy") + @Override + public void copy() { + for (int i = 0; i < config.options.arraysize; i++) { + c[i] = a[i]; + } + } + + @Override + public void mul() { + for (int i = 0; i < config.options.arraysize; i++) { + b[i] = times(config.scalar, c[i]); + } + } + + @Override + public void add() { + + for (int i = 0; i < config.options.arraysize; i++) { + c[i] = plus(a[i], b[i]); + } + } + + @Override + public void triad() { + + for (int i = 0; i < config.options.arraysize; i++) { + a[i] = plus(b[i], times(config.scalar, c[i])); + } + } + + @Override + public void nstream() { + for (int i = 0; i < config.options.arraysize; i++) { + a[i] = plus(a[i], plus(b[i], times(config.scalar, c[i]))); + } + } + + @Override + public T dot() { + T acc = from(config.evidence, 0); + for (int i = 0; i < config.options.arraysize; i++) { + acc = plus(acc, times(a[i], b[i])); + } + return acc; + } + + @Override + public Data data() { + return new Data<>(a, b, c); + } +} diff --git a/java-stream/src/main/java/javastream/jdk/GenericStream.java b/java-stream/src/main/java/javastream/jdk/GenericStream.java new file mode 100644 index 0000000..1e65b8f --- /dev/null +++ b/java-stream/src/main/java/javastream/jdk/GenericStream.java @@ -0,0 +1,86 @@ +package javastream.jdk; + +import static javastream.FractionalMaths.from; +import static javastream.FractionalMaths.plus; +import static javastream.FractionalMaths.times; + +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.IntStream; +import javastream.FractionalMaths; +import javastream.JavaStream; +import javastream.Main.Config; + +/** + * We use + * + *

    Arrays.parallelSetAll
    + * + *

    here as it internally calls + * + *

    IntStream.range(0, array.length).parallel().forEach(...)
    + */ +final class GenericStream extends JavaStream { + + private final T[] a, b, c; + + @SuppressWarnings("unchecked") + GenericStream(Config config) { + super(config); + this.a = (T[]) Array.newInstance(config.evidence, config.options.arraysize); + this.b = (T[]) Array.newInstance(config.evidence, config.options.arraysize); + this.c = (T[]) Array.newInstance(config.evidence, config.options.arraysize); + } + + @Override + public List listDevices() { + return Collections.singletonList("JVM"); + } + + @Override + public void initArrays() { + Arrays.parallelSetAll(a, i -> config.initA); + Arrays.parallelSetAll(b, i -> config.initB); + Arrays.parallelSetAll(c, i -> config.initC); + } + + @Override + public void copy() { + Arrays.parallelSetAll(c, i -> a[i]); + } + + @Override + public void mul() { + Arrays.parallelSetAll(b, i -> times(config.scalar, c[i])); + } + + @Override + public void add() { + Arrays.parallelSetAll(c, i -> plus(a[i], b[i])); + } + + @Override + public void triad() { + Arrays.parallelSetAll(a, i -> plus(b[i], times(config.scalar, c[i]))); + } + + @Override + public void nstream() { + Arrays.parallelSetAll(a, i -> plus(a[i], plus(b[i], times(config.scalar, c[i])))); + } + + @Override + public T dot() { + return IntStream.range(0, config.options.arraysize) + .parallel() + .mapToObj(i -> times(a[i], b[i])) + .reduce(from(config.evidence, 0), FractionalMaths::plus); + } + + @Override + public Data data() { + return new Data<>(a, b, c); + } +} diff --git a/java-stream/src/main/java/javastream/jdk/JdkStreams.java b/java-stream/src/main/java/javastream/jdk/JdkStreams.java new file mode 100644 index 0000000..5b58be7 --- /dev/null +++ b/java-stream/src/main/java/javastream/jdk/JdkStreams.java @@ -0,0 +1,26 @@ +package javastream.jdk; + +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.function.Function; +import javastream.JavaStream; +import javastream.JavaStream.EnumeratedStream; +import javastream.Main.Config; + +public final class JdkStreams { + + private JdkStreams() {} + + public static final Function, JavaStream> FLOAT = + config -> + new EnumeratedStream<>( + config, + new SimpleImmutableEntry<>("specialised", SpecialisedFloatStream::new), + new SimpleImmutableEntry<>("generic", GenericStream::new)); + + public static final Function, JavaStream> DOUBLE = + config -> + new EnumeratedStream<>( + config, + new SimpleImmutableEntry<>("specialised", SpecialisedDoubleStream::new), + new SimpleImmutableEntry<>("generic", GenericStream::new)); +} diff --git a/java-stream/src/main/java/javastream/jdk/PlainStream.java b/java-stream/src/main/java/javastream/jdk/PlainStream.java new file mode 100644 index 0000000..f9281e8 --- /dev/null +++ b/java-stream/src/main/java/javastream/jdk/PlainStream.java @@ -0,0 +1,26 @@ +package javastream.jdk; + +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.function.Function; +import javastream.JavaStream; +import javastream.JavaStream.EnumeratedStream; +import javastream.Main.Config; + +public final class PlainStream { + + private PlainStream() {} + + public static final Function, JavaStream> FLOAT = + config -> + new EnumeratedStream<>( + config, + new SimpleImmutableEntry<>("specialised", SpecialisedPlainFloatStream::new), + new SimpleImmutableEntry<>("generic", GenericPlainStream::new)); + + public static final Function, JavaStream> DOUBLE = + config -> + new EnumeratedStream<>( + config, + new SimpleImmutableEntry<>("specialised", SpecialisedPlainDoubleStream::new), + new SimpleImmutableEntry<>("generic", GenericPlainStream::new)); +} diff --git a/java-stream/src/main/java/javastream/jdk/SpecialisedDoubleStream.java b/java-stream/src/main/java/javastream/jdk/SpecialisedDoubleStream.java new file mode 100644 index 0000000..26406a6 --- /dev/null +++ b/java-stream/src/main/java/javastream/jdk/SpecialisedDoubleStream.java @@ -0,0 +1,84 @@ +package javastream.jdk; + +import java.util.Collections; +import java.util.List; +import java.util.stream.IntStream; +import javastream.JavaStream; +import javastream.Main.Config; + +final class SpecialisedDoubleStream extends JavaStream { + + private final double[] a, b, c; + + SpecialisedDoubleStream(Config config) { + super(config); + this.a = new double[config.options.arraysize]; + this.b = new double[config.options.arraysize]; + this.c = new double[config.options.arraysize]; + } + + @Override + public List listDevices() { + return Collections.singletonList("JVM"); + } + + @Override + public void initArrays() { + IntStream.range(0, config.options.arraysize) // + .parallel() + .forEach( + i -> { + a[i] = config.initA; + b[i] = config.initB; + c[i] = config.initC; + }); + } + + @Override + public void copy() { + IntStream.range(0, config.options.arraysize) // + .parallel() + .forEach(i -> c[i] = a[i]); + } + + @Override + public void mul() { + IntStream.range(0, config.options.arraysize) // + .parallel() + .forEach(i -> b[i] = config.scalar * c[i]); + } + + @Override + public void add() { + IntStream.range(0, config.options.arraysize) // + .parallel() + .forEach(i -> c[i] = a[i] + b[i]); + } + + @Override + public void triad() { + IntStream.range(0, config.options.arraysize) // + .parallel() + .forEach(i -> a[i] = b[i] + config.scalar * c[i]); + } + + @Override + public void nstream() { + IntStream.range(0, config.options.arraysize) // + .parallel() + .forEach(i -> a[i] += b[i] + config.scalar * c[i]); + } + + @Override + public Double dot() { + return IntStream.range(0, config.options.arraysize) + .parallel() + .mapToDouble(i -> a[i] * b[i]) + .reduce(0f, Double::sum); + } + + @Override + public Data data() { + return new Data<>(boxed(a), boxed(b), boxed(c)); + } +} diff --git a/java-stream/src/main/java/javastream/jdk/SpecialisedFloatStream.java b/java-stream/src/main/java/javastream/jdk/SpecialisedFloatStream.java new file mode 100644 index 0000000..6c414c1 --- /dev/null +++ b/java-stream/src/main/java/javastream/jdk/SpecialisedFloatStream.java @@ -0,0 +1,84 @@ +package javastream.jdk; + +import java.util.Collections; +import java.util.List; +import java.util.stream.IntStream; +import javastream.JavaStream; +import javastream.Main.Config; + +final class SpecialisedFloatStream extends JavaStream { + + private final float[] a, b, c; + + SpecialisedFloatStream(Config config) { + super(config); + this.a = new float[config.options.arraysize]; + this.b = new float[config.options.arraysize]; + this.c = new float[config.options.arraysize]; + } + + @Override + public List listDevices() { + return Collections.singletonList("JVM"); + } + + @Override + public void initArrays() { + IntStream.range(0, config.options.arraysize) // + .parallel() + .forEach( + i -> { + a[i] = config.initA; + b[i] = config.initB; + c[i] = config.initC; + }); + } + + @Override + public void copy() { + IntStream.range(0, config.options.arraysize) // + .parallel() + .forEach(i -> c[i] = a[i]); + } + + @Override + public void mul() { + IntStream.range(0, config.options.arraysize) // + .parallel() + .forEach(i -> b[i] = config.scalar * c[i]); + } + + @Override + public void add() { + IntStream.range(0, config.options.arraysize) // + .parallel() + .forEach(i -> c[i] = a[i] + b[i]); + } + + @Override + public void triad() { + IntStream.range(0, config.options.arraysize) // + .parallel() + .forEach(i -> a[i] = b[i] + config.scalar * c[i]); + } + + @Override + public void nstream() { + IntStream.range(0, config.options.arraysize) // + .parallel() + .forEach(i -> a[i] += b[i] + config.scalar * c[i]); + } + + @Override + public Float dot() { + return IntStream.range(0, config.options.arraysize) // + .parallel() + .mapToObj(i -> a[i] * b[i]) // XXX there isn't a specialised Stream for floats + .reduce(0f, Float::sum); + } + + @Override + public Data data() { + return new Data<>(boxed(a), boxed(b), boxed(c)); + } +} diff --git a/java-stream/src/main/java/javastream/jdk/SpecialisedPlainDoubleStream.java b/java-stream/src/main/java/javastream/jdk/SpecialisedPlainDoubleStream.java new file mode 100644 index 0000000..afda2ef --- /dev/null +++ b/java-stream/src/main/java/javastream/jdk/SpecialisedPlainDoubleStream.java @@ -0,0 +1,84 @@ +package javastream.jdk; + +import java.util.Collections; +import java.util.List; +import javastream.JavaStream; +import javastream.Main.Config; + +final class SpecialisedPlainDoubleStream extends JavaStream { + + private final double[] a; + private final double[] b; + private final double[] c; + + SpecialisedPlainDoubleStream(Config config) { + super(config); + this.a = new double[config.options.arraysize]; + this.b = new double[config.options.arraysize]; + this.c = new double[config.options.arraysize]; + } + + @Override + public List listDevices() { + return Collections.singletonList("JVM"); + } + + @Override + public void initArrays() { + for (int i = 0; i < config.options.arraysize; i++) { + a[i] = config.initA; + b[i] = config.initB; + c[i] = config.initC; + } + } + + @SuppressWarnings("ManualArrayCopy") + @Override + public void copy() { + for (int i = 0; i < config.options.arraysize; i++) { + c[i] = a[i]; + } + } + + @Override + public void mul() { + for (int i = 0; i < config.options.arraysize; i++) { + b[i] = config.scalar * c[i]; + } + } + + @Override + public void add() { + for (int i = 0; i < config.options.arraysize; i++) { + c[i] = a[i] + b[i]; + } + } + + @Override + public void triad() { + for (int i = 0; i < config.options.arraysize; i++) { + a[i] = b[i] + config.scalar * c[i]; + } + } + + @Override + public void nstream() { + for (int i = 0; i < config.options.arraysize; i++) { + a[i] += b[i] + config.scalar * c[i]; + } + } + + @Override + public Double dot() { + double acc = 0f; + for (int i = 0; i < config.options.arraysize; i++) { + acc += a[i] * b[i]; + } + return acc; + } + + @Override + public Data data() { + return new Data<>(boxed(a), boxed(b), boxed(c)); + } +} diff --git a/java-stream/src/main/java/javastream/jdk/SpecialisedPlainFloatStream.java b/java-stream/src/main/java/javastream/jdk/SpecialisedPlainFloatStream.java new file mode 100644 index 0000000..9ccee53 --- /dev/null +++ b/java-stream/src/main/java/javastream/jdk/SpecialisedPlainFloatStream.java @@ -0,0 +1,84 @@ +package javastream.jdk; + +import java.util.Collections; +import java.util.List; +import javastream.JavaStream; +import javastream.Main.Config; + +final class SpecialisedPlainFloatStream extends JavaStream { + + private final float[] a; + private final float[] b; + private final float[] c; + + SpecialisedPlainFloatStream(Config config) { + super(config); + this.a = new float[config.options.arraysize]; + this.b = new float[config.options.arraysize]; + this.c = new float[config.options.arraysize]; + } + + @Override + public List listDevices() { + return Collections.singletonList("JVM"); + } + + @Override + public void initArrays() { + for (int i = 0; i < config.options.arraysize; i++) { + a[i] = config.initA; + b[i] = config.initB; + c[i] = config.initC; + } + } + + @SuppressWarnings("ManualArrayCopy") + @Override + public void copy() { + for (int i = 0; i < config.options.arraysize; i++) { + c[i] = a[i]; + } + } + + @Override + public void mul() { + for (int i = 0; i < config.options.arraysize; i++) { + b[i] = config.scalar * c[i]; + } + } + + @Override + public void add() { + for (int i = 0; i < config.options.arraysize; i++) { + c[i] = a[i] + b[i]; + } + } + + @Override + public void triad() { + for (int i = 0; i < config.options.arraysize; i++) { + a[i] = b[i] + config.scalar * c[i]; + } + } + + @Override + public void nstream() { + for (int i = 0; i < config.options.arraysize; i++) { + a[i] += b[i] + config.scalar * c[i]; + } + } + + @Override + public Float dot() { + float acc = 0f; + for (int i = 0; i < config.options.arraysize; i++) { + acc += a[i] * b[i]; + } + return acc; + } + + @Override + public Data data() { + return new Data<>(boxed(a), boxed(b), boxed(c)); + } +} diff --git a/java-stream/src/main/java/javastream/tornadovm/GenericTornadoVMStream.java b/java-stream/src/main/java/javastream/tornadovm/GenericTornadoVMStream.java new file mode 100644 index 0000000..d936df6 --- /dev/null +++ b/java-stream/src/main/java/javastream/tornadovm/GenericTornadoVMStream.java @@ -0,0 +1,98 @@ +package javastream.tornadovm; + +import java.util.List; +import java.util.stream.Collectors; +import javastream.JavaStream; +import javastream.Main.Config; +import uk.ac.manchester.tornado.api.TaskSchedule; +import uk.ac.manchester.tornado.api.TornadoRuntimeCI; +import uk.ac.manchester.tornado.api.common.TornadoDevice; +import uk.ac.manchester.tornado.api.runtime.TornadoRuntime; + +abstract class GenericTornadoVMStream extends JavaStream { + + protected final TornadoDevice device; + + protected TaskSchedule copyTask; + protected TaskSchedule mulTask; + protected TaskSchedule addTask; + protected TaskSchedule triadTask; + protected TaskSchedule nstreamTask; + protected TaskSchedule dotTask; + + GenericTornadoVMStream(Config config) { + super(config); + + try { + TornadoRuntimeCI runtime = TornadoRuntime.getTornadoRuntime(); + List devices = TornadoVMStreams.enumerateDevices(runtime); + device = devices.get(config.options.device); + + if (config.options.isVerboseBenchmark()) { + System.out.println("Using TornadoVM device:"); + System.out.println(" - Name : " + device.getDescription()); + System.out.println(" - Id : " + device.getDeviceName()); + System.out.println(" - Platform : " + device.getPlatformName()); + System.out.println(" - Backend : " + device.getTornadoVMBackend().name()); + } + } catch (Throwable e) { + throw new RuntimeException( + "Unable to initialise TornadoVM, make sure you are running the binary with the `tornado -jar ...` wrapper and not `java -jar ...`", + e); + } + } + + protected static TaskSchedule mkSchedule() { + return new TaskSchedule(""); + } + + @Override + public List listDevices() { + return TornadoVMStreams.enumerateDevices(TornadoRuntime.getTornadoRuntime()).stream() + .map(d -> d.getDescription() + "(" + d.getDeviceName() + ")") + .collect(Collectors.toList()); + } + + @Override + public void initArrays() { + this.copyTask.warmup(); + this.mulTask.warmup(); + this.addTask.warmup(); + this.triadTask.warmup(); + this.nstreamTask.warmup(); + this.dotTask.warmup(); + } + + @Override + public void copy() { + this.copyTask.execute(); + } + + @Override + public void mul() { + this.mulTask.execute(); + } + + @Override + public void add() { + this.addTask.execute(); + } + + @Override + public void triad() { + this.triadTask.execute(); + } + + @Override + public void nstream() { + this.nstreamTask.execute(); + } + + protected abstract T getSum(); + + @Override + public T dot() { + this.dotTask.execute(); + return getSum(); + } +} diff --git a/java-stream/src/main/java/javastream/tornadovm/SpecialisedDouble.java b/java-stream/src/main/java/javastream/tornadovm/SpecialisedDouble.java new file mode 100644 index 0000000..065f00f --- /dev/null +++ b/java-stream/src/main/java/javastream/tornadovm/SpecialisedDouble.java @@ -0,0 +1,89 @@ +package javastream.tornadovm; + +import java.util.Arrays; + +import javastream.Main.Config; +import uk.ac.manchester.tornado.api.annotations.Parallel; +import uk.ac.manchester.tornado.api.annotations.Reduce; + +final class SpecialisedDouble extends GenericTornadoVMStream { + + @SuppressWarnings("ManualArrayCopy") + private static void copy(int size, double[] a, double[] c) { + for (@Parallel int i = 0; i < size; i++) { + c[i] = a[i]; + } + } + + private static void mul(int size, double[] b, double[] c, double scalar) { + for (@Parallel int i = 0; i < size; i++) { + b[i] = scalar * c[i]; + } + } + + private static void add(int size, double[] a, double[] b, double[] c) { + for (@Parallel int i = 0; i < size; i++) { + c[i] = a[i] + b[i]; + } + } + + private static void triad(int size, double[] a, double[] b, double[] c, double scalar) { + for (@Parallel int i = 0; i < size; i++) { + a[i] = b[i] + scalar * c[i]; + } + } + + private static void nstream(int size, double[] a, double[] b, double[] c, double scalar) { + for (@Parallel int i = 0; i < size; i++) { + a[i] = b[i] * scalar * c[i]; + } + } + + private static void dot_( + double[] a, double[] b, @Reduce double[] acc) { // prevent name clash with CL's dot + acc[0] = 0; + for (@Parallel int i = 0; i < a.length; i++) { + acc[0] += a[i] * b[i]; + } + } + + private final double[] a, b, c; + private final double[] dotSum; + + @SuppressWarnings({"PrimitiveArrayArgumentToVarargsMethod", "DuplicatedCode"}) + SpecialisedDouble(Config config) { + super(config); + final int size = config.options.arraysize; + final double scalar = config.scalar; + a = new double[size]; + b = new double[size]; + c = new double[size]; + dotSum = new double[1]; + this.copyTask = mkSchedule().task("", SpecialisedDouble::copy, size, a, c); + this.mulTask = mkSchedule().task("", SpecialisedDouble::mul, size, b, c, scalar); + this.addTask = mkSchedule().task("", SpecialisedDouble::add, size, a, b, c); + this.triadTask = mkSchedule().task("", SpecialisedDouble::triad, size, a, b, c, scalar); + this.nstreamTask = mkSchedule().task("", SpecialisedDouble::nstream, size, a, b, c, scalar); + this.dotTask = mkSchedule().task("", SpecialisedDouble::dot_, a, b, dotSum).streamOut(dotSum); + } + + @Override + public void initArrays() { + super.initArrays(); + Arrays.fill(a, config.initA); + Arrays.fill(b, config.initB); + Arrays.fill(c, config.initC); + TornadoVMStreams.xferToDevice(device, a, b, c); + } + + @Override + protected Double getSum() { + return dotSum[0]; + } + + @Override + public Data data() { + TornadoVMStreams.xferFromDevice(device, a, b, c); + return new Data<>(boxed(a), boxed(b), boxed(c)); + } +} diff --git a/java-stream/src/main/java/javastream/tornadovm/SpecialisedFloat.java b/java-stream/src/main/java/javastream/tornadovm/SpecialisedFloat.java new file mode 100644 index 0000000..e61cfe9 --- /dev/null +++ b/java-stream/src/main/java/javastream/tornadovm/SpecialisedFloat.java @@ -0,0 +1,88 @@ +package javastream.tornadovm; + +import java.util.Arrays; +import javastream.Main.Config; +import uk.ac.manchester.tornado.api.annotations.Parallel; +import uk.ac.manchester.tornado.api.annotations.Reduce; + +final class SpecialisedFloat extends GenericTornadoVMStream { + + @SuppressWarnings("ManualArrayCopy") + private static void copy(int size, float[] a, float[] c) { + for (@Parallel int i = 0; i < size; i++) { + c[i] = a[i]; + } + } + + private static void mul(int size, float[] b, float[] c, float scalar) { + for (@Parallel int i = 0; i < size; i++) { + b[i] = scalar * c[i]; + } + } + + private static void add(int size, float[] a, float[] b, float[] c) { + for (@Parallel int i = 0; i < size; i++) { + c[i] = a[i] + b[i]; + } + } + + private static void triad(int size, float[] a, float[] b, float[] c, float scalar) { + for (@Parallel int i = 0; i < size; i++) { + a[i] = b[i] + scalar * c[i]; + } + } + + private static void nstream(int size, float[] a, float[] b, float[] c, float scalar) { + for (@Parallel int i = 0; i < size; i++) { + a[i] = b[i] * scalar * c[i]; + } + } + + private static void dot_( + float[] a, float[] b, @Reduce float[] acc) { // prevent name clash with CL's dot + acc[0] = 0; + for (@Parallel int i = 0; i < a.length; i++) { + acc[0] += a[i] * b[i]; + } + } + + private final float[] a, b, c; + private final float[] dotSum; + + @SuppressWarnings({"PrimitiveArrayArgumentToVarargsMethod", "DuplicatedCode"}) + SpecialisedFloat(Config config) { + super(config); + final int size = config.options.arraysize; + final float scalar = config.scalar; + a = new float[size]; + b = new float[size]; + c = new float[size]; + dotSum = new float[1]; + this.copyTask = mkSchedule().task("", SpecialisedFloat::copy, size, a, c); + this.mulTask = mkSchedule().task("", SpecialisedFloat::mul, size, b, c, scalar); + this.addTask = mkSchedule().task("", SpecialisedFloat::add, size, a, b, c); + this.triadTask = mkSchedule().task("", SpecialisedFloat::triad, size, a, b, c, scalar); + this.nstreamTask = mkSchedule().task("", SpecialisedFloat::nstream, size, a, b, c, scalar); + this.dotTask = mkSchedule().task("", SpecialisedFloat::dot_, a, b, dotSum).streamOut(dotSum); + } + + @Override + public void initArrays() { + super.initArrays(); + Arrays.fill(a, config.initA); + Arrays.fill(b, config.initB); + Arrays.fill(c, config.initC); + TornadoVMStreams.xferToDevice(device, a, b, c); + } + + @Override + protected Float getSum() { + return dotSum[0]; + } + + @Override + public Data data() { + TornadoVMStreams.xferFromDevice(device, a, b, c); + return new Data<>(boxed(a), boxed(b), boxed(c)); + } +} diff --git a/java-stream/src/main/java/javastream/tornadovm/TornadoVMStreams.java b/java-stream/src/main/java/javastream/tornadovm/TornadoVMStreams.java new file mode 100644 index 0000000..68eecad --- /dev/null +++ b/java-stream/src/main/java/javastream/tornadovm/TornadoVMStreams.java @@ -0,0 +1,42 @@ +package javastream.tornadovm; + +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import javastream.JavaStream; +import javastream.Main.Config; +import uk.ac.manchester.tornado.api.TornadoRuntimeCI; +import uk.ac.manchester.tornado.api.common.TornadoDevice; +import uk.ac.manchester.tornado.api.mm.TornadoGlobalObjectState; +import uk.ac.manchester.tornado.api.runtime.TornadoRuntime; + +public final class TornadoVMStreams { + + private TornadoVMStreams() {} + + static void xferToDevice(TornadoDevice device, Object... xs) { + for (Object x : xs) { + TornadoGlobalObjectState state = TornadoRuntime.getTornadoRuntime().resolveObject(x); + List writeEvent = device.ensurePresent(x, state.getDeviceState(device), null, 0, 0); + if (writeEvent != null) writeEvent.forEach(e -> device.resolveEvent(e).waitOn()); + } + } + + static void xferFromDevice(TornadoDevice device, Object... xs) { + for (Object x : xs) { + TornadoGlobalObjectState state = TornadoRuntime.getTornadoRuntime().resolveObject(x); + device.resolveEvent(device.streamOut(x, 0, state.getDeviceState(device), null)).waitOn(); + } + } + + static List enumerateDevices(TornadoRuntimeCI runtime) { + return IntStream.range(0, runtime.getNumDrivers()) + .mapToObj(runtime::getDriver) + .flatMap(d -> IntStream.range(0, d.getDeviceCount()).mapToObj(d::getDevice)) + .collect(Collectors.toList()); + } + + public static final Function, JavaStream> FLOAT = SpecialisedFloat::new; + public static final Function, JavaStream> DOUBLE = SpecialisedDouble::new; +} diff --git a/java-stream/src/test/java/javastream/SmokeTest.java b/java-stream/src/test/java/javastream/SmokeTest.java new file mode 100644 index 0000000..2ceca44 --- /dev/null +++ b/java-stream/src/test/java/javastream/SmokeTest.java @@ -0,0 +1,93 @@ +package javastream; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class SmokeTest { + + // taken from https://stackoverflow.com/a/32146095/896997 + private static Stream> ofCombinations( + List> collections, List current) { + return collections.isEmpty() + ? Stream.of(current) + : collections.get(0).stream() + .flatMap( + e -> { + List list = new ArrayList<>(current); + list.add(e); + return ofCombinations(collections.subList(1, collections.size()), list); + }); + } + + @SuppressWarnings("unused") + private static Stream options() { + + LinkedHashMap> impls = new LinkedHashMap<>(); + impls.put("jdk-stream", Arrays.asList(0, 1)); + impls.put("jdk-plain", Arrays.asList(0, 1)); + // skip aparapi as none of the jdk fallbacks work correctly + // skip tornadovm as it has no jdk fallback + + List configs = + impls.entrySet().stream() + .flatMap( + e -> + Stream.concat(Stream.of(""), e.getValue().stream().map(i -> "--device " + i)) + .map(d -> "--impl " + e.getKey() + " " + d)) + .collect(Collectors.toList()); + + return ofCombinations( + new ArrayList<>( + Arrays.asList( + configs, + Arrays.asList("", "--csv"), + // XXX floats usually have a 1.0^-5 error which misses 10^-8 + Arrays.asList("", "--float --dot-tolerance 1.0e-5"), + Arrays.asList("", "--triad-only", "--nstream-only"), + Arrays.asList("", "--mibibytes"))), + Collections.emptyList()) + .map( + xs -> + Arguments.of( + xs.stream() // + .map(String::trim) // + .collect(Collectors.joining(" ")) + .trim())); + } + + @ParameterizedTest + @MethodSource("options") + void testIt(String args) { + String line = "--arraysize 2048 " + args; + + // redirect stdout/stderr and only print if anything fails + ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + ByteArrayOutputStream errContent = new ByteArrayOutputStream(); + PrintStream originalOut = System.out; + PrintStream originalErr = System.err; + + System.setOut(new PrintStream(outContent)); + System.setErr(new PrintStream(errContent)); + int run = Main.run(line.split("\\s+")); + System.setOut(originalOut); + System.setErr(originalErr); + + if (run != 0) { + System.out.println(outContent); + System.err.println(errContent); + Assertions.assertEquals(0, run, "`" + line + "` did not return 0"); + } + } +} From 82084d407be1f7498e5758403bea52599ba2727f Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 1 Jul 2021 06:01:29 +0100 Subject: [PATCH 43/78] +x for mvnw executable --- java-stream/mvnw | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 java-stream/mvnw diff --git a/java-stream/mvnw b/java-stream/mvnw old mode 100644 new mode 100755 From 867a8a32ee54b53d94932fad5aaeed966a3a5e5b Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 1 Jul 2021 06:05:10 +0100 Subject: [PATCH 44/78] Use older fmt-maven-plugin for Java 8 compat. --- java-stream/pom.xml | 2 +- java-stream/src/main/java/javastream/Main.java | 14 ++++++-------- .../javastream/tornadovm/SpecialisedDouble.java | 1 - 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/java-stream/pom.xml b/java-stream/pom.xml index 6a1e4f0..ffaee72 100644 --- a/java-stream/pom.xml +++ b/java-stream/pom.xml @@ -117,7 +117,7 @@ com.coveo fmt-maven-plugin - 2.11 + 2.9.1 diff --git a/java-stream/src/main/java/javastream/Main.java b/java-stream/src/main/java/javastream/Main.java index 4c8ed6c..32b67a4 100644 --- a/java-stream/src/main/java/javastream/Main.java +++ b/java-stream/src/main/java/javastream/Main.java @@ -1,8 +1,13 @@ package javastream; +import static javastream.FractionalMaths.divide; +import static javastream.FractionalMaths.from; +import static javastream.FractionalMaths.minus; +import static javastream.FractionalMaths.plus; +import static javastream.FractionalMaths.times; + import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; - import java.time.Duration; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.Arrays; @@ -14,7 +19,6 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; - import javastream.JavaStream.Data; import javastream.JavaStream.Timings; import javastream.aparapi.AparapiStreams; @@ -22,12 +26,6 @@ import javastream.jdk.JdkStreams; import javastream.jdk.PlainStream; import javastream.tornadovm.TornadoVMStreams; -import static javastream.FractionalMaths.divide; -import static javastream.FractionalMaths.from; -import static javastream.FractionalMaths.minus; -import static javastream.FractionalMaths.plus; -import static javastream.FractionalMaths.times; - public class Main { enum Benchmark { diff --git a/java-stream/src/main/java/javastream/tornadovm/SpecialisedDouble.java b/java-stream/src/main/java/javastream/tornadovm/SpecialisedDouble.java index 065f00f..7712e31 100644 --- a/java-stream/src/main/java/javastream/tornadovm/SpecialisedDouble.java +++ b/java-stream/src/main/java/javastream/tornadovm/SpecialisedDouble.java @@ -1,7 +1,6 @@ package javastream.tornadovm; import java.util.Arrays; - import javastream.Main.Config; import uk.ac.manchester.tornado.api.annotations.Parallel; import uk.ac.manchester.tornado.api.annotations.Reduce; From a26699c5b50dbf307ce44325b6e05491f7ad5376 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Tue, 17 Aug 2021 14:28:47 +0100 Subject: [PATCH 45/78] Add oneAPI and KA implementation Isolate projects to avoid transitive dependency Add parameter for passing devices Incorporate further reviews Update all dependencies --- JuliaStream.jl/AMDGPU/Manifest.toml | 415 +++++++++++++ JuliaStream.jl/AMDGPU/Project.toml | 7 + JuliaStream.jl/CUDA/Manifest.toml | 316 ++++++++++ JuliaStream.jl/CUDA/Project.toml | 7 + .../KernelAbstractions/Manifest.toml | 547 ++++++++++++++++++ .../KernelAbstractions/Project.toml | 11 + JuliaStream.jl/Manifest.toml | 152 +++-- JuliaStream.jl/Project.toml | 5 + JuliaStream.jl/README.md | 43 +- JuliaStream.jl/Threaded/Manifest.toml | 31 + JuliaStream.jl/Threaded/Project.toml | 6 + JuliaStream.jl/oneAPI/Manifest.toml | 319 ++++++++++ JuliaStream.jl/oneAPI/Project.toml | 7 + JuliaStream.jl/src/AMDGPUStream.jl | 72 +-- JuliaStream.jl/src/CUDAStream.jl | 48 +- JuliaStream.jl/src/DistributedStream.jl | 44 +- .../src/KernelAbstractionsStream.jl | 255 ++++++++ JuliaStream.jl/src/Stream.jl | 58 +- JuliaStream.jl/src/ThreadedStream.jl | 38 +- JuliaStream.jl/src/oneAPIStream.jl | 170 ++++++ JuliaStream.jl/update_all.sh | 7 + 21 files changed, 2393 insertions(+), 165 deletions(-) create mode 100644 JuliaStream.jl/AMDGPU/Manifest.toml create mode 100644 JuliaStream.jl/AMDGPU/Project.toml create mode 100644 JuliaStream.jl/CUDA/Manifest.toml create mode 100644 JuliaStream.jl/CUDA/Project.toml create mode 100644 JuliaStream.jl/KernelAbstractions/Manifest.toml create mode 100644 JuliaStream.jl/KernelAbstractions/Project.toml create mode 100644 JuliaStream.jl/Threaded/Manifest.toml create mode 100644 JuliaStream.jl/Threaded/Project.toml create mode 100644 JuliaStream.jl/oneAPI/Manifest.toml create mode 100644 JuliaStream.jl/oneAPI/Project.toml create mode 100644 JuliaStream.jl/src/KernelAbstractionsStream.jl create mode 100644 JuliaStream.jl/src/oneAPIStream.jl create mode 100755 JuliaStream.jl/update_all.sh diff --git a/JuliaStream.jl/AMDGPU/Manifest.toml b/JuliaStream.jl/AMDGPU/Manifest.toml new file mode 100644 index 0000000..6e27f0a --- /dev/null +++ b/JuliaStream.jl/AMDGPU/Manifest.toml @@ -0,0 +1,415 @@ +# This file is machine-generated - editing it directly is not advised + +[[AMDGPU]] +deps = ["AbstractFFTs", "Adapt", "BinaryProvider", "CEnum", "GPUArrays", "GPUCompiler", "HIP_jll", "LLVM", "Libdl", "LinearAlgebra", "MacroTools", "Pkg", "Printf", "ROCmDeviceLibs_jll", "Random", "Requires", "Setfield", "hsa_rocr_jll"] +git-tree-sha1 = "d64c97447a753cfbf0158d6c7be513f34526d559" +uuid = "21141c5a-9bdb-4563-92ae-f87d6854732e" +version = "0.2.12" + +[[AbstractFFTs]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "485ee0867925449198280d4af84bdb46a2a404d0" +uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" +version = "1.0.1" + +[[Adapt]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "84918055d15b3114ede17ac6a7182f68870c16f7" +uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +version = "3.3.1" + +[[ArgParse]] +deps = ["Logging", "TextWrap"] +git-tree-sha1 = "3102bce13da501c9104df33549f511cd25264d7d" +uuid = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" +version = "1.1.4" + +[[ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" + +[[Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[BinaryProvider]] +deps = ["Libdl", "Logging", "SHA"] +git-tree-sha1 = "ecdec412a9abc8db54c0efc5548c64dfce072058" +uuid = "b99e7846-7c00-51b0-8f62-c81ae34c0232" +version = "0.5.10" + +[[Bzip2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2" +uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" +version = "1.0.8+0" + +[[CEnum]] +git-tree-sha1 = "215a9aa4a1f23fbd05b92769fdd62559488d70e9" +uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82" +version = "0.4.1" + +[[ConstructionBase]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "f74e9d5388b8620b4cee35d4c5a618dd4dc547f4" +uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" +version = "1.3.0" + +[[Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[Downloads]] +deps = ["ArgTools", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" + +[[Elfutils_jll]] +deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "XZ_jll", "Zlib_jll", "argp_standalone_jll", "fts_jll", "obstack_jll"] +git-tree-sha1 = "8f9fcde6d89b0a3ca51cb2028beab462705c5436" +uuid = "ab5a07f8-06af-567f-a878-e8bb879eba5a" +version = "0.182.0+0" + +[[ExprTools]] +git-tree-sha1 = "b7e3d17636b348f005f11040025ae8c6f645fe92" +uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" +version = "0.1.6" + +[[Future]] +deps = ["Random"] +uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" + +[[GPUArrays]] +deps = ["AbstractFFTs", "Adapt", "LinearAlgebra", "Printf", "Random", "Serialization", "Statistics"] +git-tree-sha1 = "ececbf05f8904c92814bdbd0aafd5540b0bf2e9a" +uuid = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7" +version = "7.0.1" + +[[GPUCompiler]] +deps = ["ExprTools", "InteractiveUtils", "LLVM", "Libdl", "Logging", "TimerOutputs", "UUIDs"] +git-tree-sha1 = "4ed2616d5e656c8716736b64da86755467f26cf5" +uuid = "61eb1bfa-7361-4325-ad38-22787b887f55" +version = "0.12.9" + +[[HIP_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "ROCmCompilerSupport_jll", "ROCmDeviceLibs_jll", "ROCmOpenCLRuntime_jll", "hsa_rocr_jll"] +git-tree-sha1 = "5097d8f7b6842156ab0928371b3d03fefd8decab" +uuid = "2696aab5-0948-5276-aa9a-2a86a37016b8" +version = "4.0.0+1" + +[[InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[JLLWrappers]] +deps = ["Preferences"] +git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.3.0" + +[[LLVM]] +deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Printf", "Unicode"] +git-tree-sha1 = "d6041ad706cf458b2c9f3e501152488a26451e9c" +uuid = "929cbde3-209d-540e-8aea-75f648917ca0" +version = "4.2.0" + +[[LLVMExtra_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "a9b1130c4728b0e462a1c28772954650039eb847" +uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab" +version = "0.0.7+0" + +[[LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" + +[[LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" + +[[LibGit2]] +deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" + +[[Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[Libgcrypt_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] +git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" +uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" +version = "1.8.7+0" + +[[Libglvnd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] +git-tree-sha1 = "7739f837d6447403596a75d19ed01fd08d6f56bf" +uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" +version = "1.3.0+3" + +[[Libgpg_error_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" +uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" +version = "1.42.0+0" + +[[Libiconv_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "42b62845d70a619f063a7da093d995ec8e15e778" +uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" +version = "1.16.1+1" + +[[LinearAlgebra]] +deps = ["Libdl"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[MacroTools]] +deps = ["Markdown", "Random"] +git-tree-sha1 = "0fb723cd8c45858c22169b2e42269e53271a6df7" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.5.7" + +[[Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" + +[[MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" + +[[NUMA_jll]] +deps = ["Libdl", "Pkg"] +git-tree-sha1 = "778f9bd14400cff2c32ed357e12766ac0e3d766e" +uuid = "7f51dc2b-bb24-59f8-b771-bb1490e4195d" +version = "2.0.13+1" + +[[NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" + +[[OrderedCollections]] +git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.4.1" + +[[Parameters]] +deps = ["OrderedCollections", "UnPack"] +git-tree-sha1 = "2276ac65f1e236e0a6ea70baff3f62ad4c625345" +uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" +version = "0.12.2" + +[[Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[Preferences]] +deps = ["TOML"] +git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.2.2" + +[[Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[ROCmCompilerSupport_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "ROCmDeviceLibs_jll", "hsa_rocr_jll"] +git-tree-sha1 = "56ddcfb5d8b60c9f8c1bc619886f8d363fd1926d" +uuid = "8fbdd1d2-db62-5cd0-981e-905da1486e17" +version = "4.0.0+1" + +[[ROCmDeviceLibs_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "d764f0f28b5af89aa004871a6a38e5d061f77257" +uuid = "873c0968-716b-5aa7-bb8d-d1e2e2aeff2d" +version = "4.0.0+0" + +[[ROCmOpenCLRuntime_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "ROCmCompilerSupport_jll", "ROCmDeviceLibs_jll", "Xorg_libX11_jll", "Xorg_xorgproto_jll", "hsa_rocr_jll"] +git-tree-sha1 = "f9e3e2cb40a7990535efa7da9b9dd0e0b458a973" +uuid = "10ae2a08-2eea-53f8-8c20-eec175020e9f" +version = "4.0.0+1" + +[[Random]] +deps = ["Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.1.3" + +[[SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[Setfield]] +deps = ["ConstructionBase", "Future", "MacroTools", "Requires"] +git-tree-sha1 = "fca29e68c5062722b5b4435594c3d1ba557072a3" +uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" +version = "0.7.1" + +[[Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" + +[[Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" + +[[TextWrap]] +git-tree-sha1 = "9250ef9b01b66667380cf3275b3f7488d0e25faf" +uuid = "b718987f-49a8-5099-9789-dcd902bef87d" +version = "1.0.1" + +[[TimerOutputs]] +deps = ["ExprTools", "Printf"] +git-tree-sha1 = "209a8326c4f955e2442c07b56029e88bb48299c7" +uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" +version = "0.5.12" + +[[UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[UnPack]] +git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" +uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +version = "1.0.2" + +[[Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[XML2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "1acf5bdf07aa0907e0a37d3718bb88d4b687b74a" +uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" +version = "2.9.12+0" + +[[XSLT_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] +git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" +uuid = "aed1982a-8fda-507f-9586-7b0439959a61" +version = "1.1.34+0" + +[[XZ_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "a921669cd9a45c23031fd4eb904f5cc3d20de415" +uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" +version = "5.2.5+2" + +[[Xorg_libX11_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] +git-tree-sha1 = "5be649d550f3f4b95308bf0183b82e2582876527" +uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" +version = "1.6.9+4" + +[[Xorg_libXau_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4e490d5c960c314f33885790ed410ff3a94ce67e" +uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" +version = "1.0.9+4" + +[[Xorg_libXdmcp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4fe47bd2247248125c428978740e18a681372dd4" +uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" +version = "1.1.3+4" + +[[Xorg_libXext_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3" +uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" +version = "1.3.4+4" + +[[Xorg_libpthread_stubs_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "6783737e45d3c59a4a4c4091f5f88cdcf0908cbb" +uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" +version = "0.1.0+3" + +[[Xorg_libxcb_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] +git-tree-sha1 = "daf17f441228e7a3833846cd048892861cff16d6" +uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" +version = "1.13.0+3" + +[[Xorg_xorgproto_jll]] +deps = ["Libdl", "Pkg"] +git-tree-sha1 = "9a9eb8ce756fe0bca01b4be16da770e18d264972" +uuid = "c4d99508-4286-5418-9131-c86396af500b" +version = "2019.2.0+2" + +[[Xorg_xtrans_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "79c31e7844f6ecf779705fbc12146eb190b7d845" +uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" +version = "1.4.0+3" + +[[Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" + +[[argp_standalone_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "feaf9f6293003c2bf53056fd6930d677ed340b34" +uuid = "c53206cc-00f7-50bf-ad1e-3ae1f6e49bc3" +version = "1.3.1+0" + +[[fts_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "78732b942383d2cb521df8a1a0814911144e663d" +uuid = "d65627f6-89bd-53e8-8ab5-8b75ff535eee" +version = "1.2.7+1" + +[[hsa_rocr_jll]] +deps = ["Artifacts", "Elfutils_jll", "JLLWrappers", "Libdl", "NUMA_jll", "Pkg", "Zlib_jll", "hsakmt_roct_jll"] +git-tree-sha1 = "df8d73efec8b1e53ad527d208f5343c0368f0fcd" +uuid = "dd59ff1a-a01a-568d-8b29-0669330f116a" +version = "4.0.0+0" + +[[hsakmt_roct_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "NUMA_jll", "Pkg"] +git-tree-sha1 = "80e0c9940e15cfd6f1f1e9d9f3953ec4d48d3d4a" +uuid = "1cecccd7-a9b6-5045-9cdc-a44c19b16d76" +version = "4.0.0+0" + +[[nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" + +[[obstack_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "1c4a6b66e934fc6db4649cb2910c72f53bbfea7e" +uuid = "c88a4935-d25e-5644-aacc-5db6f1b8ef79" +version = "1.2.2+0" + +[[p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" diff --git a/JuliaStream.jl/AMDGPU/Project.toml b/JuliaStream.jl/AMDGPU/Project.toml new file mode 100644 index 0000000..5ab8447 --- /dev/null +++ b/JuliaStream.jl/AMDGPU/Project.toml @@ -0,0 +1,7 @@ +[deps] +AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" +ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" +Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a" + +[compat] +julia = "1.6" diff --git a/JuliaStream.jl/CUDA/Manifest.toml b/JuliaStream.jl/CUDA/Manifest.toml new file mode 100644 index 0000000..7330228 --- /dev/null +++ b/JuliaStream.jl/CUDA/Manifest.toml @@ -0,0 +1,316 @@ +# This file is machine-generated - editing it directly is not advised + +[[AbstractFFTs]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "485ee0867925449198280d4af84bdb46a2a404d0" +uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" +version = "1.0.1" + +[[Adapt]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "84918055d15b3114ede17ac6a7182f68870c16f7" +uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +version = "3.3.1" + +[[ArgParse]] +deps = ["Logging", "TextWrap"] +git-tree-sha1 = "3102bce13da501c9104df33549f511cd25264d7d" +uuid = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" +version = "1.1.4" + +[[ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" + +[[Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[BFloat16s]] +deps = ["LinearAlgebra", "Test"] +git-tree-sha1 = "4af69e205efc343068dc8722b8dfec1ade89254a" +uuid = "ab4f0b2a-ad5b-11e8-123f-65d77653426b" +version = "0.1.0" + +[[Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[CEnum]] +git-tree-sha1 = "215a9aa4a1f23fbd05b92769fdd62559488d70e9" +uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82" +version = "0.4.1" + +[[CUDA]] +deps = ["AbstractFFTs", "Adapt", "BFloat16s", "CEnum", "CompilerSupportLibraries_jll", "ExprTools", "GPUArrays", "GPUCompiler", "LLVM", "LazyArtifacts", "Libdl", "LinearAlgebra", "Logging", "Printf", "Random", "Random123", "RandomNumbers", "Reexport", "Requires", "SparseArrays", "SpecialFunctions", "TimerOutputs"] +git-tree-sha1 = "9303b20dfa74e4bcb4da425d351d551fbb5850be" +uuid = "052768ef-5323-5732-b1bb-66c8b64840ba" +version = "3.4.0" + +[[ChainRulesCore]] +deps = ["Compat", "LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "bdc0937269321858ab2a4f288486cb258b9a0af7" +uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +version = "1.3.0" + +[[Compat]] +deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] +git-tree-sha1 = "79b9563ef3f2cc5fc6d3046a5ee1a57c9de52495" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "3.33.0" + +[[CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" + +[[Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[DelimitedFiles]] +deps = ["Mmap"] +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" + +[[Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[DocStringExtensions]] +deps = ["LibGit2"] +git-tree-sha1 = "a32185f5428d3986f47c2ab78b1f216d5e6cc96f" +uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +version = "0.8.5" + +[[Downloads]] +deps = ["ArgTools", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" + +[[ExprTools]] +git-tree-sha1 = "b7e3d17636b348f005f11040025ae8c6f645fe92" +uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" +version = "0.1.6" + +[[GPUArrays]] +deps = ["Adapt", "LinearAlgebra", "Printf", "Random", "Serialization", "Statistics"] +git-tree-sha1 = "8fac1cf7d6ce0f2249c7acaf25d22e1e85c4a07f" +uuid = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7" +version = "8.0.2" + +[[GPUCompiler]] +deps = ["ExprTools", "InteractiveUtils", "LLVM", "Libdl", "Logging", "TimerOutputs", "UUIDs"] +git-tree-sha1 = "4ed2616d5e656c8716736b64da86755467f26cf5" +uuid = "61eb1bfa-7361-4325-ad38-22787b887f55" +version = "0.12.9" + +[[InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[IrrationalConstants]] +git-tree-sha1 = "f76424439413893a832026ca355fe273e93bce94" +uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" +version = "0.1.0" + +[[JLLWrappers]] +deps = ["Preferences"] +git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.3.0" + +[[LLVM]] +deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Printf", "Unicode"] +git-tree-sha1 = "d6041ad706cf458b2c9f3e501152488a26451e9c" +uuid = "929cbde3-209d-540e-8aea-75f648917ca0" +version = "4.2.0" + +[[LLVMExtra_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "a9b1130c4728b0e462a1c28772954650039eb847" +uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab" +version = "0.0.7+0" + +[[LazyArtifacts]] +deps = ["Artifacts", "Pkg"] +uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" + +[[LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" + +[[LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" + +[[LibGit2]] +deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" + +[[Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[LinearAlgebra]] +deps = ["Libdl"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[LogExpFunctions]] +deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "3d682c07e6dd250ed082f883dc88aee7996bf2cc" +uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" +version = "0.3.0" + +[[Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" + +[[Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" + +[[NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" + +[[OpenSpecFun_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" +uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" +version = "0.5.5+0" + +[[OrderedCollections]] +git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.4.1" + +[[Parameters]] +deps = ["OrderedCollections", "UnPack"] +git-tree-sha1 = "2276ac65f1e236e0a6ea70baff3f62ad4c625345" +uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" +version = "0.12.2" + +[[Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[Preferences]] +deps = ["TOML"] +git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.2.2" + +[[Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[Random]] +deps = ["Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[Random123]] +deps = ["Libdl", "Random", "RandomNumbers"] +git-tree-sha1 = "0e8b146557ad1c6deb1367655e052276690e71a3" +uuid = "74087812-796a-5b5d-8853-05524746bad3" +version = "1.4.2" + +[[RandomNumbers]] +deps = ["Random", "Requires"] +git-tree-sha1 = "043da614cc7e95c703498a491e2c21f58a2b8111" +uuid = "e6cf234a-135c-5ec9-84dd-332b85af5143" +version = "1.5.3" + +[[Reexport]] +git-tree-sha1 = "5f6c21241f0f655da3952fd60aa18477cf96c220" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "1.1.0" + +[[Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.1.3" + +[[SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[SharedArrays]] +deps = ["Distributed", "Mmap", "Random", "Serialization"] +uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + +[[Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[SpecialFunctions]] +deps = ["ChainRulesCore", "LogExpFunctions", "OpenSpecFun_jll"] +git-tree-sha1 = "a322a9493e49c5f3a10b50df3aedaf1cdb3244b7" +uuid = "276daf66-3868-5448-9aa4-cd146d93841b" +version = "1.6.1" + +[[Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" + +[[Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" + +[[Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[TextWrap]] +git-tree-sha1 = "9250ef9b01b66667380cf3275b3f7488d0e25faf" +uuid = "b718987f-49a8-5099-9789-dcd902bef87d" +version = "1.0.1" + +[[TimerOutputs]] +deps = ["ExprTools", "Printf"] +git-tree-sha1 = "209a8326c4f955e2442c07b56029e88bb48299c7" +uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" +version = "0.5.12" + +[[UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[UnPack]] +git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" +uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +version = "1.0.2" + +[[Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" + +[[nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" + +[[p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" diff --git a/JuliaStream.jl/CUDA/Project.toml b/JuliaStream.jl/CUDA/Project.toml new file mode 100644 index 0000000..e50582e --- /dev/null +++ b/JuliaStream.jl/CUDA/Project.toml @@ -0,0 +1,7 @@ +[deps] +ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" +CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" +Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a" + +[compat] +julia = "1.6" diff --git a/JuliaStream.jl/KernelAbstractions/Manifest.toml b/JuliaStream.jl/KernelAbstractions/Manifest.toml new file mode 100644 index 0000000..5c24cf5 --- /dev/null +++ b/JuliaStream.jl/KernelAbstractions/Manifest.toml @@ -0,0 +1,547 @@ +# This file is machine-generated - editing it directly is not advised + +[[AMDGPU]] +deps = ["AbstractFFTs", "Adapt", "BinaryProvider", "CEnum", "GPUArrays", "GPUCompiler", "HIP_jll", "LLVM", "Libdl", "LinearAlgebra", "MacroTools", "Pkg", "Printf", "ROCmDeviceLibs_jll", "Random", "Requires", "Setfield", "hsa_rocr_jll"] +git-tree-sha1 = "d64c97447a753cfbf0158d6c7be513f34526d559" +uuid = "21141c5a-9bdb-4563-92ae-f87d6854732e" +version = "0.2.12" + +[[AbstractFFTs]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "485ee0867925449198280d4af84bdb46a2a404d0" +uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" +version = "1.0.1" + +[[Adapt]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "84918055d15b3114ede17ac6a7182f68870c16f7" +uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +version = "3.3.1" + +[[ArgParse]] +deps = ["Logging", "TextWrap"] +git-tree-sha1 = "3102bce13da501c9104df33549f511cd25264d7d" +uuid = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" +version = "1.1.4" + +[[ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" + +[[Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[BFloat16s]] +deps = ["LinearAlgebra", "Test"] +git-tree-sha1 = "4af69e205efc343068dc8722b8dfec1ade89254a" +uuid = "ab4f0b2a-ad5b-11e8-123f-65d77653426b" +version = "0.1.0" + +[[Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[BinaryProvider]] +deps = ["Libdl", "Logging", "SHA"] +git-tree-sha1 = "ecdec412a9abc8db54c0efc5548c64dfce072058" +uuid = "b99e7846-7c00-51b0-8f62-c81ae34c0232" +version = "0.5.10" + +[[Bzip2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2" +uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" +version = "1.0.8+0" + +[[CEnum]] +git-tree-sha1 = "215a9aa4a1f23fbd05b92769fdd62559488d70e9" +uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82" +version = "0.4.1" + +[[CUDA]] +deps = ["AbstractFFTs", "Adapt", "BFloat16s", "CEnum", "CompilerSupportLibraries_jll", "DataStructures", "ExprTools", "GPUArrays", "GPUCompiler", "LLVM", "LazyArtifacts", "Libdl", "LinearAlgebra", "Logging", "Printf", "Random", "Random123", "RandomNumbers", "Reexport", "Requires", "SparseArrays", "SpecialFunctions", "TimerOutputs"] +git-tree-sha1 = "5e696e37e51b01ae07bd9f700afe6cbd55250bce" +uuid = "052768ef-5323-5732-b1bb-66c8b64840ba" +version = "3.3.4" + +[[CUDAKernels]] +deps = ["Adapt", "CUDA", "Cassette", "KernelAbstractions", "SpecialFunctions", "StaticArrays"] +git-tree-sha1 = "81f76297b63c67723b1d60f5e7e002ae3393974b" +uuid = "72cfdca4-0801-4ab0-bf6a-d52aa10adc57" +version = "0.3.0" + +[[Cassette]] +git-tree-sha1 = "087e76b8d48c014112ba890892c33be42ad10504" +uuid = "7057c7e9-c182-5462-911a-8362d720325c" +version = "0.3.7" + +[[ChainRulesCore]] +deps = ["Compat", "LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "bdc0937269321858ab2a4f288486cb258b9a0af7" +uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +version = "1.3.0" + +[[Compat]] +deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] +git-tree-sha1 = "79b9563ef3f2cc5fc6d3046a5ee1a57c9de52495" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "3.33.0" + +[[CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" + +[[ConstructionBase]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "f74e9d5388b8620b4cee35d4c5a618dd4dc547f4" +uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" +version = "1.3.0" + +[[DataStructures]] +deps = ["Compat", "InteractiveUtils", "OrderedCollections"] +git-tree-sha1 = "7d9d316f04214f7efdbb6398d545446e246eff02" +uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +version = "0.18.10" + +[[Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[DelimitedFiles]] +deps = ["Mmap"] +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" + +[[Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[DocStringExtensions]] +deps = ["LibGit2"] +git-tree-sha1 = "a32185f5428d3986f47c2ab78b1f216d5e6cc96f" +uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +version = "0.8.5" + +[[Downloads]] +deps = ["ArgTools", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" + +[[Elfutils_jll]] +deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "XZ_jll", "Zlib_jll", "argp_standalone_jll", "fts_jll", "obstack_jll"] +git-tree-sha1 = "8f9fcde6d89b0a3ca51cb2028beab462705c5436" +uuid = "ab5a07f8-06af-567f-a878-e8bb879eba5a" +version = "0.182.0+0" + +[[ExprTools]] +git-tree-sha1 = "b7e3d17636b348f005f11040025ae8c6f645fe92" +uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" +version = "0.1.6" + +[[Future]] +deps = ["Random"] +uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" + +[[GPUArrays]] +deps = ["AbstractFFTs", "Adapt", "LinearAlgebra", "Printf", "Random", "Serialization", "Statistics"] +git-tree-sha1 = "ececbf05f8904c92814bdbd0aafd5540b0bf2e9a" +uuid = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7" +version = "7.0.1" + +[[GPUCompiler]] +deps = ["ExprTools", "InteractiveUtils", "LLVM", "Libdl", "Logging", "TimerOutputs", "UUIDs"] +git-tree-sha1 = "4ed2616d5e656c8716736b64da86755467f26cf5" +uuid = "61eb1bfa-7361-4325-ad38-22787b887f55" +version = "0.12.9" + +[[HIP_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "ROCmCompilerSupport_jll", "ROCmDeviceLibs_jll", "ROCmOpenCLRuntime_jll", "hsa_rocr_jll"] +git-tree-sha1 = "5097d8f7b6842156ab0928371b3d03fefd8decab" +uuid = "2696aab5-0948-5276-aa9a-2a86a37016b8" +version = "4.0.0+1" + +[[InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[IrrationalConstants]] +git-tree-sha1 = "f76424439413893a832026ca355fe273e93bce94" +uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" +version = "0.1.0" + +[[JLLWrappers]] +deps = ["Preferences"] +git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.3.0" + +[[KernelAbstractions]] +deps = ["Adapt", "Cassette", "InteractiveUtils", "MacroTools", "SpecialFunctions", "StaticArrays", "UUIDs"] +git-tree-sha1 = "5e6c70389c1b1e40adb81664ca8cea6ce8127afc" +uuid = "63c18a36-062a-441e-b654-da1e3ab1ce7c" +version = "0.7.0" + +[[LLVM]] +deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Printf", "Unicode"] +git-tree-sha1 = "d6041ad706cf458b2c9f3e501152488a26451e9c" +uuid = "929cbde3-209d-540e-8aea-75f648917ca0" +version = "4.2.0" + +[[LLVMExtra_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "a9b1130c4728b0e462a1c28772954650039eb847" +uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab" +version = "0.0.7+0" + +[[LazyArtifacts]] +deps = ["Artifacts", "Pkg"] +uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" + +[[LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" + +[[LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" + +[[LibGit2]] +deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" + +[[Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[Libgcrypt_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] +git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" +uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" +version = "1.8.7+0" + +[[Libglvnd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] +git-tree-sha1 = "7739f837d6447403596a75d19ed01fd08d6f56bf" +uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" +version = "1.3.0+3" + +[[Libgpg_error_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" +uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" +version = "1.42.0+0" + +[[Libiconv_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "42b62845d70a619f063a7da093d995ec8e15e778" +uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" +version = "1.16.1+1" + +[[LinearAlgebra]] +deps = ["Libdl"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[LogExpFunctions]] +deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "3d682c07e6dd250ed082f883dc88aee7996bf2cc" +uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" +version = "0.3.0" + +[[Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[MacroTools]] +deps = ["Markdown", "Random"] +git-tree-sha1 = "0fb723cd8c45858c22169b2e42269e53271a6df7" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.5.7" + +[[Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" + +[[Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" + +[[NUMA_jll]] +deps = ["Libdl", "Pkg"] +git-tree-sha1 = "778f9bd14400cff2c32ed357e12766ac0e3d766e" +uuid = "7f51dc2b-bb24-59f8-b771-bb1490e4195d" +version = "2.0.13+1" + +[[NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" + +[[OpenSpecFun_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" +uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" +version = "0.5.5+0" + +[[OrderedCollections]] +git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.4.1" + +[[Parameters]] +deps = ["OrderedCollections", "UnPack"] +git-tree-sha1 = "2276ac65f1e236e0a6ea70baff3f62ad4c625345" +uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" +version = "0.12.2" + +[[Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[Preferences]] +deps = ["TOML"] +git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.2.2" + +[[Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[ROCKernels]] +deps = ["AMDGPU", "Adapt", "Cassette", "KernelAbstractions", "SpecialFunctions", "StaticArrays"] +git-tree-sha1 = "41105b861342637dde17797bdd9aaa537aca646b" +uuid = "7eb9e9f0-4bd3-4c4c-8bef-26bd9629d9b9" +version = "0.2.0" + +[[ROCmCompilerSupport_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "ROCmDeviceLibs_jll", "hsa_rocr_jll"] +git-tree-sha1 = "56ddcfb5d8b60c9f8c1bc619886f8d363fd1926d" +uuid = "8fbdd1d2-db62-5cd0-981e-905da1486e17" +version = "4.0.0+1" + +[[ROCmDeviceLibs_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "d764f0f28b5af89aa004871a6a38e5d061f77257" +uuid = "873c0968-716b-5aa7-bb8d-d1e2e2aeff2d" +version = "4.0.0+0" + +[[ROCmOpenCLRuntime_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "ROCmCompilerSupport_jll", "ROCmDeviceLibs_jll", "Xorg_libX11_jll", "Xorg_xorgproto_jll", "hsa_rocr_jll"] +git-tree-sha1 = "f9e3e2cb40a7990535efa7da9b9dd0e0b458a973" +uuid = "10ae2a08-2eea-53f8-8c20-eec175020e9f" +version = "4.0.0+1" + +[[Random]] +deps = ["Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[Random123]] +deps = ["Libdl", "Random", "RandomNumbers"] +git-tree-sha1 = "0e8b146557ad1c6deb1367655e052276690e71a3" +uuid = "74087812-796a-5b5d-8853-05524746bad3" +version = "1.4.2" + +[[RandomNumbers]] +deps = ["Random", "Requires"] +git-tree-sha1 = "043da614cc7e95c703498a491e2c21f58a2b8111" +uuid = "e6cf234a-135c-5ec9-84dd-332b85af5143" +version = "1.5.3" + +[[Reexport]] +git-tree-sha1 = "5f6c21241f0f655da3952fd60aa18477cf96c220" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "1.1.0" + +[[Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.1.3" + +[[SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[Setfield]] +deps = ["ConstructionBase", "Future", "MacroTools", "Requires"] +git-tree-sha1 = "fca29e68c5062722b5b4435594c3d1ba557072a3" +uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" +version = "0.7.1" + +[[SharedArrays]] +deps = ["Distributed", "Mmap", "Random", "Serialization"] +uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + +[[Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[SpecialFunctions]] +deps = ["ChainRulesCore", "LogExpFunctions", "OpenSpecFun_jll"] +git-tree-sha1 = "a322a9493e49c5f3a10b50df3aedaf1cdb3244b7" +uuid = "276daf66-3868-5448-9aa4-cd146d93841b" +version = "1.6.1" + +[[StaticArrays]] +deps = ["LinearAlgebra", "Random", "Statistics"] +git-tree-sha1 = "3240808c6d463ac46f1c1cd7638375cd22abbccb" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "1.2.12" + +[[Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" + +[[Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" + +[[Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[TextWrap]] +git-tree-sha1 = "9250ef9b01b66667380cf3275b3f7488d0e25faf" +uuid = "b718987f-49a8-5099-9789-dcd902bef87d" +version = "1.0.1" + +[[TimerOutputs]] +deps = ["ExprTools", "Printf"] +git-tree-sha1 = "209a8326c4f955e2442c07b56029e88bb48299c7" +uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" +version = "0.5.12" + +[[UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[UnPack]] +git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" +uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +version = "1.0.2" + +[[Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[XML2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "1acf5bdf07aa0907e0a37d3718bb88d4b687b74a" +uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" +version = "2.9.12+0" + +[[XSLT_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] +git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" +uuid = "aed1982a-8fda-507f-9586-7b0439959a61" +version = "1.1.34+0" + +[[XZ_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "a921669cd9a45c23031fd4eb904f5cc3d20de415" +uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" +version = "5.2.5+2" + +[[Xorg_libX11_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] +git-tree-sha1 = "5be649d550f3f4b95308bf0183b82e2582876527" +uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" +version = "1.6.9+4" + +[[Xorg_libXau_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4e490d5c960c314f33885790ed410ff3a94ce67e" +uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" +version = "1.0.9+4" + +[[Xorg_libXdmcp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4fe47bd2247248125c428978740e18a681372dd4" +uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" +version = "1.1.3+4" + +[[Xorg_libXext_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3" +uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" +version = "1.3.4+4" + +[[Xorg_libpthread_stubs_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "6783737e45d3c59a4a4c4091f5f88cdcf0908cbb" +uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" +version = "0.1.0+3" + +[[Xorg_libxcb_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] +git-tree-sha1 = "daf17f441228e7a3833846cd048892861cff16d6" +uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" +version = "1.13.0+3" + +[[Xorg_xorgproto_jll]] +deps = ["Libdl", "Pkg"] +git-tree-sha1 = "9a9eb8ce756fe0bca01b4be16da770e18d264972" +uuid = "c4d99508-4286-5418-9131-c86396af500b" +version = "2019.2.0+2" + +[[Xorg_xtrans_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "79c31e7844f6ecf779705fbc12146eb190b7d845" +uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" +version = "1.4.0+3" + +[[Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" + +[[argp_standalone_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "feaf9f6293003c2bf53056fd6930d677ed340b34" +uuid = "c53206cc-00f7-50bf-ad1e-3ae1f6e49bc3" +version = "1.3.1+0" + +[[fts_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "78732b942383d2cb521df8a1a0814911144e663d" +uuid = "d65627f6-89bd-53e8-8ab5-8b75ff535eee" +version = "1.2.7+1" + +[[hsa_rocr_jll]] +deps = ["Artifacts", "Elfutils_jll", "JLLWrappers", "Libdl", "NUMA_jll", "Pkg", "Zlib_jll", "hsakmt_roct_jll"] +git-tree-sha1 = "df8d73efec8b1e53ad527d208f5343c0368f0fcd" +uuid = "dd59ff1a-a01a-568d-8b29-0669330f116a" +version = "4.0.0+0" + +[[hsakmt_roct_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "NUMA_jll", "Pkg"] +git-tree-sha1 = "80e0c9940e15cfd6f1f1e9d9f3953ec4d48d3d4a" +uuid = "1cecccd7-a9b6-5045-9cdc-a44c19b16d76" +version = "4.0.0+0" + +[[nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" + +[[obstack_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "1c4a6b66e934fc6db4649cb2910c72f53bbfea7e" +uuid = "c88a4935-d25e-5644-aacc-5db6f1b8ef79" +version = "1.2.2+0" + +[[p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" diff --git a/JuliaStream.jl/KernelAbstractions/Project.toml b/JuliaStream.jl/KernelAbstractions/Project.toml new file mode 100644 index 0000000..71715ff --- /dev/null +++ b/JuliaStream.jl/KernelAbstractions/Project.toml @@ -0,0 +1,11 @@ +[deps] +AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" +ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" +CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" +CUDAKernels = "72cfdca4-0801-4ab0-bf6a-d52aa10adc57" +KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" +Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a" +ROCKernels = "7eb9e9f0-4bd3-4c4c-8bef-26bd9629d9b9" + +[compat] +julia = "1.6" diff --git a/JuliaStream.jl/Manifest.toml b/JuliaStream.jl/Manifest.toml index c60d77f..14f2029 100644 --- a/JuliaStream.jl/Manifest.toml +++ b/JuliaStream.jl/Manifest.toml @@ -47,9 +47,9 @@ version = "0.5.10" [[Bzip2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c3598e525718abcc440f69cc6d5f60dda0a1b61e" +git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2" uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.6+5" +version = "1.0.8+0" [[CEnum]] git-tree-sha1 = "215a9aa4a1f23fbd05b92769fdd62559488d70e9" @@ -62,17 +62,28 @@ git-tree-sha1 = "364179416eabc34c9ca32126a6bdb431680c3bad" uuid = "052768ef-5323-5732-b1bb-66c8b64840ba" version = "3.2.1" +[[CUDAKernels]] +deps = ["Adapt", "CUDA", "Cassette", "KernelAbstractions", "SpecialFunctions", "StaticArrays"] +git-tree-sha1 = "81f76297b63c67723b1d60f5e7e002ae3393974b" +uuid = "72cfdca4-0801-4ab0-bf6a-d52aa10adc57" +version = "0.3.0" + +[[Cassette]] +git-tree-sha1 = "087e76b8d48c014112ba890892c33be42ad10504" +uuid = "7057c7e9-c182-5462-911a-8362d720325c" +version = "0.3.7" + [[ChainRulesCore]] deps = ["Compat", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "8b31cc69cbc38c5c826aaa1c890c694be3622d99" +git-tree-sha1 = "bdc0937269321858ab2a4f288486cb258b9a0af7" uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "0.10.3" +version = "1.3.0" [[Compat]] deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "e4e2b39db08f967cc1360951f01e8a75ec441cab" +git-tree-sha1 = "79b9563ef3f2cc5fc6d3046a5ee1a57c9de52495" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "3.30.0" +version = "3.33.0" [[CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] @@ -80,15 +91,15 @@ uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" [[ConstructionBase]] deps = ["LinearAlgebra"] -git-tree-sha1 = "1dc43957fb9a1574fa1b7a449e101bd1fd3a9fb7" +git-tree-sha1 = "f74e9d5388b8620b4cee35d4c5a618dd4dc547f4" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" -version = "1.2.1" +version = "1.3.0" [[DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "4437b64df1e0adccc3e5d1adbc3ac741095e4677" +git-tree-sha1 = "7d9d316f04214f7efdbb6398d545446e246eff02" uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.9" +version = "0.18.10" [[Dates]] deps = ["Printf"] @@ -114,14 +125,14 @@ uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" [[Elfutils_jll]] deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "XZ_jll", "Zlib_jll", "argp_standalone_jll", "fts_jll", "obstack_jll"] -git-tree-sha1 = "76cbf1134983cfb371ad77117bb2659600ed64d6" +git-tree-sha1 = "8f9fcde6d89b0a3ca51cb2028beab462705c5436" uuid = "ab5a07f8-06af-567f-a878-e8bb879eba5a" -version = "0.179.0+0" +version = "0.182.0+0" [[ExprTools]] -git-tree-sha1 = "10407a39b87f29d47ebaca8edbc75d7c302ff93e" +git-tree-sha1 = "b7e3d17636b348f005f11040025ae8c6f645fe92" uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" -version = "0.1.3" +version = "0.1.6" [[Future]] deps = ["Random"] @@ -143,17 +154,28 @@ version = "0.11.5" deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +[[IrrationalConstants]] +git-tree-sha1 = "f76424439413893a832026ca355fe273e93bce94" +uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" +version = "0.1.0" + [[JLLWrappers]] deps = ["Preferences"] git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" version = "1.3.0" +[[KernelAbstractions]] +deps = ["Adapt", "Cassette", "InteractiveUtils", "MacroTools", "SpecialFunctions", "StaticArrays", "UUIDs"] +git-tree-sha1 = "5e6c70389c1b1e40adb81664ca8cea6ce8127afc" +uuid = "63c18a36-062a-441e-b654-da1e3ab1ce7c" +version = "0.7.0" + [[LLVM]] deps = ["CEnum", "Libdl", "Printf", "Unicode"] -git-tree-sha1 = "b499c68a45249b0385585c62f4a9b62b5db8e691" +git-tree-sha1 = "f57ac3fd2045b50d3db081663837ac5b4096947e" uuid = "929cbde3-209d-540e-8aea-75f648917ca0" -version = "3.7.1" +version = "3.9.0" [[LazyArtifacts]] deps = ["Artifacts", "Pkg"] @@ -183,19 +205,19 @@ deps = ["Libdl"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" [[LogExpFunctions]] -deps = ["DocStringExtensions", "LinearAlgebra"] -git-tree-sha1 = "1ba664552f1ef15325e68dc4c05c3ef8c2d5d885" +deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "3d682c07e6dd250ed082f883dc88aee7996bf2cc" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.2.4" +version = "0.3.0" [[Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" [[MacroTools]] deps = ["Markdown", "Random"] -git-tree-sha1 = "6a8a2a625ab0dea913aba95c11370589e0239ff0" +git-tree-sha1 = "0fb723cd8c45858c22169b2e42269e53271a6df7" uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.6" +version = "0.5.7" [[Markdown]] deps = ["Base64"] @@ -217,6 +239,12 @@ uuid = "a63ad114-7e13-5084-954f-fe012c677804" [[MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +[[NEO_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "gmmlib_jll", "libigc_jll", "oneAPI_Level_Zero_Headers_jll"] +git-tree-sha1 = "c753dd029eb0837658bf8eaee041c19e4ce5bb8c" +uuid = "700fe977-ac61-5f37-bbc8-c6c4b2b6a9fd" +version = "21.12.19358+0" + [[NUMA_jll]] deps = ["Libdl", "Pkg"] git-tree-sha1 = "778f9bd14400cff2c32ed357e12766ac0e3d766e" @@ -261,21 +289,27 @@ uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" +[[ROCKernels]] +deps = ["AMDGPU", "Adapt", "Cassette", "KernelAbstractions", "SpecialFunctions", "StaticArrays"] +git-tree-sha1 = "41105b861342637dde17797bdd9aaa537aca646b" +uuid = "7eb9e9f0-4bd3-4c4c-8bef-26bd9629d9b9" +version = "0.2.0" + [[Random]] deps = ["Serialization"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [[Random123]] deps = ["Libdl", "Random", "RandomNumbers"] -git-tree-sha1 = "7c6710c8198fd4444b5eb6a3840b7d47bd3593c5" +git-tree-sha1 = "0e8b146557ad1c6deb1367655e052276690e71a3" uuid = "74087812-796a-5b5d-8853-05524746bad3" -version = "1.3.1" +version = "1.4.2" [[RandomNumbers]] deps = ["Random", "Requires"] -git-tree-sha1 = "441e6fc35597524ada7f85e13df1f4e10137d16f" +git-tree-sha1 = "043da614cc7e95c703498a491e2c21f58a2b8111" uuid = "e6cf234a-135c-5ec9-84dd-332b85af5143" -version = "1.4.0" +version = "1.5.3" [[Reexport]] git-tree-sha1 = "5f6c21241f0f655da3952fd60aa18477cf96c220" @@ -291,6 +325,18 @@ version = "1.1.3" [[SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +[[SPIRV_LLVM_Translator_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "8cca87d57f6ddf19373cc9791fddc741406c8fbf" +uuid = "4a5d46fc-d8cf-5151-a261-86b458210efb" +version = "11.0.0+2" + +[[SPIRV_Tools_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c0324b7e07bc4649f755bfe7e00f7c6ed6aa353f" +uuid = "6ac6d60f-d740-5983-97d7-a4482c0689f4" +version = "2021.2.0+0" + [[Scratch]] deps = ["Dates"] git-tree-sha1 = "0b4b7f1393cff97c33891da2a0bf69c6ed241fda" @@ -302,9 +348,9 @@ uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" [[Setfield]] deps = ["ConstructionBase", "Future", "MacroTools", "Requires"] -git-tree-sha1 = "d5640fc570fb1b6c54512f0bd3853866bd298b3e" +git-tree-sha1 = "fca29e68c5062722b5b4435594c3d1ba557072a3" uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" -version = "0.7.0" +version = "0.7.1" [[SharedArrays]] deps = ["Distributed", "Mmap", "Random", "Serialization"] @@ -319,9 +365,15 @@ uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [[SpecialFunctions]] deps = ["ChainRulesCore", "LogExpFunctions", "OpenSpecFun_jll"] -git-tree-sha1 = "a50550fa3164a8c46747e62063b4d774ac1bcf49" +git-tree-sha1 = "a322a9493e49c5f3a10b50df3aedaf1cdb3244b7" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "1.5.1" +version = "1.6.1" + +[[StaticArrays]] +deps = ["LinearAlgebra", "Random", "Statistics"] +git-tree-sha1 = "3240808c6d463ac46f1c1cd7638375cd22abbccb" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "1.2.12" [[Statistics]] deps = ["LinearAlgebra", "SparseArrays"] @@ -346,9 +398,9 @@ version = "1.0.1" [[TimerOutputs]] deps = ["ExprTools", "Printf"] -git-tree-sha1 = "bf8aacc899a1bd16522d0350e1e2310510d77236" +git-tree-sha1 = "209a8326c4f955e2442c07b56029e88bb48299c7" uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" -version = "0.5.9" +version = "0.5.12" [[UUIDs]] deps = ["Random", "SHA"] @@ -364,9 +416,9 @@ uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" [[XZ_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9f76853ea2ba894054e24640abfb73d73e5a4cb5" +git-tree-sha1 = "a921669cd9a45c23031fd4eb904f5cc3d20de415" uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" -version = "5.2.5+0" +version = "5.2.5+2" [[Zlib_jll]] deps = ["Libdl"] @@ -374,9 +426,9 @@ uuid = "83775a58-1f1d-513f-b197-d71354ab007a" [[argp_standalone_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c4fa3457046fc93249b63e8319e743b6c8590609" +git-tree-sha1 = "feaf9f6293003c2bf53056fd6930d677ed340b34" uuid = "c53206cc-00f7-50bf-ad1e-3ae1f6e49bc3" -version = "1.3.0+0" +version = "1.3.1+0" [[fts_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -384,6 +436,12 @@ git-tree-sha1 = "78732b942383d2cb521df8a1a0814911144e663d" uuid = "d65627f6-89bd-53e8-8ab5-8b75ff535eee" version = "1.2.7+1" +[[gmmlib_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4067ef455d4fa67febe26efc3f9565a9bb7ba911" +uuid = "09858cae-167c-5acb-9302-fddc6874d481" +version = "20.3.2+0" + [[hsa_rocr_jll]] deps = ["Artifacts", "Elfutils_jll", "JLLWrappers", "Libdl", "NUMA_jll", "Pkg", "Zlib_jll", "hsakmt_roct_jll"] git-tree-sha1 = "42189f176d6ae4f37c0c0e652fec339bb0bfab5d" @@ -396,6 +454,12 @@ git-tree-sha1 = "8a9ee6c091e952e4ea6585d15131d43f789ae041" uuid = "1cecccd7-a9b6-5045-9cdc-a44c19b16d76" version = "3.8.0+0" +[[libigc_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "6140dbf267f7ab57fb791b49f2114374218b5c20" +uuid = "94295238-5935-5bd7-bb0f-b00942e9bdd5" +version = "1.0.6712+0" + [[nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" @@ -406,6 +470,24 @@ git-tree-sha1 = "1c4a6b66e934fc6db4649cb2910c72f53bbfea7e" uuid = "c88a4935-d25e-5644-aacc-5db6f1b8ef79" version = "1.2.2+0" +[[oneAPI]] +deps = ["Adapt", "CEnum", "ExprTools", "GPUArrays", "GPUCompiler", "LLVM", "LinearAlgebra", "NEO_jll", "Printf", "Random", "SPIRV_LLVM_Translator_jll", "SPIRV_Tools_jll", "SpecialFunctions", "oneAPI_Level_Zero_Loader_jll"] +git-tree-sha1 = "b4a4b84c864e75fe885a1643525f0c97ce310dd9" +uuid = "8f75cd03-7ff8-4ecb-9b8f-daf728133b1b" +version = "0.1.3" + +[[oneAPI_Level_Zero_Headers_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "48982fbfd2f3d0a30d644563dcf96892d252b395" +uuid = "f4bc562b-d309-54f8-9efb-476e56f0410d" +version = "1.1.2+1" + +[[oneAPI_Level_Zero_Loader_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "oneAPI_Level_Zero_Headers_jll"] +git-tree-sha1 = "1fa53dfdd32a732f09c254c86403e1abab653fb2" +uuid = "13eca655-d68d-5b81-8367-6d99d727ab01" +version = "1.3.6+0" + [[p7zip_jll]] deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" diff --git a/JuliaStream.jl/Project.toml b/JuliaStream.jl/Project.toml index 0afa7d0..9c7d49d 100644 --- a/JuliaStream.jl/Project.toml +++ b/JuliaStream.jl/Project.toml @@ -7,8 +7,13 @@ version = "3.4.0" AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" +CUDAKernels = "72cfdca4-0801-4ab0-bf6a-d52aa10adc57" Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" +ExprTools = "e2ba6199-217a-4e67-a87a-7c52f15ade04" +KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a" +ROCKernels = "7eb9e9f0-4bd3-4c4c-8bef-26bd9629d9b9" +oneAPI = "8f75cd03-7ff8-4ecb-9b8f-daf728133b1b" [compat] julia = "1.6" diff --git a/JuliaStream.jl/README.md b/JuliaStream.jl/README.md index 851a59d..6204da7 100644 --- a/JuliaStream.jl/README.md +++ b/JuliaStream.jl/README.md @@ -8,6 +8,8 @@ This is an implementation of BabelStream in Julia which contains the following v * `DistributedStream.jl` - Process based parallelism with `@distributed` macros * `CUDAStream.jl` - Direct port of BabelStream's native CUDA implementation using [CUDA.jl](https://github.com/JuliaGPU/CUDA.jl) * `AMDGPUStream.jl` - Direct port of BabelStream's native HIP implementation using [AMDGPU.jl](https://github.com/JuliaGPU/AMDGPU.jl) + * `oneAPIStream.jl` - Direct port of BabelStream's native SYCL implementation using [oneAPI.jl](https://github.com/JuliaGPU/oneAPI.jl) + * `KernelAbstractions.jl` - Direct port of miniBUDE's native CUDA implementation using [KernelAbstractions.jl](https://github.com/JuliaGPU/KernelAbstractions.jl) ### Build & Run @@ -15,12 +17,35 @@ Prerequisites * Julia >= 1.6+ -With Julia on path, run the benchmark with: +A set of reduced dependency projects are available for the following backend and implementations: + + * `AMDGPU` supports: + - `AMDGPUStream.jl` + * `CUDA` supports: + - `CUDAStream.jl` + * `oneAPI` supports: + - `oneAPIStream.jl` + * `KernelAbstractions` supports: + - `KernelAbstractionsStream.jl` + * `Threaded` supports: + - `PlainStream.jl` + - `ThreadedStream.jl` + - `DistributedStream.jl` + +With Julia on path, run your selected benchmark with: ```shell > cd JuliaStream.jl -> julia --project -e 'import Pkg; Pkg.instantiate()' # only required on first run -> julia --project src/Stream.jl +> julia --project= -e 'import Pkg; Pkg.instantiate()' # only required on first run +> julia --project= src/Stream.jl +``` + +For example. to run the CUDA implementation: + +```shell +> cd JuliaStream.jl +> julia --project=CUDA -e 'import Pkg; Pkg.instantiate()' +> julia --project=CUDA src/CUDAStream.jl ``` **Important:** @@ -28,3 +53,15 @@ With Julia on path, run the benchmark with: * Thread count for `ThreadedStream` must be set via the `JULIA_NUM_THREADS` environment variable (e.g `export JULIA_NUM_THREADS=$(nproc)`) otherwise it defaults to 1. * Worker count for `DistributedStream` is set with `-p ` as per the [documentation](https://docs.julialang.org/en/v1/manual/distributed-computing). * Certain implementations such as CUDA and AMDGPU will do hardware detection at runtime and may download and/or compile further software packages for the platform. + +*** + +Alternatively, the top-level project `Project.toml` contains all dependencies needed to run all implementations in `src`. +There may be instances where some packages are locked to an older version because of transitive dependency requirements. + +To run the benchmark using the top-level project, run the benchmark with: +```shell +> cd JuliaStream.jl +> julia --project -e 'import Pkg; Pkg.instantiate()' +> julia --project src/Stream.jl +``` \ No newline at end of file diff --git a/JuliaStream.jl/Threaded/Manifest.toml b/JuliaStream.jl/Threaded/Manifest.toml new file mode 100644 index 0000000..608e2da --- /dev/null +++ b/JuliaStream.jl/Threaded/Manifest.toml @@ -0,0 +1,31 @@ +# This file is machine-generated - editing it directly is not advised + +[[ArgParse]] +deps = ["Logging", "TextWrap"] +git-tree-sha1 = "3102bce13da501c9104df33549f511cd25264d7d" +uuid = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" +version = "1.1.4" + +[[Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[OrderedCollections]] +git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.4.1" + +[[Parameters]] +deps = ["OrderedCollections", "UnPack"] +git-tree-sha1 = "2276ac65f1e236e0a6ea70baff3f62ad4c625345" +uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" +version = "0.12.2" + +[[TextWrap]] +git-tree-sha1 = "9250ef9b01b66667380cf3275b3f7488d0e25faf" +uuid = "b718987f-49a8-5099-9789-dcd902bef87d" +version = "1.0.1" + +[[UnPack]] +git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" +uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +version = "1.0.2" diff --git a/JuliaStream.jl/Threaded/Project.toml b/JuliaStream.jl/Threaded/Project.toml new file mode 100644 index 0000000..b65bdf5 --- /dev/null +++ b/JuliaStream.jl/Threaded/Project.toml @@ -0,0 +1,6 @@ +[deps] +ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" +Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a" + +[compat] +julia = "1.6" diff --git a/JuliaStream.jl/oneAPI/Manifest.toml b/JuliaStream.jl/oneAPI/Manifest.toml new file mode 100644 index 0000000..ca932aa --- /dev/null +++ b/JuliaStream.jl/oneAPI/Manifest.toml @@ -0,0 +1,319 @@ +# This file is machine-generated - editing it directly is not advised + +[[Adapt]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "84918055d15b3114ede17ac6a7182f68870c16f7" +uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +version = "3.3.1" + +[[ArgParse]] +deps = ["Logging", "TextWrap"] +git-tree-sha1 = "3102bce13da501c9104df33549f511cd25264d7d" +uuid = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" +version = "1.1.4" + +[[ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" + +[[Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[CEnum]] +git-tree-sha1 = "215a9aa4a1f23fbd05b92769fdd62559488d70e9" +uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82" +version = "0.4.1" + +[[ChainRulesCore]] +deps = ["Compat", "LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "bdc0937269321858ab2a4f288486cb258b9a0af7" +uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +version = "1.3.0" + +[[Compat]] +deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] +git-tree-sha1 = "79b9563ef3f2cc5fc6d3046a5ee1a57c9de52495" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "3.33.0" + +[[CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" + +[[Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[DelimitedFiles]] +deps = ["Mmap"] +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" + +[[Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[DocStringExtensions]] +deps = ["LibGit2"] +git-tree-sha1 = "a32185f5428d3986f47c2ab78b1f216d5e6cc96f" +uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +version = "0.8.5" + +[[Downloads]] +deps = ["ArgTools", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" + +[[ExprTools]] +git-tree-sha1 = "b7e3d17636b348f005f11040025ae8c6f645fe92" +uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" +version = "0.1.6" + +[[GPUArrays]] +deps = ["Adapt", "LinearAlgebra", "Printf", "Random", "Serialization", "Statistics"] +git-tree-sha1 = "8fac1cf7d6ce0f2249c7acaf25d22e1e85c4a07f" +uuid = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7" +version = "8.0.2" + +[[GPUCompiler]] +deps = ["ExprTools", "InteractiveUtils", "LLVM", "Libdl", "Logging", "TimerOutputs", "UUIDs"] +git-tree-sha1 = "4ed2616d5e656c8716736b64da86755467f26cf5" +uuid = "61eb1bfa-7361-4325-ad38-22787b887f55" +version = "0.12.9" + +[[InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[IrrationalConstants]] +git-tree-sha1 = "f76424439413893a832026ca355fe273e93bce94" +uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" +version = "0.1.0" + +[[JLLWrappers]] +deps = ["Preferences"] +git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.3.0" + +[[LLVM]] +deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Printf", "Unicode"] +git-tree-sha1 = "d6041ad706cf458b2c9f3e501152488a26451e9c" +uuid = "929cbde3-209d-540e-8aea-75f648917ca0" +version = "4.2.0" + +[[LLVMExtra_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "a9b1130c4728b0e462a1c28772954650039eb847" +uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab" +version = "0.0.7+0" + +[[LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" + +[[LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" + +[[LibGit2]] +deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" + +[[Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[LinearAlgebra]] +deps = ["Libdl"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[LogExpFunctions]] +deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "3d682c07e6dd250ed082f883dc88aee7996bf2cc" +uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" +version = "0.3.0" + +[[Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" + +[[Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" + +[[NEO_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "gmmlib_jll", "libigc_jll", "oneAPI_Level_Zero_Headers_jll"] +git-tree-sha1 = "2bfc354b5684821dcc88f1e477cefd0dd03c60b5" +uuid = "700fe977-ac61-5f37-bbc8-c6c4b2b6a9fd" +version = "21.31.20514+0" + +[[NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" + +[[OpenSpecFun_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" +uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" +version = "0.5.5+0" + +[[OrderedCollections]] +git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.4.1" + +[[Parameters]] +deps = ["OrderedCollections", "UnPack"] +git-tree-sha1 = "2276ac65f1e236e0a6ea70baff3f62ad4c625345" +uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" +version = "0.12.2" + +[[Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[Preferences]] +deps = ["TOML"] +git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.2.2" + +[[Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[Random]] +deps = ["Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[SPIRV_LLVM_Translator_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "8cca87d57f6ddf19373cc9791fddc741406c8fbf" +uuid = "4a5d46fc-d8cf-5151-a261-86b458210efb" +version = "11.0.0+2" + +[[SPIRV_Tools_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c0324b7e07bc4649f755bfe7e00f7c6ed6aa353f" +uuid = "6ac6d60f-d740-5983-97d7-a4482c0689f4" +version = "2021.2.0+0" + +[[Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[SharedArrays]] +deps = ["Distributed", "Mmap", "Random", "Serialization"] +uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + +[[Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[SpecialFunctions]] +deps = ["ChainRulesCore", "LogExpFunctions", "OpenSpecFun_jll"] +git-tree-sha1 = "a322a9493e49c5f3a10b50df3aedaf1cdb3244b7" +uuid = "276daf66-3868-5448-9aa4-cd146d93841b" +version = "1.6.1" + +[[Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" + +[[Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" + +[[Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[TextWrap]] +git-tree-sha1 = "9250ef9b01b66667380cf3275b3f7488d0e25faf" +uuid = "b718987f-49a8-5099-9789-dcd902bef87d" +version = "1.0.1" + +[[TimerOutputs]] +deps = ["ExprTools", "Printf"] +git-tree-sha1 = "209a8326c4f955e2442c07b56029e88bb48299c7" +uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" +version = "0.5.12" + +[[UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[UnPack]] +git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" +uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +version = "1.0.2" + +[[Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" + +[[gmmlib_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "0d5e5461d21b14853b4c332045c57d2601c403bd" +uuid = "09858cae-167c-5acb-9302-fddc6874d481" +version = "21.2.1+0" + +[[libigc_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "b30a895e7ea52991a3f984ab0302c42858d766c0" +uuid = "94295238-5935-5bd7-bb0f-b00942e9bdd5" +version = "1.0.8173+0" + +[[nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" + +[[oneAPI]] +deps = ["Adapt", "CEnum", "ExprTools", "GPUArrays", "GPUCompiler", "LLVM", "LinearAlgebra", "NEO_jll", "Printf", "Random", "SPIRV_LLVM_Translator_jll", "SPIRV_Tools_jll", "SpecialFunctions", "oneAPI_Level_Zero_Headers_jll", "oneAPI_Level_Zero_Loader_jll"] +git-tree-sha1 = "92e8eefdd4694597994590230ab329545804bdb3" +uuid = "8f75cd03-7ff8-4ecb-9b8f-daf728133b1b" +version = "0.2.0" + +[[oneAPI_Level_Zero_Headers_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e1d123ff9ada6c469a1eaf57e33a74c3cb26a5a4" +uuid = "f4bc562b-d309-54f8-9efb-476e56f0410d" +version = "1.2.13+0" + +[[oneAPI_Level_Zero_Loader_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "oneAPI_Level_Zero_Headers_jll"] +git-tree-sha1 = "50124857f7e87420655929a9c8ca86749826af11" +uuid = "13eca655-d68d-5b81-8367-6d99d727ab01" +version = "1.4.1+0" + +[[p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" diff --git a/JuliaStream.jl/oneAPI/Project.toml b/JuliaStream.jl/oneAPI/Project.toml new file mode 100644 index 0000000..9f89f82 --- /dev/null +++ b/JuliaStream.jl/oneAPI/Project.toml @@ -0,0 +1,7 @@ +[deps] +ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" +Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a" +oneAPI = "8f75cd03-7ff8-4ecb-9b8f-daf728133b1b" + +[compat] +julia = "1.6" diff --git a/JuliaStream.jl/src/AMDGPUStream.jl b/JuliaStream.jl/src/AMDGPUStream.jl index 9a9cd9a..3ed9748 100644 --- a/JuliaStream.jl/src/AMDGPUStream.jl +++ b/JuliaStream.jl/src/AMDGPUStream.jl @@ -6,32 +6,23 @@ const ROCData = StreamData{T,ROCArray{T}} where {T} const TBSize = 1024::Int const DotBlocks = 256::Int -# AMDGPU.agents()'s internal iteration order isn't stable -function gpu_agents_in_repr_order() - # XXX if we select anything other than :gpu, we get - # HSA_STATUS_ERROR_INVALID_AGENT on the first kernel submission - sort(AMDGPU.get_agents(:gpu), by = repr) -end - -function devices() +function devices()::Vector{DeviceWithRepr} try - map(repr, gpu_agents_in_repr_order()) + # AMDGPU.agents()'s internal iteration order isn't stable + sorted = sort(AMDGPU.get_agents(:gpu), by = repr) + map(x -> (x, repr(x), "AMDGPU.jl"), sorted) catch # probably unsupported - [] + String[] end end -function gridsize(data::ROCData{T})::Int where {T} - return data.size -end - function make_stream( arraysize::Int, scalar::T, - device::Int, + device::DeviceWithRepr, silent::Bool, -)::ROCData{T} where {T} +)::Tuple{ROCData{T},Nothing} where {T} if arraysize % TBSize != 0 error("arraysize ($(arraysize)) must be divisible by $(TBSize)!") @@ -39,30 +30,31 @@ function make_stream( # XXX AMDGPU doesn't expose an API for setting the default like CUDA.device!() # but AMDGPU.get_default_agent returns DEFAULT_AGENT so we can do it by hand - AMDGPU.DEFAULT_AGENT[] = gpu_agents_in_repr_order()[device] - - data = ROCData{T}( - ROCArray{T}(undef, arraysize), - ROCArray{T}(undef, arraysize), - ROCArray{T}(undef, arraysize), - scalar, - arraysize, - ) + AMDGPU.DEFAULT_AGENT[] = device[1] selected = AMDGPU.get_default_agent() if !silent println("Using GPU HSA device: $(AMDGPU.get_name(selected)) ($(repr(selected)))") - println("Kernel parameters : <<<$(gridsize(data)),$(TBSize)>>>") + println("Kernel parameters : <<<$(arraysize),$(TBSize)>>>") end - return data + return ( + ROCData{T}( + ROCArray{T}(undef, arraysize), + ROCArray{T}(undef, arraysize), + ROCArray{T}(undef, arraysize), + scalar, + arraysize, + ), + nothing, + ) end -function init_arrays!(data::ROCData{T}, init::Tuple{T,T,T}) where {T} +function init_arrays!(data::ROCData{T}, _, init::Tuple{T,T,T}) where {T} AMDGPU.fill!(data.a, init[1]) AMDGPU.fill!(data.b, init[2]) AMDGPU.fill!(data.c, init[3]) end -function copy!(data::ROCData{T}) where {T} +function copy!(data::ROCData{T}, _) where {T} function kernel(a, c) i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x # only workgroupIdx starts at 1 @inbounds c[i] = a[i] @@ -70,11 +62,11 @@ function copy!(data::ROCData{T}) where {T} end AMDGPU.wait( soft = false, # soft wait causes HSA_REFCOUNT overflow issues - @roc groupsize = TBSize gridsize = gridsize(data) kernel(data.a, data.c) + @roc groupsize = TBSize gridsize = data.size kernel(data.a, data.c) ) end -function mul!(data::ROCData{T}) where {T} +function mul!(data::ROCData{T}, _) where {T} function kernel(b, c, scalar) i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x # only workgroupIdx starts at 1 @inbounds b[i] = scalar * c[i] @@ -82,11 +74,11 @@ function mul!(data::ROCData{T}) where {T} end AMDGPU.wait( soft = false, # soft wait causes HSA_REFCOUNT overflow issues - @roc groupsize = TBSize gridsize = gridsize(data) kernel(data.b, data.c, data.scalar) + @roc groupsize = TBSize gridsize = data.size kernel(data.b, data.c, data.scalar) ) end -function add!(data::ROCData{T}) where {T} +function add!(data::ROCData{T}, _) where {T} function kernel(a, b, c) i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x # only workgroupIdx starts at 1 @inbounds c[i] = a[i] + b[i] @@ -94,11 +86,11 @@ function add!(data::ROCData{T}) where {T} end AMDGPU.wait( soft = false, # soft wait causes HSA_REFCOUNT overflow issues - @roc groupsize = TBSize gridsize = gridsize(data) kernel(data.a, data.b, data.c) + @roc groupsize = TBSize gridsize = data.size kernel(data.a, data.b, data.c) ) end -function triad!(data::ROCData{T}) where {T} +function triad!(data::ROCData{T}, _) where {T} function kernel(a, b, c, scalar) i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x # only workgroupIdx starts at 1 @inbounds a[i] = b[i] + (scalar * c[i]) @@ -106,7 +98,7 @@ function triad!(data::ROCData{T}) where {T} end AMDGPU.wait( soft = false, # soft wait causes HSA_REFCOUNT overflow issues - @roc groupsize = TBSize gridsize = gridsize(data) kernel( + @roc groupsize = TBSize gridsize = data.size kernel( data.a, data.b, data.c, @@ -115,7 +107,7 @@ function triad!(data::ROCData{T}) where {T} ) end -function nstream!(data::ROCData{T}) where {T} +function nstream!(data::ROCData{T}, _) where {T} function kernel(a, b, c, scalar) i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x # only workgroupIdx starts at 1 @inbounds a[i] += b[i] + scalar * c[i] @@ -123,7 +115,7 @@ function nstream!(data::ROCData{T}) where {T} end AMDGPU.wait( soft = false, # soft wait causes HSA_REFCOUNT overflow issues - @roc groupsize = TBSize gridsize = gridsize(data) kernel( + @roc groupsize = TBSize gridsize = data.size kernel( data.a, data.b, data.c, @@ -132,7 +124,7 @@ function nstream!(data::ROCData{T}) where {T} ) end -function dot(data::ROCData{T}) where {T} +function dot(data::ROCData{T}, _) where {T} function kernel(a, b, size, partial) tb_sum = ROCDeviceArray((TBSize,), alloc_local(:reduce, T, TBSize)) local_i = workitemIdx().x @@ -174,7 +166,7 @@ function dot(data::ROCData{T}) where {T} return sum(partial_sum) end -function read_data(data::ROCData{T})::VectorData{T} where {T} +function read_data(data::ROCData{T}, _)::VectorData{T} where {T} return VectorData{T}(data.a, data.b, data.c, data.scalar, data.size) end diff --git a/JuliaStream.jl/src/CUDAStream.jl b/JuliaStream.jl/src/CUDAStream.jl index 7d671a5..dd4fc44 100644 --- a/JuliaStream.jl/src/CUDAStream.jl +++ b/JuliaStream.jl/src/CUDAStream.jl @@ -5,51 +5,53 @@ const CuData = StreamData{T,CuArray{T}} where {T} const TBSize = 1024::Int const DotBlocks = 256::Int -function devices() - return !CUDA.functional(false) ? [] : - map(d -> "$(CUDA.name(d)) ($(repr(d)))", CUDA.devices()) +function devices()::Vector{DeviceWithRepr} + return !CUDA.functional(false) ? String[] : + map(d -> (d, "$(CUDA.name(d)) ($(repr(d)))", "CUDA.jl"), CUDA.devices()) end function make_stream( arraysize::Int, scalar::T, - device::Int, + device::DeviceWithRepr, silent::Bool, -)::CuData{T} where {T} +)::Tuple{CuData{T},Nothing} where {T} if arraysize % TBSize != 0 error("arraysize ($(arraysize)) must be divisible by $(TBSize)!") end # so CUDA's device is 0 indexed, so -1 from Julia - CUDA.device!(device - 1) + CUDA.device!(device[1]) selected = CUDA.device() # show_reason is set to true here so it dumps CUDA info # for us regardless of whether it's functional if !CUDA.functional(true) error("Non-functional CUDA configuration") end - data = CuData{T}( - CuArray{T}(undef, arraysize), - CuArray{T}(undef, arraysize), - CuArray{T}(undef, arraysize), - scalar, - arraysize, - ) if !silent println("Using CUDA device: $(CUDA.name(selected)) ($(repr(selected)))") - println("Kernel parameters: <<<$(data.size ÷ TBSize),$(TBSize)>>>") + println("Kernel parameters: <<<$(arraysize ÷ TBSize),$(TBSize)>>>") end - return data + return ( + CuData{T}( + CuArray{T}(undef, arraysize), + CuArray{T}(undef, arraysize), + CuArray{T}(undef, arraysize), + scalar, + arraysize, + ), + nothing, + ) end -function init_arrays!(data::CuData{T}, init::Tuple{T,T,T}) where {T} +function init_arrays!(data::CuData{T}, _, init::Tuple{T,T,T}) where {T} CUDA.fill!(data.a, init[1]) CUDA.fill!(data.b, init[2]) CUDA.fill!(data.c, init[3]) end -function copy!(data::CuData{T}) where {T} +function copy!(data::CuData{T}, _) where {T} function kernel(a, c) i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 @inbounds c[i] = a[i] @@ -59,7 +61,7 @@ function copy!(data::CuData{T}) where {T} CUDA.synchronize() end -function mul!(data::CuData{T}) where {T} +function mul!(data::CuData{T}, _) where {T} function kernel(b, c, scalar) i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 @inbounds b[i] = scalar * c[i] @@ -69,7 +71,7 @@ function mul!(data::CuData{T}) where {T} CUDA.synchronize() end -function add!(data::CuData{T}) where {T} +function add!(data::CuData{T}, _) where {T} function kernel(a, b, c) i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 @inbounds c[i] = a[i] + b[i] @@ -79,7 +81,7 @@ function add!(data::CuData{T}) where {T} CUDA.synchronize() end -function triad!(data::CuData{T}) where {T} +function triad!(data::CuData{T}, _) where {T} function kernel(a, b, c, scalar) i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 @inbounds a[i] = b[i] + (scalar * c[i]) @@ -94,7 +96,7 @@ function triad!(data::CuData{T}) where {T} CUDA.synchronize() end -function nstream!(data::CuData{T}) where {T} +function nstream!(data::CuData{T}, _) where {T} function kernel(a, b, c, scalar) i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 @inbounds a[i] += b[i] + scalar * c[i] @@ -109,7 +111,7 @@ function nstream!(data::CuData{T}) where {T} CUDA.synchronize() end -function dot(data::CuData{T}) where {T} +function dot(data::CuData{T}, _) where {T} # direct port of the reduction in CUDAStream.cu function kernel(a, b, size, partial) tb_sum = @cuStaticSharedMem(T, TBSize) @@ -145,7 +147,7 @@ function dot(data::CuData{T}) where {T} return sum(partial_sum) end -function read_data(data::CuData{T})::VectorData{T} where {T} +function read_data(data::CuData{T}, _)::VectorData{T} where {T} return VectorData{T}(data.a, data.b, data.c, data.scalar, data.size) end diff --git a/JuliaStream.jl/src/DistributedStream.jl b/JuliaStream.jl/src/DistributedStream.jl index 2aa7ae7..2e80168 100644 --- a/JuliaStream.jl/src/DistributedStream.jl +++ b/JuliaStream.jl/src/DistributedStream.jl @@ -1,39 +1,39 @@ using Distributed @everywhere using Pkg -@everywhere Pkg.activate("."; io=devnull) # don't spam `Activating environment at...` +@everywhere Pkg.activate("."; io = devnull) # don't spam `Activating environment at...` @everywhere include("StreamData.jl") @everywhere include("Stream.jl") @everywhere using SharedArrays @everywhere const SharedArrayData = StreamData{T,SharedArray{T}} where {T} -function devices() - return ["CPU (localhost)"] +function devices()::Vector{DeviceWithRepr} + return [(undef, "CPU (localhost) $(nworkers())P", "Distributed.jl")] end function make_stream( arraysize::Int, scalar::T, - device::Int, + _::DeviceWithRepr, silent::Bool, -)::SharedArrayData{T} where {T} - if device != 1 - error("Only CPU device is supported") - end +)::Tuple{SharedArrayData{T},Nothing} where {T} if !silent println("Using max $(nworkers()) process(es) + 1 master") end - return SharedArrayData{T}( - SharedArray{T}(arraysize), - SharedArray{T}(arraysize), - SharedArray{T}(arraysize), - scalar, - arraysize, + return ( + SharedArrayData{T}( + SharedArray{T}(arraysize), + SharedArray{T}(arraysize), + SharedArray{T}(arraysize), + scalar, + arraysize, + ), + nothing, ) end -function init_arrays!(data::SharedArrayData{T}, init::Tuple{T,T,T}) where {T} +function init_arrays!(data::SharedArrayData{T}, _, init::Tuple{T,T,T}) where {T} @sync @distributed for i = 1:data.size @inbounds data.a[i] = init[1] @@ -42,43 +42,43 @@ function init_arrays!(data::SharedArrayData{T}, init::Tuple{T,T,T}) where {T} end end -function copy!(data::SharedArrayData{T}) where {T} +function copy!(data::SharedArrayData{T}, _) where {T} @sync @distributed for i = 1:data.size @inbounds data.c[i] = data.a[i] end end -function mul!(data::SharedArrayData{T}) where {T} +function mul!(data::SharedArrayData{T}, _) where {T} @sync @distributed for i = 1:data.size @inbounds data.b[i] = data.scalar * data.c[i] end end -function add!(data::SharedArrayData{T}) where {T} +function add!(data::SharedArrayData{T}, _) where {T} @sync @distributed for i = 1:data.size @inbounds data.c[i] = data.a[i] + data.b[i] end end -function triad!(data::SharedArrayData{T}) where {T} +function triad!(data::SharedArrayData{T}, _) where {T} @sync @distributed for i = 1:data.size @inbounds data.a[i] = data.b[i] + (data.scalar * data.c[i]) end end -function nstream!(data::SharedArrayData{T}) where {T} +function nstream!(data::SharedArrayData{T}, _) where {T} @sync @distributed for i = 1:data.size @inbounds data.a[i] += data.b[i] + data.scalar * data.c[i] end end -function dot(data::SharedArrayData{T}) where {T} +function dot(data::SharedArrayData{T}, _) where {T} return @distributed (+) for i = 1:data.size @inbounds data.a[i] * data.b[i] end end -function read_data(data::SharedArrayData{T})::VectorData{T} where {T} +function read_data(data::SharedArrayData{T}, _)::VectorData{T} where {T} return VectorData{T}(data.a, data.b, data.c, data.scalar, data.size) end diff --git a/JuliaStream.jl/src/KernelAbstractionsStream.jl b/JuliaStream.jl/src/KernelAbstractionsStream.jl new file mode 100644 index 0000000..8cc3699 --- /dev/null +++ b/JuliaStream.jl/src/KernelAbstractionsStream.jl @@ -0,0 +1,255 @@ +using ROCKernels, CUDAKernels, KernelAbstractions, CUDA, AMDGPU +include("Stream.jl") + +const CuData = StreamData{T,CUDA.CuArray{T}} where {T} +const ROCData = StreamData{T,AMDGPU.ROCArray{T}} where {T} + +const TBSize = 1024::Int +const DotBlocks = 256::Int + +@enum Backend cuda rocm cpu + +struct Context + backend::Backend + device::Device +end + +function list_rocm_devices()::Vector{DeviceWithRepr} + try + # AMDGPU.agents()'s internal iteration order isn't stable + sorted = sort(AMDGPU.get_agents(:gpu), by = repr) + map(x -> (x, repr(x), rocm), sorted) + catch + # probably unsupported + [] + end +end + +function list_cuda_devices()::Vector{DeviceWithRepr} + return !CUDA.functional(false) ? String[] : + map(d -> (d, "$(CUDA.name(d)) ($(repr(d)))", cuda), CUDA.devices()) +end + +function devices()::Vector{DeviceWithRepr} + cudas = list_cuda_devices() + rocms = list_rocm_devices() + cpus = [(undef, "$(Sys.cpu_info()[1].model) ($(Threads.nthreads())T)", cpu)] + vcat(cpus, cudas, rocms) +end + +function make_stream( + arraysize::Int, + scalar::T, + device::DeviceWithRepr, + silent::Bool, +) where {T} + + if arraysize % TBSize != 0 + error("arraysize ($(arraysize)) must be divisible by $(TBSize)!") + end + + (selected, _, backend) = device + if backend == cpu + if !silent + println("Using CPU with max $(Threads.nthreads()) threads") + end + partialsum = Vector{T}(undef, DotBlocks) + data = VectorData{T}( + Vector{T}(undef, arraysize), + Vector{T}(undef, arraysize), + Vector{T}(undef, arraysize), + scalar, + arraysize, + ) + backenddevice = CPU() + elseif backend == cuda + CUDA.device!(selected) + if CUDA.device() != selected + error("Cannot select CUDA device, expecting $selected, but got $(CUDA.device())") + end + if !CUDA.functional(true) + error("Non-functional CUDA configuration") + end + if !silent + println("Using CUDA device: $(CUDA.name(selected)) ($(repr(selected)))") + end + partialsum = CuArray{T}(undef, DotBlocks) + data = CuData{T}( + CuArray{T}(undef, arraysize), + CuArray{T}(undef, arraysize), + CuArray{T}(undef, arraysize), + scalar, + arraysize, + ) + backenddevice = CUDADevice() + elseif backend == rocm + AMDGPU.DEFAULT_AGENT[] = selected + if AMDGPU.get_default_agent() != selected + error( + "Cannot select HSA device, expecting $selected, but got $(AMDGPU.get_default_agent())", + ) + end + if !silent + println("Using GPU HSA device: $(AMDGPU.get_name(selected)) ($(repr(selected)))") + end + partialsum = ROCArray{T}(undef, DotBlocks) + data = ROCData{T}( + ROCArray{T}(undef, arraysize), + ROCArray{T}(undef, arraysize), + ROCArray{T}(undef, arraysize), + scalar, + arraysize, + ) + backenddevice = ROCDevice() + else + error("unsupported backend $(backend)") + end + + if !silent + println("Kernel parameters : <<<$(data.size),$(TBSize)>>>") + end + return (data, Context(backend, backenddevice)) +end + +function init_arrays!( + data::StreamData{T,C}, + context::Context, + init::Tuple{T,T,T}, +) where {T,C} + if context.backend == cpu + Threads.@threads for i = 1:data.size + @inbounds data.a[i] = init[1] + @inbounds data.b[i] = init[2] + @inbounds data.c[i] = init[3] + end + elseif context.backend == cuda + CUDA.fill!(data.a, init[1]) + CUDA.fill!(data.b, init[2]) + CUDA.fill!(data.c, init[3]) + elseif context.backend == rocm + AMDGPU.fill!(data.a, init[1]) + AMDGPU.fill!(data.b, init[2]) + AMDGPU.fill!(data.c, init[3]) + else + error("unsupported backend $(backend)") + end +end + +function copy!(data::StreamData{T,C}, context::Context) where {T,C} + @kernel function kernel(@Const(a), c) + i = @index(Global) + @inbounds c[i] = a[i] + end + wait(kernel(context.device, TBSize)(data.a, data.c, ndrange = data.size)) +end + +function mul!(data::StreamData{T,C}, context::Context) where {T,C} + @kernel function kernel(b, @Const(c), scalar) + i = @index(Global) + @inbounds b[i] = scalar * c[i] + end + wait(kernel(context.device, TBSize)(data.b, data.c, data.scalar, ndrange = data.size)) +end + +function add!(data::StreamData{T,C}, context::Context) where {T,C} + @kernel function kernel(@Const(a), @Const(b), c) + i = @index(Global) + @inbounds c[i] = a[i] + b[i] + end + wait(kernel(context.device, TBSize)(data.a, data.b, data.c, ndrange = data.size)) +end + +function triad!(data::StreamData{T,C}, context::Context) where {T,C} + @kernel function kernel(a, @Const(b), @Const(c), scalar) + i = @index(Global) + @inbounds a[i] = b[i] + (scalar * c[i]) + end + wait( + kernel(context.device, TBSize)( + data.a, + data.b, + data.c, + data.scalar, + ndrange = data.size, + ), + ) +end + +function nstream!(data::StreamData{T,C}, context::Context) where {T,C} + @kernel function kernel(a, @Const(b), @Const(c), scalar) + i = @index(Global) + @inbounds a[i] += b[i] + scalar * c[i] + end + wait( + kernel(context.device, TBSize)( + data.a, + data.b, + data.c, + data.scalar, + ndrange = data.size, + ), + ) +end + +function dot(data::StreamData{T,C}, context::Context) where {T,C} + @kernel function kernel(@Const(a), @Const(b), size, partial) + local_i = @index(Local) + group_i = @index(Group) + tb_sum = @localmem T TBSize + @inbounds tb_sum[local_i] = 0.0 + + # do dot first + i = @index(Global) + while i <= size + @inbounds tb_sum[local_i] += a[i] * b[i] + i += TBSize * DotBlocks + end + + # then tree reduction + # FIXME this does not compile when targeting CPUs: + # see https://github.com/JuliaGPU/KernelAbstractions.jl/issues/262 + offset = @private Int64 (1,) + @inbounds begin + offset[1] = @groupsize()[1] ÷ 2 + while offset[1] > 0 + @synchronize + if (local_i - 1) < offset[1] + tb_sum[local_i] += tb_sum[local_i+offset[1]] + end + offset[1] ÷= 2 + end + end + + if (local_i == 1) + @inbounds partial[group_i] = tb_sum[local_i] + end + end + + if context.backend == cpu + partial_sum = Vector{T}(undef, DotBlocks) + elseif context.backend == cuda + partial_sum = CuArray{T}(undef, DotBlocks) + elseif context.backend == rocm + partial_sum = ROCArray{T}(undef, DotBlocks) + else + error("unsupported backend $(backend)") + end + + wait( + kernel(context.device, TBSize)( + data.a, + data.b, + data.size, + partial_sum, + ndrange = TBSize * DotBlocks, + ), + ) + + return sum(partial_sum) +end + +function read_data(data::StreamData{T,C}, _::Context)::VectorData{T} where {T,C} + return VectorData{T}(data.a, data.b, data.c, data.scalar, data.size) +end + +main() diff --git a/JuliaStream.jl/src/Stream.jl b/JuliaStream.jl/src/Stream.jl index 590ab2d..b7fc940 100644 --- a/JuliaStream.jl/src/Stream.jl +++ b/JuliaStream.jl/src/Stream.jl @@ -7,6 +7,8 @@ include("StreamData.jl") const VectorData = StreamData{T,Vector{T}} where {T} +const DeviceWithRepr = Tuple{Any,String,Any} + struct Timings copy::Vector{Float64} mul::Vector{Float64} @@ -18,29 +20,33 @@ end @enum Benchmark All Triad Nstream -function run_all!(data::StreamData{T,C}, times::Int)::Tuple{Timings,T} where {T,C} +function run_all!(data::StreamData{T,C}, context, times::Int)::Tuple{Timings,T} where {T,C} timings = Timings(times) lastSum::T = 0 for i = 1:times - @inbounds timings.copy[i] = @elapsed copy!(data) - @inbounds timings.mul[i] = @elapsed mul!(data) - @inbounds timings.add[i] = @elapsed add!(data) - @inbounds timings.triad[i] = @elapsed triad!(data) - @inbounds timings.dot[i] = @elapsed lastSum = dot(data) + @inbounds timings.copy[i] = @elapsed copy!(data, context) + @inbounds timings.mul[i] = @elapsed mul!(data, context) + @inbounds timings.add[i] = @elapsed add!(data, context) + @inbounds timings.triad[i] = @elapsed triad!(data, context) + @inbounds timings.dot[i] = @elapsed lastSum = dot(data, context) end return (timings, lastSum) end -function run_triad!(data::StreamData{T,C}, times::Int)::Float64 where {T,C} +function run_triad!(data::StreamData{T,C}, context, times::Int)::Float64 where {T,C} return @elapsed for _ = 1:times - triad!(data) + triad!(data, context) end end -function run_nstream!(data::StreamData{T,C}, times::Int)::Vector{Float64} where {T,C} +function run_nstream!( + data::StreamData{T,C}, + context, + times::Int, +)::Vector{Float64} where {T,C} timings::Vector{Float64} = zeros(times) for i = 1:times - @inbounds timings[i] = @elapsed nstream!(data) + @inbounds timings[i] = @elapsed nstream!(data, context) end return timings end @@ -160,25 +166,23 @@ function main() parse_options(config) if config.list - ds = devices() - for (i, device) in enumerate(ds) - println("[$i] $(device)") + for (i, (_,repr, impl)) in enumerate(devices()) + println("[$i] ($impl) $repr") end exit(0) end ds = devices() + # TODO implement substring device match if config.device < 1 || config.device > length(ds) error( "Device $(config.device) out of range (1..$(length(ds))), NOTE: Julia is 1-indexed", ) + else + device = ds[config.device] end - if config.float - type = Float32 - else - type = Float64 - end + type = config.float ? Float32 : Float64 if config.nstream_only && !config.triad_only benchmark = Nstream @@ -256,12 +260,12 @@ function main() init::Tuple{type,type,type} = DefaultInit scalar::type = DefaultScalar - data = make_stream(config.arraysize, scalar, config.device, config.csv) + (data, context) = make_stream(config.arraysize, scalar, device, config.csv) - init_arrays!(data, init) + init_arrays!(data, context, init) if benchmark == All - (timings, sum) = run_all!(data, config.numtimes) - valid = check_solutions(read_data(data), config.numtimes, init, benchmark, sum) + (timings, sum) = run_all!(data, context, config.numtimes) + valid = check_solutions(read_data(data, context), config.numtimes, init, benchmark, sum) tabulate( mk_row(timings.copy, "Copy", 2 * array_bytes), mk_row(timings.mul, "Mul", 2 * array_bytes), @@ -270,12 +274,14 @@ function main() mk_row(timings.dot, "Dot", 2 * array_bytes), ) elseif benchmark == Nstream - timings = run_nstream!(data, config.numtimes) - valid = check_solutions(read_data(data), config.numtimes, init, benchmark, nothing) + timings = run_nstream!(data, context, config.numtimes) + valid = + check_solutions(read_data(data, context), config.numtimes, init, benchmark, nothing) tabulate(mk_row(timings, "Nstream", 4 * array_bytes)) elseif benchmark == Triad - elapsed = run_triad!(data, config.numtimes) - valid = check_solutions(read_data(data), config.numtimes, init, benchmark, nothing) + elapsed = run_triad!(data, context, config.numtimes) + valid = + check_solutions(read_data(data, context), config.numtimes, init, benchmark, nothing) total_bytes = 3 * array_bytes * config.numtimes bandwidth = mega_scale * (total_bytes / elapsed) println("Runtime (seconds): $(round(elapsed; digits=5))") diff --git a/JuliaStream.jl/src/ThreadedStream.jl b/JuliaStream.jl/src/ThreadedStream.jl index fb995e6..4422e66 100644 --- a/JuliaStream.jl/src/ThreadedStream.jl +++ b/JuliaStream.jl/src/ThreadedStream.jl @@ -1,25 +1,31 @@ include("Stream.jl") -function devices() - return ["CPU"] +function devices()::Vector{DeviceWithRepr} + return [(undef, "$(Sys.cpu_info()[1].model) ($(Threads.nthreads())T)", "Threaded")] end function make_stream( arraysize::Int, scalar::T, - device::Int, + _::DeviceWithRepr, silent::Bool, -)::VectorData{T} where {T} - if device != 1 - error("Only CPU device is supported") - end +)::Tuple{VectorData{T},Nothing} where {T} if !silent println("Using max $(Threads.nthreads()) threads") end - return VectorData{T}(1:arraysize, 1:arraysize, 1:arraysize, scalar, arraysize) + return ( + VectorData{T}( + Vector{T}(undef, arraysize), + Vector{T}(undef, arraysize), + Vector{T}(undef, arraysize), + scalar, + arraysize, + ), + nothing + ) end -function init_arrays!(data::VectorData{T}, init::Tuple{T,T,T}) where {T} +function init_arrays!(data::VectorData{T}, _, init::Tuple{T,T,T}) where {T} Threads.@threads for i = 1:data.size @inbounds data.a[i] = init[1] @inbounds data.b[i] = init[2] @@ -27,37 +33,37 @@ function init_arrays!(data::VectorData{T}, init::Tuple{T,T,T}) where {T} end end -function copy!(data::VectorData{T}) where {T} +function copy!(data::VectorData{T}, _) where {T} Threads.@threads for i = 1:data.size @inbounds data.c[i] = data.a[i] end end -function mul!(data::VectorData{T}) where {T} +function mul!(data::VectorData{T}, _) where {T} Threads.@threads for i = 1:data.size @inbounds data.b[i] = data.scalar * data.c[i] end end -function add!(data::VectorData{T}) where {T} +function add!(data::VectorData{T}, _) where {T} Threads.@threads for i = 1:data.size @inbounds data.c[i] = data.a[i] + data.b[i] end end -function triad!(data::VectorData{T}) where {T} +function triad!(data::VectorData{T}, _) where {T} Threads.@threads for i = 1:data.size @inbounds data.a[i] = data.b[i] + (data.scalar * data.c[i]) end end -function nstream!(data::VectorData{T}) where {T} +function nstream!(data::VectorData{T}, _) where {T} Threads.@threads for i = 1:data.size @inbounds data.a[i] += data.b[i] + data.scalar * data.c[i] end end -function dot(data::VectorData{T}) where {T} +function dot(data::VectorData{T}, _) where {T} partial = zeros(T, Threads.nthreads()) Threads.@threads for i = 1:data.size @inbounds partial[Threads.threadid()] += data.a[i] * data.b[i] @@ -65,7 +71,7 @@ function dot(data::VectorData{T}) where {T} return sum(partial) end -function read_data(data::VectorData{T})::VectorData{T} where {T} +function read_data(data::VectorData{T}, _)::VectorData{T} where {T} return data end diff --git a/JuliaStream.jl/src/oneAPIStream.jl b/JuliaStream.jl/src/oneAPIStream.jl new file mode 100644 index 0000000..1bc319d --- /dev/null +++ b/JuliaStream.jl/src/oneAPIStream.jl @@ -0,0 +1,170 @@ +using Base.Iterators: println +using Base.Iterators: println +using Printf: Iterators + +include("Stream.jl") +using oneAPI + +const oneData = StreamData{T,oneArray{T}} where {T} +const DotWGSize = 256::Int + +function devices()::Vector{DeviceWithRepr} + all = map(oneL0.devices, oneL0.drivers()) |> Iterators.flatten |> Iterators.collect + map(dev -> (dev, repr("text/plain", dev), "oneAPi.jl"), all) +end + +function make_stream( + arraysize::Int, + scalar::T, + device::DeviceWithRepr, + silent::Bool, +)::Tuple{oneData{T},Int} where {T} + + oneAPI.allowscalar(false) + oneAPI.device!(device[1]) + + props = oneL0.compute_properties(oneAPI.device()) + groupsize = min(props.maxTotalGroupSize, arraysize) + + if arraysize % groupsize != 0 + error("arraysize ($(arraysize)) must be divisible by $(groupsize)!") + end + + if !silent + println("Using L0 device: $(repr("text/plain",device[1]))") + println("Kernel parameters : <<<$(arraysize),$(groupsize)>>>") + end + return ( + oneData{T}( + oneArray{T}(undef, arraysize), + oneArray{T}(undef, arraysize), + oneArray{T}(undef, arraysize), + scalar, + arraysize, + ), + groupsize, + ) +end + +function init_arrays!(data::oneData{T}, _, init::Tuple{T,T,T}) where {T} + oneAPI.fill!(data.a, init[1]) + oneAPI.fill!(data.b, init[2]) + oneAPI.fill!(data.c, init[3]) +end + +function copy!(data::oneData{T}, groupsize::Int) where {T} + function kernel(a, c) + i = get_global_id() + @inbounds c[i] = a[i] + return + end + @oneapi items = groupsize groups = data.size ÷ groupsize kernel( # + data.a, + data.c, + ) + oneAPI.synchronize() +end + +function mul!(data::oneData{T}, groupsize::Int) where {T} + function kernel(b, c, scalar) + i = get_global_id() + @inbounds b[i] = scalar * c[i] + return + end + @oneapi items = groupsize groups = data.size ÷ groupsize kernel( # + data.b, + data.c, + data.scalar, + ) + oneAPI.synchronize() +end + +function add!(data::oneData{T}, groupsize::Int) where {T} + function kernel(a, b, c) + i = get_global_id() + @inbounds c[i] = a[i] + b[i] + return + end + @oneapi items = groupsize groups = data.size ÷ groupsize kernel( # + data.a, + data.b, + data.c, + ) + oneAPI.synchronize() +end + +function triad!(data::oneData{T}, groupsize::Int) where {T} + function kernel(a, b, c, scalar) + i = get_global_id() + @inbounds a[i] = b[i] + (scalar * c[i]) + return + end + @oneapi items = groupsize groups = data.size ÷ groupsize kernel( # + data.a, + data.b, + data.c, + data.scalar, + ) + oneAPI.synchronize() +end + +function nstream!(data::oneData{T}, groupsize::Int) where {T} + function kernel(a, b, c, scalar) + i = get_global_id() + @inbounds a[i] += b[i] + scalar * c[i] + return + end + @oneapi items = groupsize groups = data.size ÷ groupsize kernel( # + data.a, + data.b, + data.c, + data.scalar, + ) + oneAPI.synchronize() +end + +function dot(data::oneData{T}, groupsize::Int) where {T} + function kernel(a, b, size, partial) + wg_sum = @LocalMemory(T, (DotWGSize,)) + li = get_local_id() + @inbounds wg_sum[li] = 0.0 + + # do dot first + i = get_global_id() + while i <= size + @inbounds wg_sum[li] += a[i] * b[i] + i += get_global_size() + end + + # then tree reduction + offset = get_local_size() ÷ 2 + while offset > 0 + barrier() + if li <= offset + @inbounds wg_sum[li] += wg_sum[li+offset] + end + offset ÷= 2 + end + + if li == 1 + @inbounds partial[get_group_id()] = wg_sum[li] + end + + return + end + partial_sum = oneArray{T}(undef, groupsize) + @oneapi items = groupsize groups = DotWGSize kernel( + data.a, + data.b, + data.size, + partial_sum, + ) + oneAPI.synchronize() + return sum(partial_sum) +end + +function read_data(data::oneData{T}, _)::VectorData{T} where {T} + return VectorData{T}(data.a, data.b, data.c, data.scalar, data.size) +end + +main() \ No newline at end of file diff --git a/JuliaStream.jl/update_all.sh b/JuliaStream.jl/update_all.sh new file mode 100755 index 0000000..ad6c2ee --- /dev/null +++ b/JuliaStream.jl/update_all.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# shellcheck disable=SC2034 disable=SC2153 + +for BACKEND in "." "AMDGPU" "CUDA" "oneAPI" "Threaded" "KernelAbstractions" +do + julia --project="$BACKEND" -e 'import Pkg; Pkg.resolve(); Pkg.instantiate(); Pkg.update(); Pkg.gc();' +done \ No newline at end of file From bb271dd046643e7ac83fd3891a261ffb00f4abc5 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Wed, 18 Aug 2021 01:59:06 +0100 Subject: [PATCH 46/78] Update PlainStream with context --- JuliaStream.jl/src/PlainStream.jl | 41 ++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/JuliaStream.jl/src/PlainStream.jl b/JuliaStream.jl/src/PlainStream.jl index 259a9b7..654d6eb 100644 --- a/JuliaStream.jl/src/PlainStream.jl +++ b/JuliaStream.jl/src/PlainStream.jl @@ -1,17 +1,28 @@ include("Stream.jl") -function devices() - return ["CPU"] +function devices()::Vector{DeviceWithRepr} + return [(undef, "CPU", "Palin")] end -function make_stream(arraysize::Int, scalar::T, device::Int, silent::Bool)::VectorData{T} where {T} - if device != 1 - error("Only CPU device is supported") - end - return VectorData{T}(1:arraysize, 1:arraysize, 1:arraysize, scalar, arraysize) +function make_stream( + arraysize::Int, + scalar::T, + _::DeviceWithRepr, + silent::Bool, +)::Tuple{VectorData{T},Nothing} where {T} + return ( + VectorData{T}( + Vector{T}(undef, arraysize), + Vector{T}(undef, arraysize), + Vector{T}(undef, arraysize), + scalar, + arraysize, + ), + nothing + ) end -function init_arrays!(data::VectorData{T}, init::Tuple{T,T,T}) where {T} +function init_arrays!(data::VectorData{T}, _, init::Tuple{T,T,T}) where {T} for i = 1:data.size @inbounds data.a[i] = init[1] @inbounds data.b[i] = init[2] @@ -19,37 +30,37 @@ function init_arrays!(data::VectorData{T}, init::Tuple{T,T,T}) where {T} end end -function copy!(data::VectorData{T}) where {T} +function copy!(data::VectorData{T}, _) where {T} for i = 1:data.size @inbounds data.c[i] = data.a[i] end end -function mul!(data::VectorData{T}) where {T} +function mul!(data::VectorData{T}, _) where {T} for i = 1:data.size @inbounds data.b[i] = data.scalar * data.c[i] end end -function add!(data::VectorData{T}) where {T} +function add!(data::VectorData{T}, _) where {T} for i = 1:data.size @inbounds data.c[i] = data.a[i] + data.b[i] end end -function triad!(data::VectorData{T}) where {T} +function triad!(data::VectorData{T}, _) where {T} for i = 1:data.size @inbounds data.a[i] = data.b[i] + (data.scalar * data.c[i]) end end -function nstream!(data::VectorData{T}) where {T} +function nstream!(data::VectorData{T}, _) where {T} for i = 1:data.size @inbounds data.a[i] += data.b[i] + data.scalar * data.c[i] end end -function dot(data::VectorData{T}) where {T} +function dot(data::VectorData{T}, _) where {T} sum = zero(T) for i = 1:data.size @inbounds sum += data.a[i] * data.b[i] @@ -57,7 +68,7 @@ function dot(data::VectorData{T}) where {T} return sum end -function read_data(data::VectorData{T})::VectorData{T} where {T} +function read_data(data::VectorData{T}, _)::VectorData{T} where {T} return data end From c445b646909c95ee25658d7ac7ef8bc0e381f55b Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Wed, 18 Aug 2021 02:00:50 +0100 Subject: [PATCH 47/78] Address CUDA comments Drop soft=false for AMDGPU as this option was removed Update dependencies --- JuliaStream.jl/AMDGPU/Manifest.toml | 4 ++-- JuliaStream.jl/CUDA/Manifest.toml | 8 ++++---- .../KernelAbstractions/Manifest.toml | 4 ++-- JuliaStream.jl/oneAPI/Manifest.toml | 4 ++-- JuliaStream.jl/src/AMDGPUStream.jl | 6 ------ JuliaStream.jl/src/CUDAStream.jl | 20 +++++++++---------- 6 files changed, 19 insertions(+), 27 deletions(-) diff --git a/JuliaStream.jl/AMDGPU/Manifest.toml b/JuliaStream.jl/AMDGPU/Manifest.toml index 6e27f0a..5d1a8a7 100644 --- a/JuliaStream.jl/AMDGPU/Manifest.toml +++ b/JuliaStream.jl/AMDGPU/Manifest.toml @@ -115,9 +115,9 @@ version = "4.2.0" [[LLVMExtra_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "a9b1130c4728b0e462a1c28772954650039eb847" +git-tree-sha1 = "2d5a0044d6505f4771b5c82de87393f0c9741154" uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab" -version = "0.0.7+0" +version = "0.0.8+0" [[LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] diff --git a/JuliaStream.jl/CUDA/Manifest.toml b/JuliaStream.jl/CUDA/Manifest.toml index 7330228..af0acfc 100644 --- a/JuliaStream.jl/CUDA/Manifest.toml +++ b/JuliaStream.jl/CUDA/Manifest.toml @@ -40,9 +40,9 @@ version = "0.4.1" [[CUDA]] deps = ["AbstractFFTs", "Adapt", "BFloat16s", "CEnum", "CompilerSupportLibraries_jll", "ExprTools", "GPUArrays", "GPUCompiler", "LLVM", "LazyArtifacts", "Libdl", "LinearAlgebra", "Logging", "Printf", "Random", "Random123", "RandomNumbers", "Reexport", "Requires", "SparseArrays", "SpecialFunctions", "TimerOutputs"] -git-tree-sha1 = "9303b20dfa74e4bcb4da425d351d551fbb5850be" +git-tree-sha1 = "c583f3ccdce071b8a8bce9bf3d5d5409eaf36d2b" uuid = "052768ef-5323-5732-b1bb-66c8b64840ba" -version = "3.4.0" +version = "3.4.1" [[ChainRulesCore]] deps = ["Compat", "LinearAlgebra", "SparseArrays"] @@ -122,9 +122,9 @@ version = "4.2.0" [[LLVMExtra_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "a9b1130c4728b0e462a1c28772954650039eb847" +git-tree-sha1 = "2d5a0044d6505f4771b5c82de87393f0c9741154" uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab" -version = "0.0.7+0" +version = "0.0.8+0" [[LazyArtifacts]] deps = ["Artifacts", "Pkg"] diff --git a/JuliaStream.jl/KernelAbstractions/Manifest.toml b/JuliaStream.jl/KernelAbstractions/Manifest.toml index 5c24cf5..25fd8d1 100644 --- a/JuliaStream.jl/KernelAbstractions/Manifest.toml +++ b/JuliaStream.jl/KernelAbstractions/Manifest.toml @@ -185,9 +185,9 @@ version = "4.2.0" [[LLVMExtra_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "a9b1130c4728b0e462a1c28772954650039eb847" +git-tree-sha1 = "2d5a0044d6505f4771b5c82de87393f0c9741154" uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab" -version = "0.0.7+0" +version = "0.0.8+0" [[LazyArtifacts]] deps = ["Artifacts", "Pkg"] diff --git a/JuliaStream.jl/oneAPI/Manifest.toml b/JuliaStream.jl/oneAPI/Manifest.toml index ca932aa..3aab94b 100644 --- a/JuliaStream.jl/oneAPI/Manifest.toml +++ b/JuliaStream.jl/oneAPI/Manifest.toml @@ -104,9 +104,9 @@ version = "4.2.0" [[LLVMExtra_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "a9b1130c4728b0e462a1c28772954650039eb847" +git-tree-sha1 = "2d5a0044d6505f4771b5c82de87393f0c9741154" uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab" -version = "0.0.7+0" +version = "0.0.8+0" [[LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] diff --git a/JuliaStream.jl/src/AMDGPUStream.jl b/JuliaStream.jl/src/AMDGPUStream.jl index 3ed9748..8347637 100644 --- a/JuliaStream.jl/src/AMDGPUStream.jl +++ b/JuliaStream.jl/src/AMDGPUStream.jl @@ -61,7 +61,6 @@ function copy!(data::ROCData{T}, _) where {T} return end AMDGPU.wait( - soft = false, # soft wait causes HSA_REFCOUNT overflow issues @roc groupsize = TBSize gridsize = data.size kernel(data.a, data.c) ) end @@ -73,7 +72,6 @@ function mul!(data::ROCData{T}, _) where {T} return end AMDGPU.wait( - soft = false, # soft wait causes HSA_REFCOUNT overflow issues @roc groupsize = TBSize gridsize = data.size kernel(data.b, data.c, data.scalar) ) end @@ -85,7 +83,6 @@ function add!(data::ROCData{T}, _) where {T} return end AMDGPU.wait( - soft = false, # soft wait causes HSA_REFCOUNT overflow issues @roc groupsize = TBSize gridsize = data.size kernel(data.a, data.b, data.c) ) end @@ -97,7 +94,6 @@ function triad!(data::ROCData{T}, _) where {T} return end AMDGPU.wait( - soft = false, # soft wait causes HSA_REFCOUNT overflow issues @roc groupsize = TBSize gridsize = data.size kernel( data.a, data.b, @@ -114,7 +110,6 @@ function nstream!(data::ROCData{T}, _) where {T} return end AMDGPU.wait( - soft = false, # soft wait causes HSA_REFCOUNT overflow issues @roc groupsize = TBSize gridsize = data.size kernel( data.a, data.b, @@ -155,7 +150,6 @@ function dot(data::ROCData{T}, _) where {T} end partial_sum = ROCArray{T}(undef, DotBlocks) AMDGPU.wait( - soft = false, # soft wait causes HSA_REFCOUNT overflow issues @roc groupsize = TBSize gridsize = TBSize * DotBlocks kernel( data.a, data.b, diff --git a/JuliaStream.jl/src/CUDAStream.jl b/JuliaStream.jl/src/CUDAStream.jl index dd4fc44..b46b3c9 100644 --- a/JuliaStream.jl/src/CUDAStream.jl +++ b/JuliaStream.jl/src/CUDAStream.jl @@ -21,7 +21,6 @@ function make_stream( error("arraysize ($(arraysize)) must be divisible by $(TBSize)!") end - # so CUDA's device is 0 indexed, so -1 from Julia CUDA.device!(device[1]) selected = CUDA.device() # show_reason is set to true here so it dumps CUDA info @@ -46,14 +45,14 @@ function make_stream( end function init_arrays!(data::CuData{T}, _, init::Tuple{T,T,T}) where {T} - CUDA.fill!(data.a, init[1]) - CUDA.fill!(data.b, init[2]) - CUDA.fill!(data.c, init[3]) + fill!(data.a, init[1]) + fill!(data.b, init[2]) + fill!(data.c, init[3]) end function copy!(data::CuData{T}, _) where {T} function kernel(a, c) - i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x @inbounds c[i] = a[i] return end @@ -63,7 +62,7 @@ end function mul!(data::CuData{T}, _) where {T} function kernel(b, c, scalar) - i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x @inbounds b[i] = scalar * c[i] return end @@ -73,7 +72,7 @@ end function add!(data::CuData{T}, _) where {T} function kernel(a, b, c) - i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x @inbounds c[i] = a[i] + b[i] return end @@ -83,7 +82,7 @@ end function triad!(data::CuData{T}, _) where {T} function kernel(a, b, c, scalar) - i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x @inbounds a[i] = b[i] + (scalar * c[i]) return end @@ -98,7 +97,7 @@ end function nstream!(data::CuData{T}, _) where {T} function kernel(a, b, c, scalar) - i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x @inbounds a[i] += b[i] + scalar * c[i] return end @@ -119,7 +118,7 @@ function dot(data::CuData{T}, _) where {T} @inbounds tb_sum[local_i] = 0.0 # do dot first - i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # only blockIdx starts at 1 + i = (blockIdx().x - 1) * blockDim().x + threadIdx().x while i <= size @inbounds tb_sum[local_i] += a[i] * b[i] i += blockDim().x * gridDim().x @@ -143,7 +142,6 @@ function dot(data::CuData{T}, _) where {T} end partial_sum = CuArray{T}(undef, DotBlocks) @cuda blocks = DotBlocks threads = TBSize kernel(data.a, data.b, data.size, partial_sum) - CUDA.synchronize() return sum(partial_sum) end From 4853457dca6e3c5ce94f83f3227cd69d49bacba4 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Fri, 27 Aug 2021 14:04:58 +0100 Subject: [PATCH 48/78] Add type annotation for all kernels Update dependencies --- JuliaStream.jl/AMDGPU/Manifest.toml | 8 ++++---- JuliaStream.jl/CUDA/Manifest.toml | 16 +++++++-------- .../KernelAbstractions/Manifest.toml | 20 +++++++++---------- JuliaStream.jl/Manifest.toml | 12 +++++------ JuliaStream.jl/oneAPI/Manifest.toml | 12 +++++------ JuliaStream.jl/src/AMDGPUStream.jl | 12 +++++------ JuliaStream.jl/src/CUDAStream.jl | 12 +++++------ .../src/KernelAbstractionsStream.jl | 12 +++++------ JuliaStream.jl/src/oneAPIStream.jl | 12 +++++------ 9 files changed, 58 insertions(+), 58 deletions(-) diff --git a/JuliaStream.jl/AMDGPU/Manifest.toml b/JuliaStream.jl/AMDGPU/Manifest.toml index 5d1a8a7..6525501 100644 --- a/JuliaStream.jl/AMDGPU/Manifest.toml +++ b/JuliaStream.jl/AMDGPU/Manifest.toml @@ -109,15 +109,15 @@ version = "1.3.0" [[LLVM]] deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Printf", "Unicode"] -git-tree-sha1 = "d6041ad706cf458b2c9f3e501152488a26451e9c" +git-tree-sha1 = "23a47d417a3cd9c2e73c854bac7dd4731c105ef7" uuid = "929cbde3-209d-540e-8aea-75f648917ca0" -version = "4.2.0" +version = "4.4.0" [[LLVMExtra_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "2d5a0044d6505f4771b5c82de87393f0c9741154" +git-tree-sha1 = "9c360e5ce980b88bb31a7b086dbb19469008154b" uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab" -version = "0.0.8+0" +version = "0.0.10+0" [[LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] diff --git a/JuliaStream.jl/CUDA/Manifest.toml b/JuliaStream.jl/CUDA/Manifest.toml index af0acfc..ef6da14 100644 --- a/JuliaStream.jl/CUDA/Manifest.toml +++ b/JuliaStream.jl/CUDA/Manifest.toml @@ -52,9 +52,9 @@ version = "1.3.0" [[Compat]] deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "79b9563ef3f2cc5fc6d3046a5ee1a57c9de52495" +git-tree-sha1 = "727e463cfebd0c7b999bbf3e9e7e16f254b94193" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "3.33.0" +version = "3.34.0" [[CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] @@ -116,15 +116,15 @@ version = "1.3.0" [[LLVM]] deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Printf", "Unicode"] -git-tree-sha1 = "d6041ad706cf458b2c9f3e501152488a26451e9c" +git-tree-sha1 = "23a47d417a3cd9c2e73c854bac7dd4731c105ef7" uuid = "929cbde3-209d-540e-8aea-75f648917ca0" -version = "4.2.0" +version = "4.4.0" [[LLVMExtra_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "2d5a0044d6505f4771b5c82de87393f0c9741154" +git-tree-sha1 = "9c360e5ce980b88bb31a7b086dbb19469008154b" uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab" -version = "0.0.8+0" +version = "0.0.10+0" [[LazyArtifacts]] deps = ["Artifacts", "Pkg"] @@ -231,9 +231,9 @@ uuid = "e6cf234a-135c-5ec9-84dd-332b85af5143" version = "1.5.3" [[Reexport]] -git-tree-sha1 = "5f6c21241f0f655da3952fd60aa18477cf96c220" +git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.1.0" +version = "1.2.2" [[Requires]] deps = ["UUIDs"] diff --git a/JuliaStream.jl/KernelAbstractions/Manifest.toml b/JuliaStream.jl/KernelAbstractions/Manifest.toml index 25fd8d1..bfc562f 100644 --- a/JuliaStream.jl/KernelAbstractions/Manifest.toml +++ b/JuliaStream.jl/KernelAbstractions/Manifest.toml @@ -69,9 +69,9 @@ uuid = "72cfdca4-0801-4ab0-bf6a-d52aa10adc57" version = "0.3.0" [[Cassette]] -git-tree-sha1 = "087e76b8d48c014112ba890892c33be42ad10504" +git-tree-sha1 = "b4b1d61ebbae2bc69a45e3a6b8439b4e411bc131" uuid = "7057c7e9-c182-5462-911a-8362d720325c" -version = "0.3.7" +version = "0.3.8" [[ChainRulesCore]] deps = ["Compat", "LinearAlgebra", "SparseArrays"] @@ -81,9 +81,9 @@ version = "1.3.0" [[Compat]] deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "79b9563ef3f2cc5fc6d3046a5ee1a57c9de52495" +git-tree-sha1 = "727e463cfebd0c7b999bbf3e9e7e16f254b94193" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "3.33.0" +version = "3.34.0" [[CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] @@ -179,15 +179,15 @@ version = "0.7.0" [[LLVM]] deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Printf", "Unicode"] -git-tree-sha1 = "d6041ad706cf458b2c9f3e501152488a26451e9c" +git-tree-sha1 = "23a47d417a3cd9c2e73c854bac7dd4731c105ef7" uuid = "929cbde3-209d-540e-8aea-75f648917ca0" -version = "4.2.0" +version = "4.4.0" [[LLVMExtra_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "2d5a0044d6505f4771b5c82de87393f0c9741154" +git-tree-sha1 = "9c360e5ce980b88bb31a7b086dbb19469008154b" uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab" -version = "0.0.8+0" +version = "0.0.10+0" [[LazyArtifacts]] deps = ["Artifacts", "Pkg"] @@ -354,9 +354,9 @@ uuid = "e6cf234a-135c-5ec9-84dd-332b85af5143" version = "1.5.3" [[Reexport]] -git-tree-sha1 = "5f6c21241f0f655da3952fd60aa18477cf96c220" +git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.1.0" +version = "1.2.2" [[Requires]] deps = ["UUIDs"] diff --git a/JuliaStream.jl/Manifest.toml b/JuliaStream.jl/Manifest.toml index 14f2029..c096e05 100644 --- a/JuliaStream.jl/Manifest.toml +++ b/JuliaStream.jl/Manifest.toml @@ -69,9 +69,9 @@ uuid = "72cfdca4-0801-4ab0-bf6a-d52aa10adc57" version = "0.3.0" [[Cassette]] -git-tree-sha1 = "087e76b8d48c014112ba890892c33be42ad10504" +git-tree-sha1 = "b4b1d61ebbae2bc69a45e3a6b8439b4e411bc131" uuid = "7057c7e9-c182-5462-911a-8362d720325c" -version = "0.3.7" +version = "0.3.8" [[ChainRulesCore]] deps = ["Compat", "LinearAlgebra", "SparseArrays"] @@ -81,9 +81,9 @@ version = "1.3.0" [[Compat]] deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "79b9563ef3f2cc5fc6d3046a5ee1a57c9de52495" +git-tree-sha1 = "727e463cfebd0c7b999bbf3e9e7e16f254b94193" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "3.33.0" +version = "3.34.0" [[CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] @@ -312,9 +312,9 @@ uuid = "e6cf234a-135c-5ec9-84dd-332b85af5143" version = "1.5.3" [[Reexport]] -git-tree-sha1 = "5f6c21241f0f655da3952fd60aa18477cf96c220" +git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.1.0" +version = "1.2.2" [[Requires]] deps = ["UUIDs"] diff --git a/JuliaStream.jl/oneAPI/Manifest.toml b/JuliaStream.jl/oneAPI/Manifest.toml index 3aab94b..82c40fd 100644 --- a/JuliaStream.jl/oneAPI/Manifest.toml +++ b/JuliaStream.jl/oneAPI/Manifest.toml @@ -34,9 +34,9 @@ version = "1.3.0" [[Compat]] deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "79b9563ef3f2cc5fc6d3046a5ee1a57c9de52495" +git-tree-sha1 = "727e463cfebd0c7b999bbf3e9e7e16f254b94193" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "3.33.0" +version = "3.34.0" [[CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] @@ -98,15 +98,15 @@ version = "1.3.0" [[LLVM]] deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Printf", "Unicode"] -git-tree-sha1 = "d6041ad706cf458b2c9f3e501152488a26451e9c" +git-tree-sha1 = "23a47d417a3cd9c2e73c854bac7dd4731c105ef7" uuid = "929cbde3-209d-540e-8aea-75f648917ca0" -version = "4.2.0" +version = "4.4.0" [[LLVMExtra_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "2d5a0044d6505f4771b5c82de87393f0c9741154" +git-tree-sha1 = "9c360e5ce980b88bb31a7b086dbb19469008154b" uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab" -version = "0.0.8+0" +version = "0.0.10+0" [[LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] diff --git a/JuliaStream.jl/src/AMDGPUStream.jl b/JuliaStream.jl/src/AMDGPUStream.jl index 8347637..4dd220c 100644 --- a/JuliaStream.jl/src/AMDGPUStream.jl +++ b/JuliaStream.jl/src/AMDGPUStream.jl @@ -55,7 +55,7 @@ function init_arrays!(data::ROCData{T}, _, init::Tuple{T,T,T}) where {T} end function copy!(data::ROCData{T}, _) where {T} - function kernel(a, c) + function kernel(a::AbstractArray{T}, c::AbstractArray{T}) i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x # only workgroupIdx starts at 1 @inbounds c[i] = a[i] return @@ -66,7 +66,7 @@ function copy!(data::ROCData{T}, _) where {T} end function mul!(data::ROCData{T}, _) where {T} - function kernel(b, c, scalar) + function kernel(b::AbstractArray{T}, c::AbstractArray{T}, scalar::T) i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x # only workgroupIdx starts at 1 @inbounds b[i] = scalar * c[i] return @@ -77,7 +77,7 @@ function mul!(data::ROCData{T}, _) where {T} end function add!(data::ROCData{T}, _) where {T} - function kernel(a, b, c) + function kernel(a::AbstractArray{T}, b::AbstractArray{T}, c::AbstractArray{T}) i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x # only workgroupIdx starts at 1 @inbounds c[i] = a[i] + b[i] return @@ -88,7 +88,7 @@ function add!(data::ROCData{T}, _) where {T} end function triad!(data::ROCData{T}, _) where {T} - function kernel(a, b, c, scalar) + function kernel(a::AbstractArray{T}, b::AbstractArray{T}, c::AbstractArray{T}, scalar::T) i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x # only workgroupIdx starts at 1 @inbounds a[i] = b[i] + (scalar * c[i]) return @@ -104,7 +104,7 @@ function triad!(data::ROCData{T}, _) where {T} end function nstream!(data::ROCData{T}, _) where {T} - function kernel(a, b, c, scalar) + function kernel(a::AbstractArray{T}, b::AbstractArray{T}, c::AbstractArray{T}, scalar::T) i = (workgroupIdx().x - 1) * workgroupDim().x + workitemIdx().x # only workgroupIdx starts at 1 @inbounds a[i] += b[i] + scalar * c[i] return @@ -120,7 +120,7 @@ function nstream!(data::ROCData{T}, _) where {T} end function dot(data::ROCData{T}, _) where {T} - function kernel(a, b, size, partial) + function kernel(a::AbstractArray{T}, b::AbstractArray{T}, size::Int, partial::AbstractArray{T}) tb_sum = ROCDeviceArray((TBSize,), alloc_local(:reduce, T, TBSize)) local_i = workitemIdx().x @inbounds tb_sum[local_i] = 0.0 diff --git a/JuliaStream.jl/src/CUDAStream.jl b/JuliaStream.jl/src/CUDAStream.jl index b46b3c9..da3698e 100644 --- a/JuliaStream.jl/src/CUDAStream.jl +++ b/JuliaStream.jl/src/CUDAStream.jl @@ -51,7 +51,7 @@ function init_arrays!(data::CuData{T}, _, init::Tuple{T,T,T}) where {T} end function copy!(data::CuData{T}, _) where {T} - function kernel(a, c) + function kernel(a::AbstractArray{T}, c::AbstractArray{T}) i = (blockIdx().x - 1) * blockDim().x + threadIdx().x @inbounds c[i] = a[i] return @@ -61,7 +61,7 @@ function copy!(data::CuData{T}, _) where {T} end function mul!(data::CuData{T}, _) where {T} - function kernel(b, c, scalar) + function kernel(b::AbstractArray{T}, c::AbstractArray{T}, scalar::T) i = (blockIdx().x - 1) * blockDim().x + threadIdx().x @inbounds b[i] = scalar * c[i] return @@ -71,7 +71,7 @@ function mul!(data::CuData{T}, _) where {T} end function add!(data::CuData{T}, _) where {T} - function kernel(a, b, c) + function kernel(a::AbstractArray{T}, b::AbstractArray{T}, c::AbstractArray{T}) i = (blockIdx().x - 1) * blockDim().x + threadIdx().x @inbounds c[i] = a[i] + b[i] return @@ -81,7 +81,7 @@ function add!(data::CuData{T}, _) where {T} end function triad!(data::CuData{T}, _) where {T} - function kernel(a, b, c, scalar) + function kernel(a::AbstractArray{T}, b::AbstractArray{T}, c::AbstractArray{T}, scalar::T) i = (blockIdx().x - 1) * blockDim().x + threadIdx().x @inbounds a[i] = b[i] + (scalar * c[i]) return @@ -96,7 +96,7 @@ function triad!(data::CuData{T}, _) where {T} end function nstream!(data::CuData{T}, _) where {T} - function kernel(a, b, c, scalar) + function kernel(a::AbstractArray{T}, b::AbstractArray{T}, c::AbstractArray{T}, scalar::T) i = (blockIdx().x - 1) * blockDim().x + threadIdx().x @inbounds a[i] += b[i] + scalar * c[i] return @@ -112,7 +112,7 @@ end function dot(data::CuData{T}, _) where {T} # direct port of the reduction in CUDAStream.cu - function kernel(a, b, size, partial) + function kernel(a::AbstractArray{T}, b::AbstractArray{T}, size::Int, partial::AbstractArray{T}) tb_sum = @cuStaticSharedMem(T, TBSize) local_i = threadIdx().x @inbounds tb_sum[local_i] = 0.0 diff --git a/JuliaStream.jl/src/KernelAbstractionsStream.jl b/JuliaStream.jl/src/KernelAbstractionsStream.jl index 8cc3699..2b9d9ad 100644 --- a/JuliaStream.jl/src/KernelAbstractionsStream.jl +++ b/JuliaStream.jl/src/KernelAbstractionsStream.jl @@ -136,7 +136,7 @@ function init_arrays!( end function copy!(data::StreamData{T,C}, context::Context) where {T,C} - @kernel function kernel(@Const(a), c) + @kernel function kernel(@Const(a::AbstractArray{T}), c) i = @index(Global) @inbounds c[i] = a[i] end @@ -144,7 +144,7 @@ function copy!(data::StreamData{T,C}, context::Context) where {T,C} end function mul!(data::StreamData{T,C}, context::Context) where {T,C} - @kernel function kernel(b, @Const(c), scalar) + @kernel function kernel(b::AbstractArray{T}, @Const(c::AbstractArray{T}), scalar::T) i = @index(Global) @inbounds b[i] = scalar * c[i] end @@ -152,7 +152,7 @@ function mul!(data::StreamData{T,C}, context::Context) where {T,C} end function add!(data::StreamData{T,C}, context::Context) where {T,C} - @kernel function kernel(@Const(a), @Const(b), c) + @kernel function kernel(@Const(a::AbstractArray{T}), @Const(b::AbstractArray{T}), c) i = @index(Global) @inbounds c[i] = a[i] + b[i] end @@ -160,7 +160,7 @@ function add!(data::StreamData{T,C}, context::Context) where {T,C} end function triad!(data::StreamData{T,C}, context::Context) where {T,C} - @kernel function kernel(a, @Const(b), @Const(c), scalar) + @kernel function kernel(a::AbstractArray{T}, @Const(b::AbstractArray{T}), @Const(c), scalar::T) i = @index(Global) @inbounds a[i] = b[i] + (scalar * c[i]) end @@ -176,7 +176,7 @@ function triad!(data::StreamData{T,C}, context::Context) where {T,C} end function nstream!(data::StreamData{T,C}, context::Context) where {T,C} - @kernel function kernel(a, @Const(b), @Const(c), scalar) + @kernel function kernel(a::AbstractArray{T}, @Const(b::AbstractArray{T}), @Const(c), scalar::T) i = @index(Global) @inbounds a[i] += b[i] + scalar * c[i] end @@ -192,7 +192,7 @@ function nstream!(data::StreamData{T,C}, context::Context) where {T,C} end function dot(data::StreamData{T,C}, context::Context) where {T,C} - @kernel function kernel(@Const(a), @Const(b), size, partial) + @kernel function kernel(@Const(a::AbstractArray{T}), @Const(b::AbstractArray{T}), size::Int, partial::AbstractArray{T}) local_i = @index(Local) group_i = @index(Group) tb_sum = @localmem T TBSize diff --git a/JuliaStream.jl/src/oneAPIStream.jl b/JuliaStream.jl/src/oneAPIStream.jl index 1bc319d..83f100e 100644 --- a/JuliaStream.jl/src/oneAPIStream.jl +++ b/JuliaStream.jl/src/oneAPIStream.jl @@ -53,7 +53,7 @@ function init_arrays!(data::oneData{T}, _, init::Tuple{T,T,T}) where {T} end function copy!(data::oneData{T}, groupsize::Int) where {T} - function kernel(a, c) + function kernel(a::AbstractArray{T}, c::AbstractArray{T}) i = get_global_id() @inbounds c[i] = a[i] return @@ -66,7 +66,7 @@ function copy!(data::oneData{T}, groupsize::Int) where {T} end function mul!(data::oneData{T}, groupsize::Int) where {T} - function kernel(b, c, scalar) + function kernel(b::AbstractArray{T}, c::AbstractArray{T}, scalar::T) i = get_global_id() @inbounds b[i] = scalar * c[i] return @@ -80,7 +80,7 @@ function mul!(data::oneData{T}, groupsize::Int) where {T} end function add!(data::oneData{T}, groupsize::Int) where {T} - function kernel(a, b, c) + function kernel(a::AbstractArray{T}, b::AbstractArray{T}, c::AbstractArray{T}) i = get_global_id() @inbounds c[i] = a[i] + b[i] return @@ -94,7 +94,7 @@ function add!(data::oneData{T}, groupsize::Int) where {T} end function triad!(data::oneData{T}, groupsize::Int) where {T} - function kernel(a, b, c, scalar) + function kernel(a::AbstractArray{T}, b::AbstractArray{T}, c::AbstractArray{T}, scalar::T) i = get_global_id() @inbounds a[i] = b[i] + (scalar * c[i]) return @@ -109,7 +109,7 @@ function triad!(data::oneData{T}, groupsize::Int) where {T} end function nstream!(data::oneData{T}, groupsize::Int) where {T} - function kernel(a, b, c, scalar) + function kernel(a::AbstractArray{T}, b::AbstractArray{T}, c::AbstractArray{T}, scalar::T) i = get_global_id() @inbounds a[i] += b[i] + scalar * c[i] return @@ -124,7 +124,7 @@ function nstream!(data::oneData{T}, groupsize::Int) where {T} end function dot(data::oneData{T}, groupsize::Int) where {T} - function kernel(a, b, size, partial) + function kernel(a::AbstractArray{T}, b::AbstractArray{T}, size::Int, partial::AbstractArray{T}) wg_sum = @LocalMemory(T, (DotWGSize,)) li = get_local_id() @inbounds wg_sum[li] = 0.0 From 13cb8ffced2033d86875aa4e56de91c7c5da9926 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Sat, 28 Aug 2021 11:10:49 +0100 Subject: [PATCH 49/78] Use custom static reduction for CPU --- JuliaStream.jl/src/ThreadedStream.jl | 40 +++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/JuliaStream.jl/src/ThreadedStream.jl b/JuliaStream.jl/src/ThreadedStream.jl index 4422e66..0faabeb 100644 --- a/JuliaStream.jl/src/ThreadedStream.jl +++ b/JuliaStream.jl/src/ThreadedStream.jl @@ -63,12 +63,46 @@ function nstream!(data::VectorData{T}, _) where {T} end end +# Threads.@threads/Threads.@spawn doesn't support OpenMP's firstprivate, etc +function static_par_ranged(f::Function, range::Int, n::Int) + stride = range ÷ n + rem = range % n + strides = map(0:n) do i + width = stride + (i < rem ? 1 : 0) + offset = i < rem ? (stride + 1) * i : ((stride + 1) * rem) + (stride * (i - rem)) + (offset, width) + end + ccall(:jl_enter_threaded_region, Cvoid, ()) + try + foreach(wait, map(1:n) do group + (offset, size) = strides[group] + task = Task(() -> f(group, offset+1, offset+size)) + task.sticky = true + ccall(:jl_set_task_tid, Cvoid, (Any, Cint), task, group-1) # ccall, so 0-based for group + schedule(task) + end) + finally + ccall(:jl_exit_threaded_region, Cvoid, ()) + end +end + function dot(data::VectorData{T}, _) where {T} - partial = zeros(T, Threads.nthreads()) - Threads.@threads for i = 1:data.size - @inbounds partial[Threads.threadid()] += data.a[i] * data.b[i] + partial = Vector{T}(undef, Threads.nthreads()) + static_par_ranged(data.size, Threads.nthreads()) do group, startidx, endidx + acc = zero(T) + @fastmath for i = startidx:endidx + @inbounds acc += data.a[i] * data.b[i] + end + @inbounds partial[group] = acc end return sum(partial) + # This doesn't do well on aarch64 because of the excessive Threads.threadid() ccall + # and inhibited vectorisation from the lack of @fastmath + # partial = zeros(T, Threads.nthreads()) + # Threads.@threads for i = 1:data.size + # @inbounds partial[Threads.threadid()] += (data.a[i] * data.b[i]) + # end + # return sum(partial) end function read_data(data::VectorData{T}, _)::VectorData{T} where {T} From 41f17673650e94d53f691ca9c6a848ffed70f346 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Sat, 28 Aug 2021 11:16:19 +0100 Subject: [PATCH 50/78] Pause GC during benchmark to reduce noise --- JuliaStream.jl/src/Stream.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/JuliaStream.jl/src/Stream.jl b/JuliaStream.jl/src/Stream.jl index b7fc940..1905c81 100644 --- a/JuliaStream.jl/src/Stream.jl +++ b/JuliaStream.jl/src/Stream.jl @@ -260,8 +260,9 @@ function main() init::Tuple{type,type,type} = DefaultInit scalar::type = DefaultScalar - (data, context) = make_stream(config.arraysize, scalar, device, config.csv) + GC.enable(false) + (data, context) = make_stream(config.arraysize, scalar, device, config.csv) init_arrays!(data, context, init) if benchmark == All (timings, sum) = run_all!(data, context, config.numtimes) @@ -289,6 +290,8 @@ function main() else error("Bad benchmark $(benchmark)") end + + GC.enable(true) if !valid exit(1) From 78b52a496c26bb2d7b89d5d3d24c22d98992c077 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Sat, 28 Aug 2021 11:39:08 +0100 Subject: [PATCH 51/78] Use @simd instead of @fastmath for CPU reduction --- JuliaStream.jl/src/ThreadedStream.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/JuliaStream.jl/src/ThreadedStream.jl b/JuliaStream.jl/src/ThreadedStream.jl index 0faabeb..f282fda 100644 --- a/JuliaStream.jl/src/ThreadedStream.jl +++ b/JuliaStream.jl/src/ThreadedStream.jl @@ -90,14 +90,14 @@ function dot(data::VectorData{T}, _) where {T} partial = Vector{T}(undef, Threads.nthreads()) static_par_ranged(data.size, Threads.nthreads()) do group, startidx, endidx acc = zero(T) - @fastmath for i = startidx:endidx + @simd for i = startidx:endidx @inbounds acc += data.a[i] * data.b[i] end @inbounds partial[group] = acc end return sum(partial) # This doesn't do well on aarch64 because of the excessive Threads.threadid() ccall - # and inhibited vectorisation from the lack of @fastmath + # and inhibited vectorisation from the lack of @simd # partial = zeros(T, Threads.nthreads()) # Threads.@threads for i = 1:data.size # @inbounds partial[Threads.threadid()] += (data.a[i] * data.b[i]) From a66696d97146fd378213eaa1c1ad62a7dc74a241 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 11 Nov 2021 23:11:04 +0000 Subject: [PATCH 52/78] Initial Thrust implementation --- CMakeLists.txt | 1 + THRUST.cmake | 87 +++++++++++++++++ ThrustStream.cu | 235 +++++++++++++++++++++++++++++++++++++++++++++ ThrustStream.h | 43 +++++++++ ci-test-compile.sh | 7 ++ main.cpp | 6 ++ 6 files changed, 379 insertions(+) create mode 100644 THRUST.cmake create mode 100644 ThrustStream.cu create mode 100644 ThrustStream.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 797a9c0..42abfd8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,6 +113,7 @@ register_model(ACC ACC ACCStream.cpp) # defining RAJA collides with the RAJA namespace so USE_RAJA register_model(RAJA USE_RAJA RAJAStream.cpp) register_model(TBB TBB TBBStream.cpp) +register_model(THRUST THRUST ThrustStream.cu) # Thrust uses cu, even for rocThrust set(USAGE ON CACHE BOOL "Whether to print all custom flags for the selected model") diff --git a/THRUST.cmake b/THRUST.cmake new file mode 100644 index 0000000..1b94bf5 --- /dev/null +++ b/THRUST.cmake @@ -0,0 +1,87 @@ + +register_flag_optional(THRUST_IMPL + "Which Thrust implementation to use, supported options include: + - CUDA (via https://github.com/NVIDIA/thrust) + - ROCM (VIA https://github.com/ROCmSoftwarePlatform/rocThrust) + " + "CUDA") + +register_flag_optional(SDK_DIR + "Path to the selected Thrust implementation (e.g `/opt/nvidia/hpc_sdk/Linux_x86_64/21.9/cuda/include/thrust/` for NVHPC, `/opt/rocm` for ROCm)" + "") + +register_flag_optional(BACKEND + "[THRUST_IMPL==CUDA] CUDA's Thrust implementation supports the following backends: + - CUDA + - OMP + - TBB + " + "CUDA") + +register_flag_optional(CMAKE_CUDA_COMPILER + "[THRUST_IMPL==CUDA] Path to the CUDA nvcc compiler" + "") + +# XXX we may want to drop this eventually and use CMAKE_CUDA_ARCHITECTURES directly +register_flag_optional(CUDA_ARCH + "[THRUST_IMPL==CUDA] Nvidia architecture, will be passed in via `-arch=` (e.g `sm_70`) for nvcc" + "") + +register_flag_optional(CUDA_EXTRA_FLAGS + "[THRUST_IMPL==CUDA] Additional CUDA flags passed to nvcc, this is appended after `CUDA_ARCH`" + "") + + +macro(setup) + set(CMAKE_CXX_STANDARD 14) + + if (${THRUST_IMPL} STREQUAL "CUDA") + + # see CUDA.cmake, we're only adding a few Thrust related libraries here + + if (POLICY CMP0104) + cmake_policy(SET CMP0104 OLD) + endif () + + # add -forward-unknown-to-host-compiler for compatibility reasons + set(CMAKE_CUDA_FLAGS ${CMAKE_CUDA_FLAGS} "--expt-extended-lambda -forward-unknown-to-host-compiler -arch=${CUDA_ARCH}" ${CUDA_EXTRA_FLAGS}) + enable_language(CUDA) + # CMake defaults to -O2 for CUDA at Release, let's wipe that and use the global RELEASE_FLAG + # appended later + wipe_gcc_style_optimisation_flags(CMAKE_CUDA_FLAGS_${BUILD_TYPE}) + + message(STATUS "NVCC flags: ${CMAKE_CUDA_FLAGS} ${CMAKE_CUDA_FLAGS_${BUILD_TYPE}}") + + + if (SDK_DIR) + find_package(CUB REQUIRED CONFIG PATHS ${SDK_DIR}/cub) + find_package(Thrust REQUIRED CONFIG PATHS ${SDK_DIR}/thrust) + else () + find_package(CUB REQUIRED CONFIG) + find_package(Thrust REQUIRED CONFIG) + endif () + + message(STATUS "Using Thrust backend: ${BACKEND}") + + # this creates the interface that we can link to + thrust_create_target(Thrust HOST CPP DEVICE ${BACKEND}) + + register_link_library(Thrust) + elseif (${THRUST_IMPL} STREQUAL "ROCM") + if (SDK_DIR) + find_package(rocprim REQUIRED CONFIG PATHS ${SDK_DIR}/rocprim) + find_package(rocthrust REQUIRED CONFIG PATHS ${SDK_DIR}/rocthrust) + else () + find_package(rocprim REQUIRED CONFIG) + find_package(rocthrust REQUIRED CONFIG) + endif () + register_link_library(roc::rocthrust) + else () + message(FATAL_ERROR "Unsupported THRUST_IMPL provided: ${THRUST_IMPL}") + endif () + + +endmacro() + + + \ No newline at end of file diff --git a/ThrustStream.cu b/ThrustStream.cu new file mode 100644 index 0000000..3a57ab0 --- /dev/null +++ b/ThrustStream.cu @@ -0,0 +1,235 @@ +// Copyright (c) 2020 Tom Deakin +// University of Bristol HPC +// +// For full license terms please see the LICENSE file distributed with this +// source code + +#include "ThrustStream.h" +#include +#include +#include +#include + +static inline void synchronise() +{ +// rocThrust doesn't synchronise between thrust calls +#if defined(THRUST_DEVICE_SYSTEM_HIP) && THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_HIP + hipDeviceSynchronize(); +#endif +} + +template +ThrustStream::ThrustStream(const int ARRAY_SIZE, int device) + : array_size{ARRAY_SIZE}, a(array_size), b(array_size), c(array_size) { + std::cout << "Using CUDA device: " << getDeviceName(device) << std::endl; + std::cout << "Driver: " << getDeviceDriver(device) << std::endl; + std::cout << "Thrust version: " << THRUST_VERSION << std::endl; + +#if THRUST_DEVICE_SYSTEM == 0 + // as per Thrust docs, 0 is reserved for undefined backend + std::cout << "Thrust backend: undefined" << std::endl; +#elif THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA + std::cout << "Thrust backend: CUDA" << std::endl; +#elif THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_OMP + std::cout << "Thrust backend: OMP" << std::endl; +#elif THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_TBB + std::cout << "Thrust backend: TBB" << std::endl; +#elif THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CPP + std::cout << "Thrust backend: CPP" << std::endl; +#elif THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_TBB + std::cout << "Thrust backend: TBB" << std::endl; +#else + +#if defined(THRUST_DEVICE_SYSTEM_HIP) && THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_HIP + std::cout << "Thrust backend: HIP" << std::endl; +#else + std::cout << "Thrust backend: " << THRUST_DEVICE_SYSTEM << "(unknown)" << std::endl; +#endif + +#endif + +} + +template +void ThrustStream::init_arrays(T initA, T initB, T initC) +{ + thrust::fill(a.begin(), a.end(), initA); + thrust::fill(b.begin(), b.end(), initB); + thrust::fill(c.begin(), c.end(), initC); + synchronise(); +} + +template +void ThrustStream::read_arrays(std::vector& h_a, std::vector& h_b, std::vector& h_c) +{ + thrust::copy(a.begin(), a.end(), h_a.begin()); + thrust::copy(b.begin(), b.end(), h_b.begin()); + thrust::copy(c.begin(), c.end(), h_c.begin()); +} + +template +void ThrustStream::copy() +{ + thrust::copy(a.begin(), a.end(),c.begin()); + synchronise(); +} + +template +void ThrustStream::mul() +{ + const T scalar = startScalar; + thrust::transform( + c.begin(), + c.end(), + b.begin(), + [=] __device__ __host__ (const T &ci){ + return ci * scalar; + } + ); + synchronise(); +} + +template +void ThrustStream::add() +{ + thrust::transform( + thrust::make_zip_iterator(thrust::make_tuple(a.begin(), b.begin())), + thrust::make_zip_iterator(thrust::make_tuple(a.end(), b.end())), + c.begin(), + thrust::make_zip_function( + [] __device__ __host__ (const T& ai, const T& bi){ + return ai + bi; + }) + ); + synchronise(); +} + +template +void ThrustStream::triad() +{ + const T scalar = startScalar; + thrust::transform( + thrust::make_zip_iterator(thrust::make_tuple(b.begin(), c.begin())), + thrust::make_zip_iterator(thrust::make_tuple(b.end(), c.end())), + a.begin(), + thrust::make_zip_function( + [=] __device__ __host__ (const T& bi, const T& ci){ + return bi + scalar * ci; + }) + ); + synchronise(); +} + +template +void ThrustStream::nstream() +{ + const T scalar = startScalar; + thrust::transform( + thrust::make_zip_iterator(thrust::make_tuple(a.begin(), b.begin(), c.begin())), + thrust::make_zip_iterator(thrust::make_tuple(a.end(), b.end(), c.end())), + a.begin(), + thrust::make_zip_function( + [=] __device__ __host__ (const T& ai, const T& bi, const T& ci){ + return ai + bi + scalar * ci; + }) + ); + synchronise(); +} + +template +T ThrustStream::dot() +{ + return thrust::inner_product(a.begin(), a.end(), b.begin(), T{}); +} + +#if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA || \ + (defined(THRUST_DEVICE_SYSTEM_HIP) && THRUST_DEVICE_SYSTEM_HIP == THRUST_DEVICE_SYSTEM) + +#ifdef __NVCC__ +#define IMPL_FN__(fn) cuda ## fn +#define IMPL_TYPE__(tpe) cuda ## tpe +#elif defined(__HIP_PLATFORM_HCC__) +#define IMPL_FN__(fn) hip ## fn +#define IMPL_TYPE__(tpe) hip ## tpe ## _t +#else +# error Unsupported compiler for Thrust +#endif + +void check_error(void) +{ + IMPL_FN__(Error_t) err = IMPL_FN__(GetLastError()); + if (err != IMPL_FN__(Success)) + { + std::cerr << "Error: " << IMPL_FN__(GetErrorString(err)) << std::endl; + exit(err); + } +} + +void listDevices(void) +{ + // Get number of devices + int count; + IMPL_FN__(GetDeviceCount(&count)); + check_error(); + + // Print device names + if (count == 0) + { + std::cerr << "No devices found." << std::endl; + } + else + { + std::cout << std::endl; + std::cout << "Devices:" << std::endl; + for (int i = 0; i < count; i++) + { + std::cout << i << ": " << getDeviceName(i) << std::endl; + } + std::cout << std::endl; + } +} + +std::string getDeviceName(const int device) +{ + IMPL_TYPE__(DeviceProp) props = {}; + IMPL_FN__(GetDeviceProperties(&props, device)); + check_error(); + return std::string(props.name); +} + + +std::string getDeviceDriver(const int device) +{ + IMPL_FN__(SetDevice(device)); + check_error(); + int driver; + IMPL_FN__(DriverGetVersion(&driver)); + check_error(); + return std::to_string(driver); +} + +#undef IMPL_FN__ +#undef IMPL_TPE__ + +#else + +void listDevices(void) +{ + std::cout << "0: CPU" << std::endl; +} + +std::string getDeviceName(const int) +{ + return std::string("(device name unavailable)"); +} + +std::string getDeviceDriver(const int) +{ + return std::string("(device driver unavailable)"); +} + +#endif + +template class ThrustStream; +template class ThrustStream; + diff --git a/ThrustStream.h b/ThrustStream.h new file mode 100644 index 0000000..f87ace7 --- /dev/null +++ b/ThrustStream.h @@ -0,0 +1,43 @@ +// Copyright (c) 2020 Tom Deakin +// University of Bristol HPC +// +// For full license terms please see the LICENSE file distributed with this +// source code + +#pragma once + +#include +#include +#include + +#include "Stream.h" + +#define IMPLEMENTATION_STRING "Thrust" + +template +class ThrustStream : public Stream +{ + protected: + // Size of arrays + int array_size; + + thrust::device_vector a; + thrust::device_vector b; + thrust::device_vector c; + + public: + ThrustStream(const int, int); + ~ThrustStream() = default; + + virtual void copy() override; + virtual void add() override; + virtual void mul() override; + virtual void triad() override; + virtual void nstream() override; + virtual T dot() override; + + virtual void init_arrays(T initA, T initB, T initC) override; + virtual void read_arrays(std::vector& a, std::vector& b, std::vector& c) override; + +}; + diff --git a/ci-test-compile.sh b/ci-test-compile.sh index 00ca718..0a162f2 100755 --- a/ci-test-compile.sh +++ b/ci-test-compile.sh @@ -171,6 +171,11 @@ build_gcc() { # -DCUDA_TOOLKIT_ROOT_DIR=${NVHPC_CUDA_DIR:?} \ # -DCUDA_ARCH=$NV_ARCH" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVSDK/cuda/include/thrust -DTHRUST_IMPL=CUDA -DBACKEND=CUDA" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVSDK/cuda/include/thrust -DTHRUST_IMPL=CUDA -DBACKEND=OMP" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVSDK/cuda/include/thrust -DTHRUST_IMPL=CUDA -DBACKEND=TBB" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVSDK/cuda/include/thrust -DTHRUST_IMPL=CUDA -DBACKEND=CPP" + } build_clang() { @@ -220,6 +225,8 @@ build_aomp() { build_hip() { run_build hip_build "${HIP_CXX:?}" HIP "-DCMAKE_CXX_COMPILER=${HIP_CXX:?}" + + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CXX_COMPILER=${HIP_CXX:?} -DSDK_DIR=$ROCM_PATH -DTHRUST_IMPL=ROCM" } build_icpx() { diff --git a/main.cpp b/main.cpp index de301ce..2791bdc 100644 --- a/main.cpp +++ b/main.cpp @@ -27,6 +27,8 @@ #include "STD20Stream.hpp" #elif defined(TBB) #include "TBBStream.hpp" +#elif defined(THRUST) +#include "ThrustStream.h" #elif defined(HIP) #include "HIPStream.h" #elif defined(HC) @@ -272,6 +274,10 @@ void run() // Use the C++20 implementation stream = new TBBStream(ARRAY_SIZE, deviceIndex); +#elif defined(THRUST) + // Use the Thrust implementation + stream = new ThrustStream(ARRAY_SIZE, deviceIndex); + #elif defined(ACC) // Use the OpenACC implementation stream = new ACCStream(ARRAY_SIZE, deviceIndex); From c2f75b90b3e2f02076d1e462f7a05aa72e433cd6 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 11 Nov 2021 23:30:04 +0000 Subject: [PATCH 53/78] Fix CI NVHPC path Fix CI ROCm install sources --- ci-prepare-bionic.sh | 7 +++++-- ci-test-compile.sh | 18 +++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/ci-prepare-bionic.sh b/ci-prepare-bionic.sh index fa3b2d2..3a3abac 100755 --- a/ci-prepare-bionic.sh +++ b/ci-prepare-bionic.sh @@ -143,9 +143,12 @@ setup_nvhpc() { local bin_dir="$sdk_dir/compilers/bin" "$bin_dir/makelocalrc" "$bin_dir" -x + export_var NVHPC_SDK_DIR "$sdk_dir" + export_var NVHPC_CUDA_DIR "$sdk_dir/cuda/11.3" + export_var NVHPC_NVCXX "$bin_dir/nvc++" export_var NVHPC_NVCC "$sdk_dir/cuda/11.3/bin/nvcc" - export_var NVHPC_CUDA_DIR "$sdk_dir/cuda/11.3" + echo "Installed CUDA versions:" ls "$sdk_dir/cuda" verify_bin_exists "$NVHPC_NVCXX" @@ -249,7 +252,7 @@ setup_clang_gcc() { setup_rocm() { wget -q -O - "https://repo.radeon.com/rocm/rocm.gpg.key" | sudo apt-key add - - echo 'deb [arch=amd64] https://repo.radeon.com/rocm/apt/debian/ xenial main' | sudo tee /etc/apt/sources.list.d/rocm.list + echo 'deb [arch=amd64] https://repo.radeon.com/rocm/apt/4.5 ubuntu main' | sudo tee /etc/apt/sources.list.d/rocm.list sudo apt-get update -qq sudo apt-get install -y -qq rocm-dev export_var ROCM_PATH "/opt/rocm" diff --git a/ci-test-compile.sh b/ci-test-compile.sh index 0a162f2..e56931c 100755 --- a/ci-test-compile.sh +++ b/ci-test-compile.sh @@ -92,11 +92,11 @@ run_build() { # GCC_CXX="/usr/bin/g++" # CLANG_CXX="/usr/bin/clang++" -# NVSDK="/home/tom/Downloads/nvhpc_2021_212_Linux_x86_64_cuda_11.2/install_components/Linux_x86_64/21.2/" -# NVHPC_NVCXX="$NVSDK/compilers/bin/nvc++" -# NVHPC_NVCC="$NVSDK/cuda/11.2/bin/nvcc" -# NVHPC_CUDA_DIR="$NVSDK/cuda/11.2" -# "$NVSDK/compilers/bin/makelocalrc" "$NVSDK/compilers/bin/" -x +# NVHPC_SDK_DIR="/home/tom/Downloads/nvhpc_2021_212_Linux_x86_64_cuda_11.2/install_components/Linux_x86_64/21.2/" +# NVHPC_NVCXX="$NVHPC_SDK_DIR/compilers/bin/nvc++" +# NVHPC_NVCC="$NVHPC_SDK_DIR/cuda/11.2/bin/nvcc" +# NVHPC_CUDA_DIR="$NVHPC_SDK_DIR/cuda/11.2" +# "$NVHPC_SDK_DIR/compilers/bin/makelocalrc" "$NVHPC_SDK_DIR/compilers/bin/" -x # AOCC_CXX="/opt/AMD/aocc-compiler-2.3.0/bin/clang++" # AOMP_CXX="/usr/lib/aomp/bin/clang++" @@ -171,10 +171,10 @@ build_gcc() { # -DCUDA_TOOLKIT_ROOT_DIR=${NVHPC_CUDA_DIR:?} \ # -DCUDA_ARCH=$NV_ARCH" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVSDK/cuda/include/thrust -DTHRUST_IMPL=CUDA -DBACKEND=CUDA" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVSDK/cuda/include/thrust -DTHRUST_IMPL=CUDA -DBACKEND=OMP" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVSDK/cuda/include/thrust -DTHRUST_IMPL=CUDA -DBACKEND=TBB" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVSDK/cuda/include/thrust -DTHRUST_IMPL=CUDA -DBACKEND=CPP" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_SDK_DIR/cuda/include/thrust -DTHRUST_IMPL=CUDA -DBACKEND=CUDA" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_SDK_DIR/cuda/include/thrust -DTHRUST_IMPL=CUDA -DBACKEND=OMP" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_SDK_DIR/cuda/include/thrust -DTHRUST_IMPL=CUDA -DBACKEND=TBB" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_SDK_DIR/cuda/include/thrust -DTHRUST_IMPL=CUDA -DBACKEND=CPP" } From a463e88895e7725c002dad15cab1781cd76c0bd3 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 11 Nov 2021 23:50:27 +0000 Subject: [PATCH 54/78] Fix CI rocThrust build variables Fix CI CUDA cmake module include path Bump CI NVHPC version --- THRUST.cmake | 4 ++-- ci-prepare-bionic.sh | 10 +++++----- ci-test-compile.sh | 14 ++++++++------ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/THRUST.cmake b/THRUST.cmake index 1b94bf5..8aaef26 100644 --- a/THRUST.cmake +++ b/THRUST.cmake @@ -2,12 +2,12 @@ register_flag_optional(THRUST_IMPL "Which Thrust implementation to use, supported options include: - CUDA (via https://github.com/NVIDIA/thrust) - - ROCM (VIA https://github.com/ROCmSoftwarePlatform/rocThrust) + - ROCM (via https://github.com/ROCmSoftwarePlatform/rocThrust) " "CUDA") register_flag_optional(SDK_DIR - "Path to the selected Thrust implementation (e.g `/opt/nvidia/hpc_sdk/Linux_x86_64/21.9/cuda/include/thrust/` for NVHPC, `/opt/rocm` for ROCm)" + "Path to the selected Thrust implementation (e.g `/opt/nvidia/hpc_sdk/Linux_x86_64/21.9/cuda/include` for NVHPC, `/opt/rocm` for ROCm)" "") register_flag_optional(BACKEND diff --git a/ci-prepare-bionic.sh b/ci-prepare-bionic.sh index 3a3abac..462d321 100755 --- a/ci-prepare-bionic.sh +++ b/ci-prepare-bionic.sh @@ -135,19 +135,19 @@ setup_aocc() { setup_nvhpc() { echo "Preparing Nvidia HPC SDK" local tarball="nvhpc.tar.gz" -# local url="http://localhost:8000/nvhpc_2021_215_Linux_x86_64_cuda_11.3.tar.gz" - local url="https://developer.download.nvidia.com/hpc-sdk/21.5/nvhpc_2021_215_Linux_x86_64_cuda_11.3.tar.gz" +# local url="http://localhost:8000/nvhpc_2021_219_Linux_x86_64_cuda_11.4.tar.gz" + local url="https://developer.download.nvidia.com/hpc-sdk/21.9/nvhpc_2021_219_Linux_x86_64_cuda_11.4.tar.gz" get_and_untar "$tarball" "$url" - local sdk_dir="$PWD/nvhpc_2021_215_Linux_x86_64_cuda_11.3/install_components/Linux_x86_64/21.5" + local sdk_dir="$PWD/nvhpc_2021_219_Linux_x86_64_cuda_11.4/install_components/Linux_x86_64/21.9" local bin_dir="$sdk_dir/compilers/bin" "$bin_dir/makelocalrc" "$bin_dir" -x export_var NVHPC_SDK_DIR "$sdk_dir" - export_var NVHPC_CUDA_DIR "$sdk_dir/cuda/11.3" + export_var NVHPC_CUDA_DIR "$sdk_dir/cuda/11.4" export_var NVHPC_NVCXX "$bin_dir/nvc++" - export_var NVHPC_NVCC "$sdk_dir/cuda/11.3/bin/nvcc" + export_var NVHPC_NVCC "$sdk_dir/cuda/11.4/bin/nvcc" echo "Installed CUDA versions:" ls "$sdk_dir/cuda" diff --git a/ci-test-compile.sh b/ci-test-compile.sh index e56931c..6544731 100755 --- a/ci-test-compile.sh +++ b/ci-test-compile.sh @@ -171,10 +171,10 @@ build_gcc() { # -DCUDA_TOOLKIT_ROOT_DIR=${NVHPC_CUDA_DIR:?} \ # -DCUDA_ARCH=$NV_ARCH" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_SDK_DIR/cuda/include/thrust -DTHRUST_IMPL=CUDA -DBACKEND=CUDA" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_SDK_DIR/cuda/include/thrust -DTHRUST_IMPL=CUDA -DBACKEND=OMP" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_SDK_DIR/cuda/include/thrust -DTHRUST_IMPL=CUDA -DBACKEND=TBB" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_SDK_DIR/cuda/include/thrust -DTHRUST_IMPL=CUDA -DBACKEND=CPP" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_SDK_DIR/cuda/include -DTHRUST_IMPL=CUDA -DBACKEND=CUDA" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_SDK_DIR/cuda/include -DTHRUST_IMPL=CUDA -DBACKEND=OMP" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_SDK_DIR/cuda/include -DTHRUST_IMPL=CUDA -DBACKEND=TBB" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_SDK_DIR/cuda/include -DTHRUST_IMPL=CUDA -DBACKEND=CPP" } @@ -224,9 +224,11 @@ build_aomp() { } build_hip() { - run_build hip_build "${HIP_CXX:?}" HIP "-DCMAKE_CXX_COMPILER=${HIP_CXX:?}" + local name="hip_build" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CXX_COMPILER=${HIP_CXX:?} -DSDK_DIR=$ROCM_PATH -DTHRUST_IMPL=ROCM" + run_build $name "${HIP_CXX:?}" HIP "-DCMAKE_CXX_COMPILER=${HIP_CXX:?}" + + run_build $name "${GCC_CXX:?}" THRUST "-DCMAKE_CXX_COMPILER=${HIP_CXX:?} -DSDK_DIR=$ROCM_PATH -DTHRUST_IMPL=ROCM" } build_icpx() { From 0d55a7261b54bf87311a0aac50f3702366ed3e33 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Fri, 12 Nov 2021 00:14:07 +0000 Subject: [PATCH 55/78] Fix CI not installing rocThrust Fix CI CUDA flag version --- ci-prepare-bionic.sh | 2 +- ci-test-compile.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci-prepare-bionic.sh b/ci-prepare-bionic.sh index 462d321..6aee569 100755 --- a/ci-prepare-bionic.sh +++ b/ci-prepare-bionic.sh @@ -254,7 +254,7 @@ setup_rocm() { wget -q -O - "https://repo.radeon.com/rocm/rocm.gpg.key" | sudo apt-key add - echo 'deb [arch=amd64] https://repo.radeon.com/rocm/apt/4.5 ubuntu main' | sudo tee /etc/apt/sources.list.d/rocm.list sudo apt-get update -qq - sudo apt-get install -y -qq rocm-dev + sudo apt-get install -y -qq rocm-dev rocthrust-dev export_var ROCM_PATH "/opt/rocm" export_var HIP_CXX "$ROCM_PATH/bin/hipcc" verify_bin_exists "$HIP_CXX" diff --git a/ci-test-compile.sh b/ci-test-compile.sh index 6544731..f72a76b 100755 --- a/ci-test-compile.sh +++ b/ci-test-compile.sh @@ -124,7 +124,7 @@ run_build() { AMD_ARCH="gfx_903" NV_ARCH="sm_70" -NV_ARCH_CCXY="cuda11.3,cc80" +NV_ARCH_CCXY="cuda11.4,cc80" build_gcc() { local name="gcc_build" From fe4007b4460953b4bafdfb825e1a24e9807c756b Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Fri, 12 Nov 2021 02:26:31 +0000 Subject: [PATCH 56/78] Fix CI ROCm quirks Fix CI CUDA path --- ci-prepare-bionic.sh | 1 + ci-test-compile.sh | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ci-prepare-bionic.sh b/ci-prepare-bionic.sh index 6aee569..7294905 100755 --- a/ci-prepare-bionic.sh +++ b/ci-prepare-bionic.sh @@ -256,6 +256,7 @@ setup_rocm() { sudo apt-get update -qq sudo apt-get install -y -qq rocm-dev rocthrust-dev export_var ROCM_PATH "/opt/rocm" + export_var PATH "$ROCM_PATH/bin:$PATH" # ROCm needs this for many of their libraries' CMake build to work export_var HIP_CXX "$ROCM_PATH/bin/hipcc" verify_bin_exists "$HIP_CXX" "$HIP_CXX" --version diff --git a/ci-test-compile.sh b/ci-test-compile.sh index f72a76b..e0bec2f 100755 --- a/ci-test-compile.sh +++ b/ci-test-compile.sh @@ -171,10 +171,10 @@ build_gcc() { # -DCUDA_TOOLKIT_ROOT_DIR=${NVHPC_CUDA_DIR:?} \ # -DCUDA_ARCH=$NV_ARCH" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_SDK_DIR/cuda/include -DTHRUST_IMPL=CUDA -DBACKEND=CUDA" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_SDK_DIR/cuda/include -DTHRUST_IMPL=CUDA -DBACKEND=OMP" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_SDK_DIR/cuda/include -DTHRUST_IMPL=CUDA -DBACKEND=TBB" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_SDK_DIR/cuda/include -DTHRUST_IMPL=CUDA -DBACKEND=CPP" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_CUDA_DIR/include -DTHRUST_IMPL=CUDA -DBACKEND=CUDA" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_CUDA_DIR/include -DTHRUST_IMPL=CUDA -DBACKEND=OMP" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_CUDA_DIR/include -DTHRUST_IMPL=CUDA -DBACKEND=TBB" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_CUDA_DIR/include -DTHRUST_IMPL=CUDA -DBACKEND=CPP" } From dc42388df311c4aff08e97834a607143f95757b2 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Fri, 12 Nov 2021 03:25:18 +0000 Subject: [PATCH 57/78] Fix CXX recognition issues for rocThrust Fix CI check for min CMake version on CUDA Thrust Temporarily disable CUDA Thrust w/ TBB for now --- THRUST.cmake | 4 ++++ ci-test-compile.sh | 29 +++++++++++++++++++++++------ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/THRUST.cmake b/THRUST.cmake index 8aaef26..0c286c2 100644 --- a/THRUST.cmake +++ b/THRUST.cmake @@ -75,6 +75,10 @@ macro(setup) find_package(rocprim REQUIRED CONFIG) find_package(rocthrust REQUIRED CONFIG) endif () + + # for HIP we treat *.cu files as CXX otherwise CMake doesn't compile them + set_source_files_properties(${IMPL_SOURCES} PROPERTIES LANGUAGE CXX) + register_link_library(roc::rocthrust) else () message(FATAL_ERROR "Unsupported THRUST_IMPL provided: ${THRUST_IMPL}") diff --git a/ci-test-compile.sh b/ci-test-compile.sh index e0bec2f..a61834c 100755 --- a/ci-test-compile.sh +++ b/ci-test-compile.sh @@ -57,7 +57,7 @@ run_build() { local cmake_code=$? "$CMAKE_BIN" --build "$build" -j "$(nproc)" &>>"$log" - "$CMAKE_BIN" --build "$build" --target install -j "$(nproc)" &>>"$log" + "$CMAKE_BIN" --build "$build" --target install -j "$(nproc)" &>>"$log" local cmake_code=$? set -e @@ -171,10 +171,27 @@ build_gcc() { # -DCUDA_TOOLKIT_ROOT_DIR=${NVHPC_CUDA_DIR:?} \ # -DCUDA_ARCH=$NV_ARCH" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_CUDA_DIR/include -DTHRUST_IMPL=CUDA -DBACKEND=CUDA" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_CUDA_DIR/include -DTHRUST_IMPL=CUDA -DBACKEND=OMP" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_CUDA_DIR/include -DTHRUST_IMPL=CUDA -DBACKEND=TBB" - run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_CUDA_DIR/include -DTHRUST_IMPL=CUDA -DBACKEND=CPP" + + # CMake >= 3.15 only due to Nvidia's Thrust CMake requirements + local current=$("$CMAKE_BIN" --version | head -n 1 | cut -d ' ' -f3) + local required="3.15.0" + if [ "$(printf '%s\n' "$required" "$current" | sort -V | head -n1)" = "$required" ]; then + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_CUDA_DIR/include -DTHRUST_IMPL=CUDA -DBACKEND=CUDA" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_CUDA_DIR/include -DTHRUST_IMPL=CUDA -DBACKEND=OMP" + run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_CUDA_DIR/include -DTHRUST_IMPL=CUDA -DBACKEND=CPP" + + # FIXME CUDA Thrust + TBB throws the following error: + # /usr/lib/gcc/x86_64-linux-gnu/9/include/avx512fintrin.h(9146): error: identifier "__builtin_ia32_rndscaless_round" is undefined + # /usr/lib/gcc/x86_64-linux-gnu/9/include/avx512fintrin.h(9155): error: identifier "__builtin_ia32_rndscalesd_round" is undefined + # /usr/lib/gcc/x86_64-linux-gnu/9/include/avx512fintrin.h(14797): error: identifier "__builtin_ia32_rndscaless_round" is undefined + # /usr/lib/gcc/x86_64-linux-gnu/9/include/avx512fintrin.h(14806): error: identifier "__builtin_ia32_rndscalesd_round" is undefined + # /usr/lib/gcc/x86_64-linux-gnu/9/include/avx512dqintrin.h(1365): error: identifier "__builtin_ia32_fpclassss" is undefined + # /usr/lib/gcc/x86_64-linux-gnu/9/include/avx512dqintrin.h(1372): error: identifier "__builtin_ia32_fpclasssd" is undefined + + # run_build $name "${GCC_CXX:?}" THRUST "$cxx -DCMAKE_CUDA_COMPILER=${NVHPC_NVCC:?} -DCUDA_ARCH=$NV_ARCH -DSDK_DIR=$NVHPC_CUDA_DIR/include -DTHRUST_IMPL=CUDA -DBACKEND=TBB" + else + echo "CMake version ${current} < ${required}, skipping Thrust models" + fi } @@ -198,7 +215,7 @@ build_clang() { run_build $name "${CLANG_CXX:?}" OCL "$cxx -DOpenCL_LIBRARY=${OCL_LIB:?}" run_build $name "${CLANG_CXX:?}" STD "$cxx -DCXX_EXTRA_LIBRARIES=${CLANG_STD_PAR_LIB:-}" # run_build $name "${LANG_CXX:?}" STD20 "$cxx -DCXX_EXTRA_LIBRARIES=${CLANG_STD_PAR_LIB:-}" # not yet supported - + run_build $name "${CLANG_CXX:?}" TBB "$cxx -DONE_TBB_DIR=$TBB_LIB" run_build $name "${CLANG_CXX:?}" TBB "$cxx" # build TBB again with the system TBB From f5fe55c204bde0888d78a77d10e01a0060af1ec3 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Tue, 30 Nov 2021 18:22:55 +0000 Subject: [PATCH 58/78] [WIP] Drop CL headers and Makefiles Update README Move new models to /src --- .github/workflows/main.yaml | 4 +- README.android | 36 - README.md | 43 +- TBB.make | 56 - legacy/Makefile | 22 - src/CL/cl.h | 1902 ----------------- src/CL/cl_d3d10.h | 117 - src/CL/cl_d3d11.h | 117 - src/CL/cl_dx9_media_sharing.h | 118 - src/CL/cl_dx9_media_sharing_intel.h | 170 -- src/CL/cl_egl.h | 120 -- src/CL/cl_ext.h | 841 -------- src/CL/cl_ext_intel.h | 682 ------ src/CL/cl_gl.h | 159 -- src/CL/cl_gl_ext.h | 40 - src/CL/cl_half.h | 440 ---- src/CL/cl_icd.h | 1287 ----------- src/CL/cl_platform.h | 1384 ------------ src/CL/cl_va_api_media_sharing_intel.h | 160 -- src/CL/cl_version.h | 81 - src/CL/opencl.h | 33 - src/acc/Makefile | 58 - src/cuda/Makefile | 40 - src/hip/Makefile | 11 - .../java/java-stream}/.gitignore | 0 .../.mvn/wrapper/maven-wrapper.jar | Bin .../.mvn/wrapper/maven-wrapper.properties | 0 .../java/java-stream}/README.md | 0 {java-stream => src/java/java-stream}/mvnw | 0 .../java/java-stream}/mvnw.cmd | 0 {java-stream => src/java/java-stream}/pom.xml | 0 .../main/java/javastream/FractionalMaths.java | 0 .../src/main/java/javastream/JavaStream.java | 0 .../src/main/java/javastream/Main.java | 0 .../javastream/aparapi/AparapiStreams.java | 0 .../aparapi/GenericAparapiStreamKernel.java | 0 .../aparapi/SpecialisedDoubleKernel.java | 0 .../aparapi/SpecialisedFloatKernel.java | 0 .../javastream/jdk/GenericPlainStream.java | 0 .../java/javastream/jdk/GenericStream.java | 0 .../main/java/javastream/jdk/JdkStreams.java | 0 .../main/java/javastream/jdk/PlainStream.java | 0 .../jdk/SpecialisedDoubleStream.java | 0 .../jdk/SpecialisedFloatStream.java | 0 .../jdk/SpecialisedPlainDoubleStream.java | 0 .../jdk/SpecialisedPlainFloatStream.java | 0 .../tornadovm/GenericTornadoVMStream.java | 0 .../tornadovm/SpecialisedDouble.java | 0 .../tornadovm/SpecialisedFloat.java | 0 .../tornadovm/TornadoVMStreams.java | 0 .../src/test/java/javastream/SmokeTest.java | 0 .../JuliaStream.jl}/.JuliaFormatter.toml | 0 .../julia/JuliaStream.jl}/.gitignore | 0 .../JuliaStream.jl}/AMDGPU/Manifest.toml | 0 .../julia/JuliaStream.jl}/AMDGPU/Project.toml | 0 .../julia/JuliaStream.jl}/CUDA/Manifest.toml | 0 .../julia/JuliaStream.jl}/CUDA/Project.toml | 0 .../KernelAbstractions/Manifest.toml | 0 .../KernelAbstractions/Project.toml | 0 .../julia/JuliaStream.jl}/Manifest.toml | 0 .../julia/JuliaStream.jl}/Project.toml | 0 .../julia/JuliaStream.jl}/README.md | 0 .../JuliaStream.jl}/Threaded/Manifest.toml | 0 .../JuliaStream.jl}/Threaded/Project.toml | 0 .../JuliaStream.jl}/oneAPI/Manifest.toml | 0 .../julia/JuliaStream.jl}/oneAPI/Project.toml | 0 .../julia/JuliaStream.jl}/src/AMDGPUStream.jl | 0 .../julia/JuliaStream.jl}/src/CUDAStream.jl | 0 .../JuliaStream.jl}/src/DistributedStream.jl | 0 .../julia/JuliaStream.jl}/src/JuliaStream.jl | 0 .../src/KernelAbstractionsStream.jl | 0 .../julia/JuliaStream.jl}/src/PlainStream.jl | 0 .../julia/JuliaStream.jl}/src/Stream.jl | 0 .../julia/JuliaStream.jl}/src/StreamData.jl | 0 .../JuliaStream.jl}/src/ThreadedStream.jl | 0 .../julia/JuliaStream.jl}/src/oneAPIStream.jl | 0 .../julia/JuliaStream.jl}/update_all.sh | 0 src/kokkos/Makefile | 99 - {legacy => src/legacy}/HCStream.cpp | 0 {legacy => src/legacy}/HCStream.h | 0 src/{ => ocl}/CL/cl2.hpp | 0 src/ocl/Makefile | 39 - src/omp/Makefile | 103 - src/raja/Makefile | 58 - .../scala/scala-stream}/.bsp/sbt.json | 0 .../scala/scala-stream}/.gitignore | 0 .../scala/scala-stream}/.jvmopts | 0 .../scala/scala-stream}/.scalafmt.conf | 0 .../scala/scala-stream}/README.md | 0 .../scala/scala-stream}/build.sbt | 0 .../scala-stream}/project/build.properties | 0 .../scala/scala-stream}/project/plugins.sbt | 0 .../scala/scala-stream}/reflect-config.json | 0 {scala-stream => src/scala/scala-stream}/sbt | 0 .../sbt-dist/bin/java9-rt-export.jar | Bin .../scala/scala-stream}/sbt-dist/bin/sbt | 0 .../sbt-dist/bin/sbt-launch-lib.bash | 0 .../scala-stream}/sbt-dist/bin/sbt-launch.jar | Bin .../scala/scala-stream}/sbt-dist/bin/sbt.bat | 0 .../scala-stream}/sbt-dist/conf/sbtconfig.txt | 0 .../scala/scala-stream}/sbt-dist/conf/sbtopts | 0 .../main/scala/scalastream/J8SStream.scala | 0 .../main/scala/scalastream/ParStream.scala | 0 .../main/scala/scalastream/PlainStream.scala | 0 .../main/scala/scalastream/ScalaStream.scala | 0 .../main/scala/scalastream/ThreadStream.scala | 0 src/std/Makefile | 14 - src/std20/Makefile | 26 - src/sycl/Makefile | 81 - TBB.cmake => src/tbb/TBB.cmake | 0 TBBStream.cpp => src/tbb/TBBStream.cpp | 0 TBBStream.hpp => src/tbb/TBBStream.hpp | 0 THRUST.cmake => src/thrust/THRUST.cmake | 0 ThrustStream.cu => src/thrust/ThrustStream.cu | 0 ThrustStream.h => src/thrust/ThrustStream.h | 0 115 files changed, 10 insertions(+), 8331 deletions(-) delete mode 100644 README.android delete mode 100644 TBB.make delete mode 100644 legacy/Makefile delete mode 100644 src/CL/cl.h delete mode 100644 src/CL/cl_d3d10.h delete mode 100644 src/CL/cl_d3d11.h delete mode 100644 src/CL/cl_dx9_media_sharing.h delete mode 100644 src/CL/cl_dx9_media_sharing_intel.h delete mode 100644 src/CL/cl_egl.h delete mode 100644 src/CL/cl_ext.h delete mode 100644 src/CL/cl_ext_intel.h delete mode 100644 src/CL/cl_gl.h delete mode 100644 src/CL/cl_gl_ext.h delete mode 100644 src/CL/cl_half.h delete mode 100644 src/CL/cl_icd.h delete mode 100644 src/CL/cl_platform.h delete mode 100644 src/CL/cl_va_api_media_sharing_intel.h delete mode 100644 src/CL/cl_version.h delete mode 100644 src/CL/opencl.h delete mode 100644 src/acc/Makefile delete mode 100644 src/cuda/Makefile delete mode 100644 src/hip/Makefile rename {java-stream => src/java/java-stream}/.gitignore (100%) rename {java-stream => src/java/java-stream}/.mvn/wrapper/maven-wrapper.jar (100%) rename {java-stream => src/java/java-stream}/.mvn/wrapper/maven-wrapper.properties (100%) rename {java-stream => src/java/java-stream}/README.md (100%) rename {java-stream => src/java/java-stream}/mvnw (100%) rename {java-stream => src/java/java-stream}/mvnw.cmd (100%) rename {java-stream => src/java/java-stream}/pom.xml (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/FractionalMaths.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/JavaStream.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/Main.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/aparapi/AparapiStreams.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/aparapi/GenericAparapiStreamKernel.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/aparapi/SpecialisedDoubleKernel.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/aparapi/SpecialisedFloatKernel.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/jdk/GenericPlainStream.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/jdk/GenericStream.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/jdk/JdkStreams.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/jdk/PlainStream.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/jdk/SpecialisedDoubleStream.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/jdk/SpecialisedFloatStream.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/jdk/SpecialisedPlainDoubleStream.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/jdk/SpecialisedPlainFloatStream.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/tornadovm/GenericTornadoVMStream.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/tornadovm/SpecialisedDouble.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/tornadovm/SpecialisedFloat.java (100%) rename {java-stream => src/java/java-stream}/src/main/java/javastream/tornadovm/TornadoVMStreams.java (100%) rename {java-stream => src/java/java-stream}/src/test/java/javastream/SmokeTest.java (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/.JuliaFormatter.toml (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/.gitignore (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/AMDGPU/Manifest.toml (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/AMDGPU/Project.toml (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/CUDA/Manifest.toml (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/CUDA/Project.toml (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/KernelAbstractions/Manifest.toml (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/KernelAbstractions/Project.toml (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/Manifest.toml (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/Project.toml (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/README.md (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/Threaded/Manifest.toml (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/Threaded/Project.toml (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/oneAPI/Manifest.toml (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/oneAPI/Project.toml (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/src/AMDGPUStream.jl (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/src/CUDAStream.jl (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/src/DistributedStream.jl (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/src/JuliaStream.jl (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/src/KernelAbstractionsStream.jl (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/src/PlainStream.jl (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/src/Stream.jl (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/src/StreamData.jl (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/src/ThreadedStream.jl (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/src/oneAPIStream.jl (100%) rename {JuliaStream.jl => src/julia/JuliaStream.jl}/update_all.sh (100%) delete mode 100644 src/kokkos/Makefile rename {legacy => src/legacy}/HCStream.cpp (100%) rename {legacy => src/legacy}/HCStream.h (100%) rename src/{ => ocl}/CL/cl2.hpp (100%) delete mode 100644 src/ocl/Makefile delete mode 100644 src/omp/Makefile delete mode 100644 src/raja/Makefile rename {scala-stream => src/scala/scala-stream}/.bsp/sbt.json (100%) rename {scala-stream => src/scala/scala-stream}/.gitignore (100%) rename {scala-stream => src/scala/scala-stream}/.jvmopts (100%) rename {scala-stream => src/scala/scala-stream}/.scalafmt.conf (100%) rename {scala-stream => src/scala/scala-stream}/README.md (100%) rename {scala-stream => src/scala/scala-stream}/build.sbt (100%) rename {scala-stream => src/scala/scala-stream}/project/build.properties (100%) rename {scala-stream => src/scala/scala-stream}/project/plugins.sbt (100%) rename {scala-stream => src/scala/scala-stream}/reflect-config.json (100%) rename {scala-stream => src/scala/scala-stream}/sbt (100%) rename {scala-stream => src/scala/scala-stream}/sbt-dist/bin/java9-rt-export.jar (100%) rename {scala-stream => src/scala/scala-stream}/sbt-dist/bin/sbt (100%) rename {scala-stream => src/scala/scala-stream}/sbt-dist/bin/sbt-launch-lib.bash (100%) rename {scala-stream => src/scala/scala-stream}/sbt-dist/bin/sbt-launch.jar (100%) rename {scala-stream => src/scala/scala-stream}/sbt-dist/bin/sbt.bat (100%) rename {scala-stream => src/scala/scala-stream}/sbt-dist/conf/sbtconfig.txt (100%) rename {scala-stream => src/scala/scala-stream}/sbt-dist/conf/sbtopts (100%) rename {scala-stream => src/scala/scala-stream}/src/main/scala/scalastream/J8SStream.scala (100%) rename {scala-stream => src/scala/scala-stream}/src/main/scala/scalastream/ParStream.scala (100%) rename {scala-stream => src/scala/scala-stream}/src/main/scala/scalastream/PlainStream.scala (100%) rename {scala-stream => src/scala/scala-stream}/src/main/scala/scalastream/ScalaStream.scala (100%) rename {scala-stream => src/scala/scala-stream}/src/main/scala/scalastream/ThreadStream.scala (100%) delete mode 100644 src/std/Makefile delete mode 100644 src/std20/Makefile delete mode 100644 src/sycl/Makefile rename TBB.cmake => src/tbb/TBB.cmake (100%) rename TBBStream.cpp => src/tbb/TBBStream.cpp (100%) rename TBBStream.hpp => src/tbb/TBBStream.hpp (100%) rename THRUST.cmake => src/thrust/THRUST.cmake (100%) rename ThrustStream.cu => src/thrust/ThrustStream.cu (100%) rename ThrustStream.h => src/thrust/ThrustStream.h (100%) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index b7cc493..423064a 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-18.04 defaults: run: - working-directory: ./java-stream + working-directory: ./src/java/java-stream steps: - uses: actions/checkout@v2 - name: Test build project @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-18.04 defaults: run: - working-directory: ./JuliaStream.jl + working-directory: ./src/julia/JuliaStream.jl steps: - uses: actions/checkout@v2 - name: Setup project diff --git a/README.android b/README.android deleted file mode 100644 index edc4a52..0000000 --- a/README.android +++ /dev/null @@ -1,36 +0,0 @@ -Android (outdated instructions) ------------------- - -Assuming you have a recent Android NDK available, you can use the -toolchain that it provides to build GPU-STREAM. You should first -use the NDK to generate a standalone toolchain: - - # Select a directory to install the toolchain to - ANDROID_NATIVE_TOOLCHAIN=/path/to/toolchain - - ${NDK}/build/tools/make-standalone-toolchain.sh \ - --platform=android-14 \ - --toolchain=arm-linux-androideabi-4.8 \ - --install-dir=${ANDROID_NATIVE_TOOLCHAIN} - -Make sure that the OpenCL headers and library (libOpenCL.so) are -available in `${ANDROID_NATIVE_TOOLCHAIN}/sysroot/usr/`. - -You should then be able to build GPU-STREAM: - - make CXX=${ANDROID_NATIVE_TOOLCHAIN}/bin/arm-linux-androideabi-g++ - -Copy the executable and OpenCL kernels to the device: - - adb push gpu-stream-ocl /data/local/tmp - adb push ocl-stream-kernels.cl /data/local/tmp - -Run GPU-STREAM from an adb shell: - - adb shell - cd /data/local/tmp - - # Use float if device doesn't support double, and reduce array size - ./gpu-stream-ocl --float -n 6 -s 10000000 - - diff --git a/README.md b/README.md index 598b4cd..d36da1a 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ There are multiple implementations of this benchmark in a variety of programming Currently implemented are: - OpenCL - CUDA + - HIP - OpenACC - OpenMP 3 and 4.5 - C++ Parallel STL @@ -20,11 +21,14 @@ Currently implemented are: - RAJA - SYCL - TBB + - Thrust (via CUDA or HIP) This code was previously called GPU-STREAM. This project also contains implementations in alternative languages with different build systems: -* Scala - [scala-stream](./scala-stream) +* Julia - [JuliaStream.jl](./src/julia/JuliaStream.jl) +* Java - [java-stream](./src/java/java-stream) +* Scala - [scala-stream](./src/scala/scala-stream) How is this different to STREAM? -------------------------------- @@ -109,41 +113,10 @@ Alternatively, refer to the [CI script](./ci-test-compile.sh), which test-compil ### GNU Make -We have supplied a series of Makefiles, one for each programming model, to assist with building. -The Makefiles contain common build options, and should be simple to customise for your needs too. +Support for Make has been removed from 4.0 onwards. +However, as the build process only involves a few source files, the required compile commands can be extracted from the CI output. -General usage is `make -C src/` -Common compiler flags and names can be set by passing a `COMPILER` option to Make, e.g. `make COMPILER=GNU`. -Some models allow specifying a CPU or GPU style target, and this can be set by passing a `TARGET` option to Make, e.g. `make TARGET=GPU`. - -Pass in extra flags via the `EXTRA_FLAGS` option. - -The binaries are named in the form `-stream`. - -#### Building Kokkos for Make - -Kokkos version >= 3 requires setting the `KOKKOS_PATH` flag to the *source* directory of a distribution. -For example: - -``` -cd -wget https://github.com/kokkos/kokkos/archive/3.1.01.tar.gz -tar -xvf 3.1.01.tar.gz # should end up with ~/kokkos-3.1.01 -cd BabelStream -make -C src/kokkos KOKKOS_PATH=~/kokkos-3.1.01 -``` -See make output for more information on supported flags. - -#### Building RAJA for Make - -We use the following command to build RAJA using the Intel Compiler. -``` -cmake .. -DCMAKE_INSTALL_PREFIX= -DCMAKE_C_COMPILER=icc -DCMAKE_CXX_COMPILER=icpc -DRAJA_PTR="RAJA_USE_RESTRICT_ALIGNED_PTR" -DCMAKE_BUILD_TYPE=ICCBuild -DRAJA_ENABLE_TESTS=Off -``` -For building with CUDA support, we use the following command. -``` -cmake .. -DCMAKE_INSTALL_PREFIX= -DRAJA_PTR="RAJA_USE_RESTRICT_ALIGNED_PTR" -DRAJA_ENABLE_CUDA=1 -DRAJA_ENABLE_TESTS=Off -``` + Results ------- diff --git a/TBB.make b/TBB.make deleted file mode 100644 index c224a5a..0000000 --- a/TBB.make +++ /dev/null @@ -1,56 +0,0 @@ - -ifndef COMPILER -define compiler_help -Set COMPILER to change flags (defaulting to GNU). -Available compilers are: - GNU INTEL INTEL_LEGACY - -endef -$(info $(compiler_help)) -COMPILER=GNU -endif - - -CXX_GNU = g++ -CXX_INTEL = icpx -CXX_INTEL_LEGACY = icpc -CXX = $(COMPILER_$(COMPILER)) - -CXXFLAGS_GNU = -march=native -CXXFLAGS_INTEL = -march=native -CXXFLAGS_INTEL_LEGACY = -qopt-streaming-stores=always - -CXX = $(CXX_$(COMPILER)) -CXXFLAGS = -std=c++11 -O3 $(CXXFLAGS_$(COMPILER)) - - - -ifndef PARTITIONER -define partitioner_help -Set PARTITIONER to select TBB's partitioner. -Partitioner specifies how a loop template should partition its work among threads. - -Available options: - AUTO - Optimize range subdivision based on work-stealing events. - AFFINITY - Proportional splitting that optimizes for cache affinity. - STATIC - Distribute work uniformly with no additional load balancing. - SIMPLE - Recursively split its range until it cannot be further subdivided. - -See https://spec.oneapi.com/versions/latest/elements/oneTBB/source/algorithms.html#partitioners -for more details. - -endef -$(info $(partitioner_help)) -PARTITIONER=AUTO -endif - -PARTITIONER_MODE = -DPARTITIONER_$(PARTITIONER) - - -tbb-stream: main.cpp TBBStream.cpp - $(CXX) -DTBB $(PARTITIONER_MODE) $(CXXFLAGS) $^ $(EXTRA_FLAGS) -I$(TBB_DIR)/include -Wl,-rpath,$(TBB_DIR)/lib/intel64/gcc4.8 $(TBB_DIR)/lib/intel64/gcc4.8/libtbb.so -o $@ - -.PHONY: clean -clean: - rm -f tbb-stream - diff --git a/legacy/Makefile b/legacy/Makefile deleted file mode 100644 index 8047ae1..0000000 --- a/legacy/Makefile +++ /dev/null @@ -1,22 +0,0 @@ - -HCC = hcc - -CXXFLAGS+=-O3 $(shell hcc-config --cxxflags) -LDFLAGS+=$(shell hcc-config --ldflags) - -ifdef TBSIZE -CXXFLAGS+=-DVIRTUALTILESIZE=$(TBSIZE) -endif - -ifdef NTILES -CXXFLAGS+=-DNTILES=$(TBSIZE) -endif - - -hc-stream: ../main.cpp HCStream.cpp - $(HCC) $(CXXFLAGS) -DHC $^ $(LDFLAGS) $(EXTRA_FLAGS) -o $@ -I. -I.. - - -.PHONY: clean -clean: - rm -f hc-stream diff --git a/src/CL/cl.h b/src/CL/cl.h deleted file mode 100644 index f33f999..0000000 --- a/src/CL/cl.h +++ /dev/null @@ -1,1902 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008-2020 The Khronos Group Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -#ifndef __OPENCL_CL_H -#define __OPENCL_CL_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/******************************************************************************/ - -typedef struct _cl_platform_id * cl_platform_id; -typedef struct _cl_device_id * cl_device_id; -typedef struct _cl_context * cl_context; -typedef struct _cl_command_queue * cl_command_queue; -typedef struct _cl_mem * cl_mem; -typedef struct _cl_program * cl_program; -typedef struct _cl_kernel * cl_kernel; -typedef struct _cl_event * cl_event; -typedef struct _cl_sampler * cl_sampler; - -typedef cl_uint cl_bool; /* WARNING! Unlike cl_ types in cl_platform.h, cl_bool is not guaranteed to be the same size as the bool in kernels. */ -typedef cl_ulong cl_bitfield; -typedef cl_bitfield cl_device_type; -typedef cl_uint cl_platform_info; -typedef cl_uint cl_device_info; -typedef cl_bitfield cl_device_fp_config; -typedef cl_uint cl_device_mem_cache_type; -typedef cl_uint cl_device_local_mem_type; -typedef cl_bitfield cl_device_exec_capabilities; -#ifdef CL_VERSION_2_0 -typedef cl_bitfield cl_device_svm_capabilities; -#endif -typedef cl_bitfield cl_command_queue_properties; -#ifdef CL_VERSION_1_2 -typedef intptr_t cl_device_partition_property; -typedef cl_bitfield cl_device_affinity_domain; -#endif - -typedef intptr_t cl_context_properties; -typedef cl_uint cl_context_info; -#ifdef CL_VERSION_2_0 -typedef cl_bitfield cl_queue_properties; -#endif -typedef cl_uint cl_command_queue_info; -typedef cl_uint cl_channel_order; -typedef cl_uint cl_channel_type; -typedef cl_bitfield cl_mem_flags; -#ifdef CL_VERSION_2_0 -typedef cl_bitfield cl_svm_mem_flags; -#endif -typedef cl_uint cl_mem_object_type; -typedef cl_uint cl_mem_info; -#ifdef CL_VERSION_1_2 -typedef cl_bitfield cl_mem_migration_flags; -#endif -typedef cl_uint cl_image_info; -#ifdef CL_VERSION_1_1 -typedef cl_uint cl_buffer_create_type; -#endif -typedef cl_uint cl_addressing_mode; -typedef cl_uint cl_filter_mode; -typedef cl_uint cl_sampler_info; -typedef cl_bitfield cl_map_flags; -#ifdef CL_VERSION_2_0 -typedef intptr_t cl_pipe_properties; -typedef cl_uint cl_pipe_info; -#endif -typedef cl_uint cl_program_info; -typedef cl_uint cl_program_build_info; -#ifdef CL_VERSION_1_2 -typedef cl_uint cl_program_binary_type; -#endif -typedef cl_int cl_build_status; -typedef cl_uint cl_kernel_info; -#ifdef CL_VERSION_1_2 -typedef cl_uint cl_kernel_arg_info; -typedef cl_uint cl_kernel_arg_address_qualifier; -typedef cl_uint cl_kernel_arg_access_qualifier; -typedef cl_bitfield cl_kernel_arg_type_qualifier; -#endif -typedef cl_uint cl_kernel_work_group_info; -#ifdef CL_VERSION_2_1 -typedef cl_uint cl_kernel_sub_group_info; -#endif -typedef cl_uint cl_event_info; -typedef cl_uint cl_command_type; -typedef cl_uint cl_profiling_info; -#ifdef CL_VERSION_2_0 -typedef cl_bitfield cl_sampler_properties; -typedef cl_uint cl_kernel_exec_info; -#endif -#ifdef CL_VERSION_3_0 -typedef cl_bitfield cl_device_atomic_capabilities; -typedef cl_uint cl_khronos_vendor_id; -typedef cl_bitfield cl_mem_properties; -typedef cl_uint cl_version; -#endif - -typedef struct _cl_image_format { - cl_channel_order image_channel_order; - cl_channel_type image_channel_data_type; -} cl_image_format; - -#ifdef CL_VERSION_1_2 - -typedef struct _cl_image_desc { - cl_mem_object_type image_type; - size_t image_width; - size_t image_height; - size_t image_depth; - size_t image_array_size; - size_t image_row_pitch; - size_t image_slice_pitch; - cl_uint num_mip_levels; - cl_uint num_samples; -#ifdef CL_VERSION_2_0 -#ifdef __GNUC__ - __extension__ /* Prevents warnings about anonymous union in -pedantic builds */ -#endif -#ifdef _MSC_VER -#pragma warning( push ) -#pragma warning( disable : 4201 ) /* Prevents warning about nameless struct/union in /W4 /Za builds */ -#endif - union { -#endif - cl_mem buffer; -#ifdef CL_VERSION_2_0 - cl_mem mem_object; - }; -#ifdef _MSC_VER -#pragma warning( pop ) -#endif -#endif -} cl_image_desc; - -#endif - -#ifdef CL_VERSION_1_1 - -typedef struct _cl_buffer_region { - size_t origin; - size_t size; -} cl_buffer_region; - -#endif - -#ifdef CL_VERSION_3_0 - -#define CL_NAME_VERSION_MAX_NAME_SIZE 64 - -typedef struct _cl_name_version { - cl_version version; - char name[CL_NAME_VERSION_MAX_NAME_SIZE]; -} cl_name_version; - -#endif - -/******************************************************************************/ - -/* Error Codes */ -#define CL_SUCCESS 0 -#define CL_DEVICE_NOT_FOUND -1 -#define CL_DEVICE_NOT_AVAILABLE -2 -#define CL_COMPILER_NOT_AVAILABLE -3 -#define CL_MEM_OBJECT_ALLOCATION_FAILURE -4 -#define CL_OUT_OF_RESOURCES -5 -#define CL_OUT_OF_HOST_MEMORY -6 -#define CL_PROFILING_INFO_NOT_AVAILABLE -7 -#define CL_MEM_COPY_OVERLAP -8 -#define CL_IMAGE_FORMAT_MISMATCH -9 -#define CL_IMAGE_FORMAT_NOT_SUPPORTED -10 -#define CL_BUILD_PROGRAM_FAILURE -11 -#define CL_MAP_FAILURE -12 -#ifdef CL_VERSION_1_1 -#define CL_MISALIGNED_SUB_BUFFER_OFFSET -13 -#define CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST -14 -#endif -#ifdef CL_VERSION_1_2 -#define CL_COMPILE_PROGRAM_FAILURE -15 -#define CL_LINKER_NOT_AVAILABLE -16 -#define CL_LINK_PROGRAM_FAILURE -17 -#define CL_DEVICE_PARTITION_FAILED -18 -#define CL_KERNEL_ARG_INFO_NOT_AVAILABLE -19 -#endif - -#define CL_INVALID_VALUE -30 -#define CL_INVALID_DEVICE_TYPE -31 -#define CL_INVALID_PLATFORM -32 -#define CL_INVALID_DEVICE -33 -#define CL_INVALID_CONTEXT -34 -#define CL_INVALID_QUEUE_PROPERTIES -35 -#define CL_INVALID_COMMAND_QUEUE -36 -#define CL_INVALID_HOST_PTR -37 -#define CL_INVALID_MEM_OBJECT -38 -#define CL_INVALID_IMAGE_FORMAT_DESCRIPTOR -39 -#define CL_INVALID_IMAGE_SIZE -40 -#define CL_INVALID_SAMPLER -41 -#define CL_INVALID_BINARY -42 -#define CL_INVALID_BUILD_OPTIONS -43 -#define CL_INVALID_PROGRAM -44 -#define CL_INVALID_PROGRAM_EXECUTABLE -45 -#define CL_INVALID_KERNEL_NAME -46 -#define CL_INVALID_KERNEL_DEFINITION -47 -#define CL_INVALID_KERNEL -48 -#define CL_INVALID_ARG_INDEX -49 -#define CL_INVALID_ARG_VALUE -50 -#define CL_INVALID_ARG_SIZE -51 -#define CL_INVALID_KERNEL_ARGS -52 -#define CL_INVALID_WORK_DIMENSION -53 -#define CL_INVALID_WORK_GROUP_SIZE -54 -#define CL_INVALID_WORK_ITEM_SIZE -55 -#define CL_INVALID_GLOBAL_OFFSET -56 -#define CL_INVALID_EVENT_WAIT_LIST -57 -#define CL_INVALID_EVENT -58 -#define CL_INVALID_OPERATION -59 -#define CL_INVALID_GL_OBJECT -60 -#define CL_INVALID_BUFFER_SIZE -61 -#define CL_INVALID_MIP_LEVEL -62 -#define CL_INVALID_GLOBAL_WORK_SIZE -63 -#ifdef CL_VERSION_1_1 -#define CL_INVALID_PROPERTY -64 -#endif -#ifdef CL_VERSION_1_2 -#define CL_INVALID_IMAGE_DESCRIPTOR -65 -#define CL_INVALID_COMPILER_OPTIONS -66 -#define CL_INVALID_LINKER_OPTIONS -67 -#define CL_INVALID_DEVICE_PARTITION_COUNT -68 -#endif -#ifdef CL_VERSION_2_0 -#define CL_INVALID_PIPE_SIZE -69 -#define CL_INVALID_DEVICE_QUEUE -70 -#endif -#ifdef CL_VERSION_2_2 -#define CL_INVALID_SPEC_ID -71 -#define CL_MAX_SIZE_RESTRICTION_EXCEEDED -72 -#endif - - -/* cl_bool */ -#define CL_FALSE 0 -#define CL_TRUE 1 -#ifdef CL_VERSION_1_2 -#define CL_BLOCKING CL_TRUE -#define CL_NON_BLOCKING CL_FALSE -#endif - -/* cl_platform_info */ -#define CL_PLATFORM_PROFILE 0x0900 -#define CL_PLATFORM_VERSION 0x0901 -#define CL_PLATFORM_NAME 0x0902 -#define CL_PLATFORM_VENDOR 0x0903 -#define CL_PLATFORM_EXTENSIONS 0x0904 -#ifdef CL_VERSION_2_1 -#define CL_PLATFORM_HOST_TIMER_RESOLUTION 0x0905 -#endif -#ifdef CL_VERSION_3_0 -#define CL_PLATFORM_NUMERIC_VERSION 0x0906 -#define CL_PLATFORM_EXTENSIONS_WITH_VERSION 0x0907 -#endif - -/* cl_device_type - bitfield */ -#define CL_DEVICE_TYPE_DEFAULT (1 << 0) -#define CL_DEVICE_TYPE_CPU (1 << 1) -#define CL_DEVICE_TYPE_GPU (1 << 2) -#define CL_DEVICE_TYPE_ACCELERATOR (1 << 3) -#ifdef CL_VERSION_1_2 -#define CL_DEVICE_TYPE_CUSTOM (1 << 4) -#endif -#define CL_DEVICE_TYPE_ALL 0xFFFFFFFF - -/* cl_device_info */ -#define CL_DEVICE_TYPE 0x1000 -#define CL_DEVICE_VENDOR_ID 0x1001 -#define CL_DEVICE_MAX_COMPUTE_UNITS 0x1002 -#define CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS 0x1003 -#define CL_DEVICE_MAX_WORK_GROUP_SIZE 0x1004 -#define CL_DEVICE_MAX_WORK_ITEM_SIZES 0x1005 -#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR 0x1006 -#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT 0x1007 -#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT 0x1008 -#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG 0x1009 -#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT 0x100A -#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE 0x100B -#define CL_DEVICE_MAX_CLOCK_FREQUENCY 0x100C -#define CL_DEVICE_ADDRESS_BITS 0x100D -#define CL_DEVICE_MAX_READ_IMAGE_ARGS 0x100E -#define CL_DEVICE_MAX_WRITE_IMAGE_ARGS 0x100F -#define CL_DEVICE_MAX_MEM_ALLOC_SIZE 0x1010 -#define CL_DEVICE_IMAGE2D_MAX_WIDTH 0x1011 -#define CL_DEVICE_IMAGE2D_MAX_HEIGHT 0x1012 -#define CL_DEVICE_IMAGE3D_MAX_WIDTH 0x1013 -#define CL_DEVICE_IMAGE3D_MAX_HEIGHT 0x1014 -#define CL_DEVICE_IMAGE3D_MAX_DEPTH 0x1015 -#define CL_DEVICE_IMAGE_SUPPORT 0x1016 -#define CL_DEVICE_MAX_PARAMETER_SIZE 0x1017 -#define CL_DEVICE_MAX_SAMPLERS 0x1018 -#define CL_DEVICE_MEM_BASE_ADDR_ALIGN 0x1019 -#define CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE 0x101A -#define CL_DEVICE_SINGLE_FP_CONFIG 0x101B -#define CL_DEVICE_GLOBAL_MEM_CACHE_TYPE 0x101C -#define CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE 0x101D -#define CL_DEVICE_GLOBAL_MEM_CACHE_SIZE 0x101E -#define CL_DEVICE_GLOBAL_MEM_SIZE 0x101F -#define CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE 0x1020 -#define CL_DEVICE_MAX_CONSTANT_ARGS 0x1021 -#define CL_DEVICE_LOCAL_MEM_TYPE 0x1022 -#define CL_DEVICE_LOCAL_MEM_SIZE 0x1023 -#define CL_DEVICE_ERROR_CORRECTION_SUPPORT 0x1024 -#define CL_DEVICE_PROFILING_TIMER_RESOLUTION 0x1025 -#define CL_DEVICE_ENDIAN_LITTLE 0x1026 -#define CL_DEVICE_AVAILABLE 0x1027 -#define CL_DEVICE_COMPILER_AVAILABLE 0x1028 -#define CL_DEVICE_EXECUTION_CAPABILITIES 0x1029 -#define CL_DEVICE_QUEUE_PROPERTIES 0x102A /* deprecated */ -#ifdef CL_VERSION_2_0 -#define CL_DEVICE_QUEUE_ON_HOST_PROPERTIES 0x102A -#endif -#define CL_DEVICE_NAME 0x102B -#define CL_DEVICE_VENDOR 0x102C -#define CL_DRIVER_VERSION 0x102D -#define CL_DEVICE_PROFILE 0x102E -#define CL_DEVICE_VERSION 0x102F -#define CL_DEVICE_EXTENSIONS 0x1030 -#define CL_DEVICE_PLATFORM 0x1031 -#ifdef CL_VERSION_1_2 -#define CL_DEVICE_DOUBLE_FP_CONFIG 0x1032 -#endif -/* 0x1033 reserved for CL_DEVICE_HALF_FP_CONFIG which is already defined in "cl_ext.h" */ -#ifdef CL_VERSION_1_1 -#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF 0x1034 -#define CL_DEVICE_HOST_UNIFIED_MEMORY 0x1035 /* deprecated */ -#define CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR 0x1036 -#define CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT 0x1037 -#define CL_DEVICE_NATIVE_VECTOR_WIDTH_INT 0x1038 -#define CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG 0x1039 -#define CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT 0x103A -#define CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE 0x103B -#define CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF 0x103C -#define CL_DEVICE_OPENCL_C_VERSION 0x103D -#endif -#ifdef CL_VERSION_1_2 -#define CL_DEVICE_LINKER_AVAILABLE 0x103E -#define CL_DEVICE_BUILT_IN_KERNELS 0x103F -#define CL_DEVICE_IMAGE_MAX_BUFFER_SIZE 0x1040 -#define CL_DEVICE_IMAGE_MAX_ARRAY_SIZE 0x1041 -#define CL_DEVICE_PARENT_DEVICE 0x1042 -#define CL_DEVICE_PARTITION_MAX_SUB_DEVICES 0x1043 -#define CL_DEVICE_PARTITION_PROPERTIES 0x1044 -#define CL_DEVICE_PARTITION_AFFINITY_DOMAIN 0x1045 -#define CL_DEVICE_PARTITION_TYPE 0x1046 -#define CL_DEVICE_REFERENCE_COUNT 0x1047 -#define CL_DEVICE_PREFERRED_INTEROP_USER_SYNC 0x1048 -#define CL_DEVICE_PRINTF_BUFFER_SIZE 0x1049 -#endif -#ifdef CL_VERSION_2_0 -#define CL_DEVICE_IMAGE_PITCH_ALIGNMENT 0x104A -#define CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT 0x104B -#define CL_DEVICE_MAX_READ_WRITE_IMAGE_ARGS 0x104C -#define CL_DEVICE_MAX_GLOBAL_VARIABLE_SIZE 0x104D -#define CL_DEVICE_QUEUE_ON_DEVICE_PROPERTIES 0x104E -#define CL_DEVICE_QUEUE_ON_DEVICE_PREFERRED_SIZE 0x104F -#define CL_DEVICE_QUEUE_ON_DEVICE_MAX_SIZE 0x1050 -#define CL_DEVICE_MAX_ON_DEVICE_QUEUES 0x1051 -#define CL_DEVICE_MAX_ON_DEVICE_EVENTS 0x1052 -#define CL_DEVICE_SVM_CAPABILITIES 0x1053 -#define CL_DEVICE_GLOBAL_VARIABLE_PREFERRED_TOTAL_SIZE 0x1054 -#define CL_DEVICE_MAX_PIPE_ARGS 0x1055 -#define CL_DEVICE_PIPE_MAX_ACTIVE_RESERVATIONS 0x1056 -#define CL_DEVICE_PIPE_MAX_PACKET_SIZE 0x1057 -#define CL_DEVICE_PREFERRED_PLATFORM_ATOMIC_ALIGNMENT 0x1058 -#define CL_DEVICE_PREFERRED_GLOBAL_ATOMIC_ALIGNMENT 0x1059 -#define CL_DEVICE_PREFERRED_LOCAL_ATOMIC_ALIGNMENT 0x105A -#endif -#ifdef CL_VERSION_2_1 -#define CL_DEVICE_IL_VERSION 0x105B -#define CL_DEVICE_MAX_NUM_SUB_GROUPS 0x105C -#define CL_DEVICE_SUB_GROUP_INDEPENDENT_FORWARD_PROGRESS 0x105D -#endif -#ifdef CL_VERSION_3_0 -#define CL_DEVICE_NUMERIC_VERSION 0x105E -#define CL_DEVICE_EXTENSIONS_WITH_VERSION 0x1060 -#define CL_DEVICE_ILS_WITH_VERSION 0x1061 -#define CL_DEVICE_BUILT_IN_KERNELS_WITH_VERSION 0x1062 -#define CL_DEVICE_ATOMIC_MEMORY_CAPABILITIES 0x1063 -#define CL_DEVICE_ATOMIC_FENCE_CAPABILITIES 0x1064 -#define CL_DEVICE_NON_UNIFORM_WORK_GROUP_SUPPORT 0x1065 -#define CL_DEVICE_OPENCL_C_ALL_VERSIONS 0x1066 -#define CL_DEVICE_PREFERRED_WORK_GROUP_SIZE_MULTIPLE 0x1067 -#define CL_DEVICE_WORK_GROUP_COLLECTIVE_FUNCTIONS_SUPPORT 0x1068 -#define CL_DEVICE_GENERIC_ADDRESS_SPACE_SUPPORT 0x1069 -/* 0x106A to 0x106E - Reserved for upcoming KHR extension */ -#define CL_DEVICE_OPENCL_C_FEATURES 0x106F -#define CL_DEVICE_DEVICE_ENQUEUE_SUPPORT 0x1070 -#define CL_DEVICE_PIPE_SUPPORT 0x1071 -#endif - -/* cl_device_fp_config - bitfield */ -#define CL_FP_DENORM (1 << 0) -#define CL_FP_INF_NAN (1 << 1) -#define CL_FP_ROUND_TO_NEAREST (1 << 2) -#define CL_FP_ROUND_TO_ZERO (1 << 3) -#define CL_FP_ROUND_TO_INF (1 << 4) -#define CL_FP_FMA (1 << 5) -#ifdef CL_VERSION_1_1 -#define CL_FP_SOFT_FLOAT (1 << 6) -#endif -#ifdef CL_VERSION_1_2 -#define CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT (1 << 7) -#endif - -/* cl_device_mem_cache_type */ -#define CL_NONE 0x0 -#define CL_READ_ONLY_CACHE 0x1 -#define CL_READ_WRITE_CACHE 0x2 - -/* cl_device_local_mem_type */ -#define CL_LOCAL 0x1 -#define CL_GLOBAL 0x2 - -/* cl_device_exec_capabilities - bitfield */ -#define CL_EXEC_KERNEL (1 << 0) -#define CL_EXEC_NATIVE_KERNEL (1 << 1) - -/* cl_command_queue_properties - bitfield */ -#define CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE (1 << 0) -#define CL_QUEUE_PROFILING_ENABLE (1 << 1) -#ifdef CL_VERSION_2_0 -#define CL_QUEUE_ON_DEVICE (1 << 2) -#define CL_QUEUE_ON_DEVICE_DEFAULT (1 << 3) -#endif - -/* cl_context_info */ -#define CL_CONTEXT_REFERENCE_COUNT 0x1080 -#define CL_CONTEXT_DEVICES 0x1081 -#define CL_CONTEXT_PROPERTIES 0x1082 -#ifdef CL_VERSION_1_1 -#define CL_CONTEXT_NUM_DEVICES 0x1083 -#endif - -/* cl_context_properties */ -#define CL_CONTEXT_PLATFORM 0x1084 -#ifdef CL_VERSION_1_2 -#define CL_CONTEXT_INTEROP_USER_SYNC 0x1085 -#endif - -#ifdef CL_VERSION_1_2 - -/* cl_device_partition_property */ -#define CL_DEVICE_PARTITION_EQUALLY 0x1086 -#define CL_DEVICE_PARTITION_BY_COUNTS 0x1087 -#define CL_DEVICE_PARTITION_BY_COUNTS_LIST_END 0x0 -#define CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN 0x1088 - -#endif - -#ifdef CL_VERSION_1_2 - -/* cl_device_affinity_domain */ -#define CL_DEVICE_AFFINITY_DOMAIN_NUMA (1 << 0) -#define CL_DEVICE_AFFINITY_DOMAIN_L4_CACHE (1 << 1) -#define CL_DEVICE_AFFINITY_DOMAIN_L3_CACHE (1 << 2) -#define CL_DEVICE_AFFINITY_DOMAIN_L2_CACHE (1 << 3) -#define CL_DEVICE_AFFINITY_DOMAIN_L1_CACHE (1 << 4) -#define CL_DEVICE_AFFINITY_DOMAIN_NEXT_PARTITIONABLE (1 << 5) - -#endif - -#ifdef CL_VERSION_2_0 - -/* cl_device_svm_capabilities */ -#define CL_DEVICE_SVM_COARSE_GRAIN_BUFFER (1 << 0) -#define CL_DEVICE_SVM_FINE_GRAIN_BUFFER (1 << 1) -#define CL_DEVICE_SVM_FINE_GRAIN_SYSTEM (1 << 2) -#define CL_DEVICE_SVM_ATOMICS (1 << 3) - -#endif - -/* cl_command_queue_info */ -#define CL_QUEUE_CONTEXT 0x1090 -#define CL_QUEUE_DEVICE 0x1091 -#define CL_QUEUE_REFERENCE_COUNT 0x1092 -#define CL_QUEUE_PROPERTIES 0x1093 -#ifdef CL_VERSION_2_0 -#define CL_QUEUE_SIZE 0x1094 -#endif -#ifdef CL_VERSION_2_1 -#define CL_QUEUE_DEVICE_DEFAULT 0x1095 -#endif -#ifdef CL_VERSION_3_0 -#define CL_QUEUE_PROPERTIES_ARRAY 0x1098 -#endif - -/* cl_mem_flags and cl_svm_mem_flags - bitfield */ -#define CL_MEM_READ_WRITE (1 << 0) -#define CL_MEM_WRITE_ONLY (1 << 1) -#define CL_MEM_READ_ONLY (1 << 2) -#define CL_MEM_USE_HOST_PTR (1 << 3) -#define CL_MEM_ALLOC_HOST_PTR (1 << 4) -#define CL_MEM_COPY_HOST_PTR (1 << 5) -/* reserved (1 << 6) */ -#ifdef CL_VERSION_1_2 -#define CL_MEM_HOST_WRITE_ONLY (1 << 7) -#define CL_MEM_HOST_READ_ONLY (1 << 8) -#define CL_MEM_HOST_NO_ACCESS (1 << 9) -#endif -#ifdef CL_VERSION_2_0 -#define CL_MEM_SVM_FINE_GRAIN_BUFFER (1 << 10) /* used by cl_svm_mem_flags only */ -#define CL_MEM_SVM_ATOMICS (1 << 11) /* used by cl_svm_mem_flags only */ -#define CL_MEM_KERNEL_READ_AND_WRITE (1 << 12) -#endif - -#ifdef CL_VERSION_1_2 - -/* cl_mem_migration_flags - bitfield */ -#define CL_MIGRATE_MEM_OBJECT_HOST (1 << 0) -#define CL_MIGRATE_MEM_OBJECT_CONTENT_UNDEFINED (1 << 1) - -#endif - -/* cl_channel_order */ -#define CL_R 0x10B0 -#define CL_A 0x10B1 -#define CL_RG 0x10B2 -#define CL_RA 0x10B3 -#define CL_RGB 0x10B4 -#define CL_RGBA 0x10B5 -#define CL_BGRA 0x10B6 -#define CL_ARGB 0x10B7 -#define CL_INTENSITY 0x10B8 -#define CL_LUMINANCE 0x10B9 -#ifdef CL_VERSION_1_1 -#define CL_Rx 0x10BA -#define CL_RGx 0x10BB -#define CL_RGBx 0x10BC -#endif -#ifdef CL_VERSION_1_2 -#define CL_DEPTH 0x10BD -#define CL_DEPTH_STENCIL 0x10BE -#endif -#ifdef CL_VERSION_2_0 -#define CL_sRGB 0x10BF -#define CL_sRGBx 0x10C0 -#define CL_sRGBA 0x10C1 -#define CL_sBGRA 0x10C2 -#define CL_ABGR 0x10C3 -#endif - -/* cl_channel_type */ -#define CL_SNORM_INT8 0x10D0 -#define CL_SNORM_INT16 0x10D1 -#define CL_UNORM_INT8 0x10D2 -#define CL_UNORM_INT16 0x10D3 -#define CL_UNORM_SHORT_565 0x10D4 -#define CL_UNORM_SHORT_555 0x10D5 -#define CL_UNORM_INT_101010 0x10D6 -#define CL_SIGNED_INT8 0x10D7 -#define CL_SIGNED_INT16 0x10D8 -#define CL_SIGNED_INT32 0x10D9 -#define CL_UNSIGNED_INT8 0x10DA -#define CL_UNSIGNED_INT16 0x10DB -#define CL_UNSIGNED_INT32 0x10DC -#define CL_HALF_FLOAT 0x10DD -#define CL_FLOAT 0x10DE -#ifdef CL_VERSION_1_2 -#define CL_UNORM_INT24 0x10DF -#endif -#ifdef CL_VERSION_2_1 -#define CL_UNORM_INT_101010_2 0x10E0 -#endif - -/* cl_mem_object_type */ -#define CL_MEM_OBJECT_BUFFER 0x10F0 -#define CL_MEM_OBJECT_IMAGE2D 0x10F1 -#define CL_MEM_OBJECT_IMAGE3D 0x10F2 -#ifdef CL_VERSION_1_2 -#define CL_MEM_OBJECT_IMAGE2D_ARRAY 0x10F3 -#define CL_MEM_OBJECT_IMAGE1D 0x10F4 -#define CL_MEM_OBJECT_IMAGE1D_ARRAY 0x10F5 -#define CL_MEM_OBJECT_IMAGE1D_BUFFER 0x10F6 -#endif -#ifdef CL_VERSION_2_0 -#define CL_MEM_OBJECT_PIPE 0x10F7 -#endif - -/* cl_mem_info */ -#define CL_MEM_TYPE 0x1100 -#define CL_MEM_FLAGS 0x1101 -#define CL_MEM_SIZE 0x1102 -#define CL_MEM_HOST_PTR 0x1103 -#define CL_MEM_MAP_COUNT 0x1104 -#define CL_MEM_REFERENCE_COUNT 0x1105 -#define CL_MEM_CONTEXT 0x1106 -#ifdef CL_VERSION_1_1 -#define CL_MEM_ASSOCIATED_MEMOBJECT 0x1107 -#define CL_MEM_OFFSET 0x1108 -#endif -#ifdef CL_VERSION_2_0 -#define CL_MEM_USES_SVM_POINTER 0x1109 -#endif -#ifdef CL_VERSION_3_0 -#define CL_MEM_PROPERTIES 0x110A -#endif - -/* cl_image_info */ -#define CL_IMAGE_FORMAT 0x1110 -#define CL_IMAGE_ELEMENT_SIZE 0x1111 -#define CL_IMAGE_ROW_PITCH 0x1112 -#define CL_IMAGE_SLICE_PITCH 0x1113 -#define CL_IMAGE_WIDTH 0x1114 -#define CL_IMAGE_HEIGHT 0x1115 -#define CL_IMAGE_DEPTH 0x1116 -#ifdef CL_VERSION_1_2 -#define CL_IMAGE_ARRAY_SIZE 0x1117 -#define CL_IMAGE_BUFFER 0x1118 -#define CL_IMAGE_NUM_MIP_LEVELS 0x1119 -#define CL_IMAGE_NUM_SAMPLES 0x111A -#endif - - -/* cl_pipe_info */ -#ifdef CL_VERSION_2_0 -#define CL_PIPE_PACKET_SIZE 0x1120 -#define CL_PIPE_MAX_PACKETS 0x1121 -#endif -#ifdef CL_VERSION_3_0 -#define CL_PIPE_PROPERTIES 0x1122 -#endif - -/* cl_addressing_mode */ -#define CL_ADDRESS_NONE 0x1130 -#define CL_ADDRESS_CLAMP_TO_EDGE 0x1131 -#define CL_ADDRESS_CLAMP 0x1132 -#define CL_ADDRESS_REPEAT 0x1133 -#ifdef CL_VERSION_1_1 -#define CL_ADDRESS_MIRRORED_REPEAT 0x1134 -#endif - -/* cl_filter_mode */ -#define CL_FILTER_NEAREST 0x1140 -#define CL_FILTER_LINEAR 0x1141 - -/* cl_sampler_info */ -#define CL_SAMPLER_REFERENCE_COUNT 0x1150 -#define CL_SAMPLER_CONTEXT 0x1151 -#define CL_SAMPLER_NORMALIZED_COORDS 0x1152 -#define CL_SAMPLER_ADDRESSING_MODE 0x1153 -#define CL_SAMPLER_FILTER_MODE 0x1154 -#ifdef CL_VERSION_2_0 -/* These enumerants are for the cl_khr_mipmap_image extension. - They have since been added to cl_ext.h with an appropriate - KHR suffix, but are left here for backwards compatibility. */ -#define CL_SAMPLER_MIP_FILTER_MODE 0x1155 -#define CL_SAMPLER_LOD_MIN 0x1156 -#define CL_SAMPLER_LOD_MAX 0x1157 -#endif -#ifdef CL_VERSION_3_0 -#define CL_SAMPLER_PROPERTIES 0x1158 -#endif - -/* cl_map_flags - bitfield */ -#define CL_MAP_READ (1 << 0) -#define CL_MAP_WRITE (1 << 1) -#ifdef CL_VERSION_1_2 -#define CL_MAP_WRITE_INVALIDATE_REGION (1 << 2) -#endif - -/* cl_program_info */ -#define CL_PROGRAM_REFERENCE_COUNT 0x1160 -#define CL_PROGRAM_CONTEXT 0x1161 -#define CL_PROGRAM_NUM_DEVICES 0x1162 -#define CL_PROGRAM_DEVICES 0x1163 -#define CL_PROGRAM_SOURCE 0x1164 -#define CL_PROGRAM_BINARY_SIZES 0x1165 -#define CL_PROGRAM_BINARIES 0x1166 -#ifdef CL_VERSION_1_2 -#define CL_PROGRAM_NUM_KERNELS 0x1167 -#define CL_PROGRAM_KERNEL_NAMES 0x1168 -#endif -#ifdef CL_VERSION_2_1 -#define CL_PROGRAM_IL 0x1169 -#endif -#ifdef CL_VERSION_2_2 -#define CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT 0x116A -#define CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT 0x116B -#endif - -/* cl_program_build_info */ -#define CL_PROGRAM_BUILD_STATUS 0x1181 -#define CL_PROGRAM_BUILD_OPTIONS 0x1182 -#define CL_PROGRAM_BUILD_LOG 0x1183 -#ifdef CL_VERSION_1_2 -#define CL_PROGRAM_BINARY_TYPE 0x1184 -#endif -#ifdef CL_VERSION_2_0 -#define CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE 0x1185 -#endif - -#ifdef CL_VERSION_1_2 - -/* cl_program_binary_type */ -#define CL_PROGRAM_BINARY_TYPE_NONE 0x0 -#define CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT 0x1 -#define CL_PROGRAM_BINARY_TYPE_LIBRARY 0x2 -#define CL_PROGRAM_BINARY_TYPE_EXECUTABLE 0x4 - -#endif - -/* cl_build_status */ -#define CL_BUILD_SUCCESS 0 -#define CL_BUILD_NONE -1 -#define CL_BUILD_ERROR -2 -#define CL_BUILD_IN_PROGRESS -3 - -/* cl_kernel_info */ -#define CL_KERNEL_FUNCTION_NAME 0x1190 -#define CL_KERNEL_NUM_ARGS 0x1191 -#define CL_KERNEL_REFERENCE_COUNT 0x1192 -#define CL_KERNEL_CONTEXT 0x1193 -#define CL_KERNEL_PROGRAM 0x1194 -#ifdef CL_VERSION_1_2 -#define CL_KERNEL_ATTRIBUTES 0x1195 -#endif - -#ifdef CL_VERSION_1_2 - -/* cl_kernel_arg_info */ -#define CL_KERNEL_ARG_ADDRESS_QUALIFIER 0x1196 -#define CL_KERNEL_ARG_ACCESS_QUALIFIER 0x1197 -#define CL_KERNEL_ARG_TYPE_NAME 0x1198 -#define CL_KERNEL_ARG_TYPE_QUALIFIER 0x1199 -#define CL_KERNEL_ARG_NAME 0x119A - -#endif - -#ifdef CL_VERSION_1_2 - -/* cl_kernel_arg_address_qualifier */ -#define CL_KERNEL_ARG_ADDRESS_GLOBAL 0x119B -#define CL_KERNEL_ARG_ADDRESS_LOCAL 0x119C -#define CL_KERNEL_ARG_ADDRESS_CONSTANT 0x119D -#define CL_KERNEL_ARG_ADDRESS_PRIVATE 0x119E - -#endif - -#ifdef CL_VERSION_1_2 - -/* cl_kernel_arg_access_qualifier */ -#define CL_KERNEL_ARG_ACCESS_READ_ONLY 0x11A0 -#define CL_KERNEL_ARG_ACCESS_WRITE_ONLY 0x11A1 -#define CL_KERNEL_ARG_ACCESS_READ_WRITE 0x11A2 -#define CL_KERNEL_ARG_ACCESS_NONE 0x11A3 - -#endif - -#ifdef CL_VERSION_1_2 - -/* cl_kernel_arg_type_qualifier */ -#define CL_KERNEL_ARG_TYPE_NONE 0 -#define CL_KERNEL_ARG_TYPE_CONST (1 << 0) -#define CL_KERNEL_ARG_TYPE_RESTRICT (1 << 1) -#define CL_KERNEL_ARG_TYPE_VOLATILE (1 << 2) -#ifdef CL_VERSION_2_0 -#define CL_KERNEL_ARG_TYPE_PIPE (1 << 3) -#endif - -#endif - -/* cl_kernel_work_group_info */ -#define CL_KERNEL_WORK_GROUP_SIZE 0x11B0 -#define CL_KERNEL_COMPILE_WORK_GROUP_SIZE 0x11B1 -#define CL_KERNEL_LOCAL_MEM_SIZE 0x11B2 -#define CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE 0x11B3 -#define CL_KERNEL_PRIVATE_MEM_SIZE 0x11B4 -#ifdef CL_VERSION_1_2 -#define CL_KERNEL_GLOBAL_WORK_SIZE 0x11B5 -#endif - -#ifdef CL_VERSION_2_1 - -/* cl_kernel_sub_group_info */ -#define CL_KERNEL_MAX_SUB_GROUP_SIZE_FOR_NDRANGE 0x2033 -#define CL_KERNEL_SUB_GROUP_COUNT_FOR_NDRANGE 0x2034 -#define CL_KERNEL_LOCAL_SIZE_FOR_SUB_GROUP_COUNT 0x11B8 -#define CL_KERNEL_MAX_NUM_SUB_GROUPS 0x11B9 -#define CL_KERNEL_COMPILE_NUM_SUB_GROUPS 0x11BA - -#endif - -#ifdef CL_VERSION_2_0 - -/* cl_kernel_exec_info */ -#define CL_KERNEL_EXEC_INFO_SVM_PTRS 0x11B6 -#define CL_KERNEL_EXEC_INFO_SVM_FINE_GRAIN_SYSTEM 0x11B7 - -#endif - -/* cl_event_info */ -#define CL_EVENT_COMMAND_QUEUE 0x11D0 -#define CL_EVENT_COMMAND_TYPE 0x11D1 -#define CL_EVENT_REFERENCE_COUNT 0x11D2 -#define CL_EVENT_COMMAND_EXECUTION_STATUS 0x11D3 -#ifdef CL_VERSION_1_1 -#define CL_EVENT_CONTEXT 0x11D4 -#endif - -/* cl_command_type */ -#define CL_COMMAND_NDRANGE_KERNEL 0x11F0 -#define CL_COMMAND_TASK 0x11F1 -#define CL_COMMAND_NATIVE_KERNEL 0x11F2 -#define CL_COMMAND_READ_BUFFER 0x11F3 -#define CL_COMMAND_WRITE_BUFFER 0x11F4 -#define CL_COMMAND_COPY_BUFFER 0x11F5 -#define CL_COMMAND_READ_IMAGE 0x11F6 -#define CL_COMMAND_WRITE_IMAGE 0x11F7 -#define CL_COMMAND_COPY_IMAGE 0x11F8 -#define CL_COMMAND_COPY_IMAGE_TO_BUFFER 0x11F9 -#define CL_COMMAND_COPY_BUFFER_TO_IMAGE 0x11FA -#define CL_COMMAND_MAP_BUFFER 0x11FB -#define CL_COMMAND_MAP_IMAGE 0x11FC -#define CL_COMMAND_UNMAP_MEM_OBJECT 0x11FD -#define CL_COMMAND_MARKER 0x11FE -#define CL_COMMAND_ACQUIRE_GL_OBJECTS 0x11FF -#define CL_COMMAND_RELEASE_GL_OBJECTS 0x1200 -#ifdef CL_VERSION_1_1 -#define CL_COMMAND_READ_BUFFER_RECT 0x1201 -#define CL_COMMAND_WRITE_BUFFER_RECT 0x1202 -#define CL_COMMAND_COPY_BUFFER_RECT 0x1203 -#define CL_COMMAND_USER 0x1204 -#endif -#ifdef CL_VERSION_1_2 -#define CL_COMMAND_BARRIER 0x1205 -#define CL_COMMAND_MIGRATE_MEM_OBJECTS 0x1206 -#define CL_COMMAND_FILL_BUFFER 0x1207 -#define CL_COMMAND_FILL_IMAGE 0x1208 -#endif -#ifdef CL_VERSION_2_0 -#define CL_COMMAND_SVM_FREE 0x1209 -#define CL_COMMAND_SVM_MEMCPY 0x120A -#define CL_COMMAND_SVM_MEMFILL 0x120B -#define CL_COMMAND_SVM_MAP 0x120C -#define CL_COMMAND_SVM_UNMAP 0x120D -#endif -#ifdef CL_VERSION_3_0 -#define CL_COMMAND_SVM_MIGRATE_MEM 0x120E -#endif - -/* command execution status */ -#define CL_COMPLETE 0x0 -#define CL_RUNNING 0x1 -#define CL_SUBMITTED 0x2 -#define CL_QUEUED 0x3 - -/* cl_buffer_create_type */ -#ifdef CL_VERSION_1_1 -#define CL_BUFFER_CREATE_TYPE_REGION 0x1220 -#endif - -/* cl_profiling_info */ -#define CL_PROFILING_COMMAND_QUEUED 0x1280 -#define CL_PROFILING_COMMAND_SUBMIT 0x1281 -#define CL_PROFILING_COMMAND_START 0x1282 -#define CL_PROFILING_COMMAND_END 0x1283 -#ifdef CL_VERSION_2_0 -#define CL_PROFILING_COMMAND_COMPLETE 0x1284 -#endif - -/* cl_device_atomic_capabilities - bitfield */ -#ifdef CL_VERSION_3_0 -#define CL_DEVICE_ATOMIC_ORDER_RELAXED (1 << 0) -#define CL_DEVICE_ATOMIC_ORDER_ACQ_REL (1 << 1) -#define CL_DEVICE_ATOMIC_ORDER_SEQ_CST (1 << 2) -#define CL_DEVICE_ATOMIC_SCOPE_WORK_ITEM (1 << 3) -#define CL_DEVICE_ATOMIC_SCOPE_WORK_GROUP (1 << 4) -#define CL_DEVICE_ATOMIC_SCOPE_DEVICE (1 << 5) -#define CL_DEVICE_ATOMIC_SCOPE_ALL_DEVICES (1 << 6) -#endif - -/* cl_khronos_vendor_id */ -#define CL_KHRONOS_VENDOR_ID_CODEPLAY 0x10004 - -#ifdef CL_VERSION_3_0 - -/* cl_version */ -#define CL_VERSION_MAJOR_BITS (10) -#define CL_VERSION_MINOR_BITS (10) -#define CL_VERSION_PATCH_BITS (12) - -#define CL_VERSION_MAJOR_MASK ((1 << CL_VERSION_MAJOR_BITS) - 1) -#define CL_VERSION_MINOR_MASK ((1 << CL_VERSION_MINOR_BITS) - 1) -#define CL_VERSION_PATCH_MASK ((1 << CL_VERSION_PATCH_BITS) - 1) - -#define CL_VERSION_MAJOR(version) \ - ((version) >> (CL_VERSION_MINOR_BITS + CL_VERSION_PATCH_BITS)) - -#define CL_VERSION_MINOR(version) \ - (((version) >> CL_VERSION_PATCH_BITS) & CL_VERSION_MINOR_MASK) - -#define CL_VERSION_PATCH(version) ((version) & CL_VERSION_PATCH_MASK) - -#define CL_MAKE_VERSION(major, minor, patch) \ - ((((major) & CL_VERSION_MAJOR_MASK) \ - << (CL_VERSION_MINOR_BITS + CL_VERSION_PATCH_BITS)) | \ - (((minor) & CL_VERSION_MINOR_MASK) << CL_VERSION_PATCH_BITS) | \ - ((patch) & CL_VERSION_PATCH_MASK)) - -#endif - -/********************************************************************************************************/ - -/* Platform API */ -extern CL_API_ENTRY cl_int CL_API_CALL -clGetPlatformIDs(cl_uint num_entries, - cl_platform_id * platforms, - cl_uint * num_platforms) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetPlatformInfo(cl_platform_id platform, - cl_platform_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -/* Device APIs */ -extern CL_API_ENTRY cl_int CL_API_CALL -clGetDeviceIDs(cl_platform_id platform, - cl_device_type device_type, - cl_uint num_entries, - cl_device_id * devices, - cl_uint * num_devices) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetDeviceInfo(cl_device_id device, - cl_device_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_2 - -extern CL_API_ENTRY cl_int CL_API_CALL -clCreateSubDevices(cl_device_id in_device, - const cl_device_partition_property * properties, - cl_uint num_devices, - cl_device_id * out_devices, - cl_uint * num_devices_ret) CL_API_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clRetainDevice(cl_device_id device) CL_API_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clReleaseDevice(cl_device_id device) CL_API_SUFFIX__VERSION_1_2; - -#endif - -#ifdef CL_VERSION_2_1 - -extern CL_API_ENTRY cl_int CL_API_CALL -clSetDefaultDeviceCommandQueue(cl_context context, - cl_device_id device, - cl_command_queue command_queue) CL_API_SUFFIX__VERSION_2_1; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetDeviceAndHostTimer(cl_device_id device, - cl_ulong* device_timestamp, - cl_ulong* host_timestamp) CL_API_SUFFIX__VERSION_2_1; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetHostTimer(cl_device_id device, - cl_ulong * host_timestamp) CL_API_SUFFIX__VERSION_2_1; - -#endif - -/* Context APIs */ -extern CL_API_ENTRY cl_context CL_API_CALL -clCreateContext(const cl_context_properties * properties, - cl_uint num_devices, - const cl_device_id * devices, - void (CL_CALLBACK * pfn_notify)(const char * errinfo, - const void * private_info, - size_t cb, - void * user_data), - void * user_data, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_context CL_API_CALL -clCreateContextFromType(const cl_context_properties * properties, - cl_device_type device_type, - void (CL_CALLBACK * pfn_notify)(const char * errinfo, - const void * private_info, - size_t cb, - void * user_data), - void * user_data, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clRetainContext(cl_context context) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clReleaseContext(cl_context context) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetContextInfo(cl_context context, - cl_context_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -/* Command Queue APIs */ - -#ifdef CL_VERSION_2_0 - -extern CL_API_ENTRY cl_command_queue CL_API_CALL -clCreateCommandQueueWithProperties(cl_context context, - cl_device_id device, - const cl_queue_properties * properties, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_2_0; - -#endif - -extern CL_API_ENTRY cl_int CL_API_CALL -clRetainCommandQueue(cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clReleaseCommandQueue(cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetCommandQueueInfo(cl_command_queue command_queue, - cl_command_queue_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -/* Memory Object APIs */ -extern CL_API_ENTRY cl_mem CL_API_CALL -clCreateBuffer(cl_context context, - cl_mem_flags flags, - size_t size, - void * host_ptr, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_1 - -extern CL_API_ENTRY cl_mem CL_API_CALL -clCreateSubBuffer(cl_mem buffer, - cl_mem_flags flags, - cl_buffer_create_type buffer_create_type, - const void * buffer_create_info, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_1; - -#endif - -#ifdef CL_VERSION_1_2 - -extern CL_API_ENTRY cl_mem CL_API_CALL -clCreateImage(cl_context context, - cl_mem_flags flags, - const cl_image_format * image_format, - const cl_image_desc * image_desc, - void * host_ptr, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_2; - -#endif - -#ifdef CL_VERSION_2_0 - -extern CL_API_ENTRY cl_mem CL_API_CALL -clCreatePipe(cl_context context, - cl_mem_flags flags, - cl_uint pipe_packet_size, - cl_uint pipe_max_packets, - const cl_pipe_properties * properties, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_2_0; - -#endif - -#ifdef CL_VERSION_3_0 - -extern CL_API_ENTRY cl_mem CL_API_CALL -clCreateBufferWithProperties(cl_context context, - const cl_mem_properties * properties, - cl_mem_flags flags, - size_t size, - void * host_ptr, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_3_0; - -extern CL_API_ENTRY cl_mem CL_API_CALL -clCreateImageWithProperties(cl_context context, - const cl_mem_properties * properties, - cl_mem_flags flags, - const cl_image_format * image_format, - const cl_image_desc * image_desc, - void * host_ptr, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_3_0; - -#endif - -extern CL_API_ENTRY cl_int CL_API_CALL -clRetainMemObject(cl_mem memobj) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clReleaseMemObject(cl_mem memobj) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetSupportedImageFormats(cl_context context, - cl_mem_flags flags, - cl_mem_object_type image_type, - cl_uint num_entries, - cl_image_format * image_formats, - cl_uint * num_image_formats) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetMemObjectInfo(cl_mem memobj, - cl_mem_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetImageInfo(cl_mem image, - cl_image_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_2_0 - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetPipeInfo(cl_mem pipe, - cl_pipe_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_2_0; - -#endif - -#ifdef CL_VERSION_1_1 - -extern CL_API_ENTRY cl_int CL_API_CALL -clSetMemObjectDestructorCallback(cl_mem memobj, - void (CL_CALLBACK * pfn_notify)(cl_mem memobj, - void * user_data), - void * user_data) CL_API_SUFFIX__VERSION_1_1; - -#endif - -/* SVM Allocation APIs */ - -#ifdef CL_VERSION_2_0 - -extern CL_API_ENTRY void * CL_API_CALL -clSVMAlloc(cl_context context, - cl_svm_mem_flags flags, - size_t size, - cl_uint alignment) CL_API_SUFFIX__VERSION_2_0; - -extern CL_API_ENTRY void CL_API_CALL -clSVMFree(cl_context context, - void * svm_pointer) CL_API_SUFFIX__VERSION_2_0; - -#endif - -/* Sampler APIs */ - -#ifdef CL_VERSION_2_0 - -extern CL_API_ENTRY cl_sampler CL_API_CALL -clCreateSamplerWithProperties(cl_context context, - const cl_sampler_properties * sampler_properties, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_2_0; - -#endif - -extern CL_API_ENTRY cl_int CL_API_CALL -clRetainSampler(cl_sampler sampler) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clReleaseSampler(cl_sampler sampler) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetSamplerInfo(cl_sampler sampler, - cl_sampler_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -/* Program Object APIs */ -extern CL_API_ENTRY cl_program CL_API_CALL -clCreateProgramWithSource(cl_context context, - cl_uint count, - const char ** strings, - const size_t * lengths, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_program CL_API_CALL -clCreateProgramWithBinary(cl_context context, - cl_uint num_devices, - const cl_device_id * device_list, - const size_t * lengths, - const unsigned char ** binaries, - cl_int * binary_status, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_2 - -extern CL_API_ENTRY cl_program CL_API_CALL -clCreateProgramWithBuiltInKernels(cl_context context, - cl_uint num_devices, - const cl_device_id * device_list, - const char * kernel_names, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_2; - -#endif - -#ifdef CL_VERSION_2_1 - -extern CL_API_ENTRY cl_program CL_API_CALL -clCreateProgramWithIL(cl_context context, - const void* il, - size_t length, - cl_int* errcode_ret) CL_API_SUFFIX__VERSION_2_1; - -#endif - -extern CL_API_ENTRY cl_int CL_API_CALL -clRetainProgram(cl_program program) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clReleaseProgram(cl_program program) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clBuildProgram(cl_program program, - cl_uint num_devices, - const cl_device_id * device_list, - const char * options, - void (CL_CALLBACK * pfn_notify)(cl_program program, - void * user_data), - void * user_data) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_2 - -extern CL_API_ENTRY cl_int CL_API_CALL -clCompileProgram(cl_program program, - cl_uint num_devices, - const cl_device_id * device_list, - const char * options, - cl_uint num_input_headers, - const cl_program * input_headers, - const char ** header_include_names, - void (CL_CALLBACK * pfn_notify)(cl_program program, - void * user_data), - void * user_data) CL_API_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_program CL_API_CALL -clLinkProgram(cl_context context, - cl_uint num_devices, - const cl_device_id * device_list, - const char * options, - cl_uint num_input_programs, - const cl_program * input_programs, - void (CL_CALLBACK * pfn_notify)(cl_program program, - void * user_data), - void * user_data, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_2; - -#endif - -#ifdef CL_VERSION_2_2 - -extern CL_API_ENTRY cl_int CL_API_CALL -clSetProgramReleaseCallback(cl_program program, - void (CL_CALLBACK * pfn_notify)(cl_program program, - void * user_data), - void * user_data) CL_API_SUFFIX__VERSION_2_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clSetProgramSpecializationConstant(cl_program program, - cl_uint spec_id, - size_t spec_size, - const void* spec_value) CL_API_SUFFIX__VERSION_2_2; - -#endif - -#ifdef CL_VERSION_1_2 - -extern CL_API_ENTRY cl_int CL_API_CALL -clUnloadPlatformCompiler(cl_platform_id platform) CL_API_SUFFIX__VERSION_1_2; - -#endif - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetProgramInfo(cl_program program, - cl_program_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetProgramBuildInfo(cl_program program, - cl_device_id device, - cl_program_build_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -/* Kernel Object APIs */ -extern CL_API_ENTRY cl_kernel CL_API_CALL -clCreateKernel(cl_program program, - const char * kernel_name, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clCreateKernelsInProgram(cl_program program, - cl_uint num_kernels, - cl_kernel * kernels, - cl_uint * num_kernels_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_2_1 - -extern CL_API_ENTRY cl_kernel CL_API_CALL -clCloneKernel(cl_kernel source_kernel, - cl_int* errcode_ret) CL_API_SUFFIX__VERSION_2_1; - -#endif - -extern CL_API_ENTRY cl_int CL_API_CALL -clRetainKernel(cl_kernel kernel) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clReleaseKernel(cl_kernel kernel) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clSetKernelArg(cl_kernel kernel, - cl_uint arg_index, - size_t arg_size, - const void * arg_value) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_2_0 - -extern CL_API_ENTRY cl_int CL_API_CALL -clSetKernelArgSVMPointer(cl_kernel kernel, - cl_uint arg_index, - const void * arg_value) CL_API_SUFFIX__VERSION_2_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clSetKernelExecInfo(cl_kernel kernel, - cl_kernel_exec_info param_name, - size_t param_value_size, - const void * param_value) CL_API_SUFFIX__VERSION_2_0; - -#endif - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetKernelInfo(cl_kernel kernel, - cl_kernel_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_2 - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetKernelArgInfo(cl_kernel kernel, - cl_uint arg_indx, - cl_kernel_arg_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_2; - -#endif - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetKernelWorkGroupInfo(cl_kernel kernel, - cl_device_id device, - cl_kernel_work_group_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_2_1 - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetKernelSubGroupInfo(cl_kernel kernel, - cl_device_id device, - cl_kernel_sub_group_info param_name, - size_t input_value_size, - const void* input_value, - size_t param_value_size, - void* param_value, - size_t* param_value_size_ret) CL_API_SUFFIX__VERSION_2_1; - -#endif - -/* Event Object APIs */ -extern CL_API_ENTRY cl_int CL_API_CALL -clWaitForEvents(cl_uint num_events, - const cl_event * event_list) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetEventInfo(cl_event event, - cl_event_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_1 - -extern CL_API_ENTRY cl_event CL_API_CALL -clCreateUserEvent(cl_context context, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_1; - -#endif - -extern CL_API_ENTRY cl_int CL_API_CALL -clRetainEvent(cl_event event) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clReleaseEvent(cl_event event) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_1 - -extern CL_API_ENTRY cl_int CL_API_CALL -clSetUserEventStatus(cl_event event, - cl_int execution_status) CL_API_SUFFIX__VERSION_1_1; - -extern CL_API_ENTRY cl_int CL_API_CALL -clSetEventCallback(cl_event event, - cl_int command_exec_callback_type, - void (CL_CALLBACK * pfn_notify)(cl_event event, - cl_int event_command_status, - void * user_data), - void * user_data) CL_API_SUFFIX__VERSION_1_1; - -#endif - -/* Profiling APIs */ -extern CL_API_ENTRY cl_int CL_API_CALL -clGetEventProfilingInfo(cl_event event, - cl_profiling_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -/* Flush and Finish APIs */ -extern CL_API_ENTRY cl_int CL_API_CALL -clFlush(cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clFinish(cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0; - -/* Enqueued Commands APIs */ -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueReadBuffer(cl_command_queue command_queue, - cl_mem buffer, - cl_bool blocking_read, - size_t offset, - size_t size, - void * ptr, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_1 - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueReadBufferRect(cl_command_queue command_queue, - cl_mem buffer, - cl_bool blocking_read, - const size_t * buffer_offset, - const size_t * host_offset, - const size_t * region, - size_t buffer_row_pitch, - size_t buffer_slice_pitch, - size_t host_row_pitch, - size_t host_slice_pitch, - void * ptr, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_1; - -#endif - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueWriteBuffer(cl_command_queue command_queue, - cl_mem buffer, - cl_bool blocking_write, - size_t offset, - size_t size, - const void * ptr, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_1 - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueWriteBufferRect(cl_command_queue command_queue, - cl_mem buffer, - cl_bool blocking_write, - const size_t * buffer_offset, - const size_t * host_offset, - const size_t * region, - size_t buffer_row_pitch, - size_t buffer_slice_pitch, - size_t host_row_pitch, - size_t host_slice_pitch, - const void * ptr, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_1; - -#endif - -#ifdef CL_VERSION_1_2 - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueFillBuffer(cl_command_queue command_queue, - cl_mem buffer, - const void * pattern, - size_t pattern_size, - size_t offset, - size_t size, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_2; - -#endif - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueCopyBuffer(cl_command_queue command_queue, - cl_mem src_buffer, - cl_mem dst_buffer, - size_t src_offset, - size_t dst_offset, - size_t size, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_1 - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueCopyBufferRect(cl_command_queue command_queue, - cl_mem src_buffer, - cl_mem dst_buffer, - const size_t * src_origin, - const size_t * dst_origin, - const size_t * region, - size_t src_row_pitch, - size_t src_slice_pitch, - size_t dst_row_pitch, - size_t dst_slice_pitch, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_1; - -#endif - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueReadImage(cl_command_queue command_queue, - cl_mem image, - cl_bool blocking_read, - const size_t * origin, - const size_t * region, - size_t row_pitch, - size_t slice_pitch, - void * ptr, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueWriteImage(cl_command_queue command_queue, - cl_mem image, - cl_bool blocking_write, - const size_t * origin, - const size_t * region, - size_t input_row_pitch, - size_t input_slice_pitch, - const void * ptr, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_2 - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueFillImage(cl_command_queue command_queue, - cl_mem image, - const void * fill_color, - const size_t * origin, - const size_t * region, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_2; - -#endif - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueCopyImage(cl_command_queue command_queue, - cl_mem src_image, - cl_mem dst_image, - const size_t * src_origin, - const size_t * dst_origin, - const size_t * region, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueCopyImageToBuffer(cl_command_queue command_queue, - cl_mem src_image, - cl_mem dst_buffer, - const size_t * src_origin, - const size_t * region, - size_t dst_offset, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueCopyBufferToImage(cl_command_queue command_queue, - cl_mem src_buffer, - cl_mem dst_image, - size_t src_offset, - const size_t * dst_origin, - const size_t * region, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY void * CL_API_CALL -clEnqueueMapBuffer(cl_command_queue command_queue, - cl_mem buffer, - cl_bool blocking_map, - cl_map_flags map_flags, - size_t offset, - size_t size, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY void * CL_API_CALL -clEnqueueMapImage(cl_command_queue command_queue, - cl_mem image, - cl_bool blocking_map, - cl_map_flags map_flags, - const size_t * origin, - const size_t * region, - size_t * image_row_pitch, - size_t * image_slice_pitch, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueUnmapMemObject(cl_command_queue command_queue, - cl_mem memobj, - void * mapped_ptr, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_2 - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueMigrateMemObjects(cl_command_queue command_queue, - cl_uint num_mem_objects, - const cl_mem * mem_objects, - cl_mem_migration_flags flags, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_2; - -#endif - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueNDRangeKernel(cl_command_queue command_queue, - cl_kernel kernel, - cl_uint work_dim, - const size_t * global_work_offset, - const size_t * global_work_size, - const size_t * local_work_size, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueNativeKernel(cl_command_queue command_queue, - void (CL_CALLBACK * user_func)(void *), - void * args, - size_t cb_args, - cl_uint num_mem_objects, - const cl_mem * mem_list, - const void ** args_mem_loc, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_2 - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueMarkerWithWaitList(cl_command_queue command_queue, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueBarrierWithWaitList(cl_command_queue command_queue, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_2; - -#endif - -#ifdef CL_VERSION_2_0 - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueSVMFree(cl_command_queue command_queue, - cl_uint num_svm_pointers, - void * svm_pointers[], - void (CL_CALLBACK * pfn_free_func)(cl_command_queue queue, - cl_uint num_svm_pointers, - void * svm_pointers[], - void * user_data), - void * user_data, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_2_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueSVMMemcpy(cl_command_queue command_queue, - cl_bool blocking_copy, - void * dst_ptr, - const void * src_ptr, - size_t size, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_2_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueSVMMemFill(cl_command_queue command_queue, - void * svm_ptr, - const void * pattern, - size_t pattern_size, - size_t size, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_2_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueSVMMap(cl_command_queue command_queue, - cl_bool blocking_map, - cl_map_flags flags, - void * svm_ptr, - size_t size, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_2_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueSVMUnmap(cl_command_queue command_queue, - void * svm_ptr, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_2_0; - -#endif - -#ifdef CL_VERSION_2_1 - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueSVMMigrateMem(cl_command_queue command_queue, - cl_uint num_svm_pointers, - const void ** svm_pointers, - const size_t * sizes, - cl_mem_migration_flags flags, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_2_1; - -#endif - -#ifdef CL_VERSION_1_2 - -/* Extension function access - * - * Returns the extension function address for the given function name, - * or NULL if a valid function can not be found. The client must - * check to make sure the address is not NULL, before using or - * calling the returned function address. - */ -extern CL_API_ENTRY void * CL_API_CALL -clGetExtensionFunctionAddressForPlatform(cl_platform_id platform, - const char * func_name) CL_API_SUFFIX__VERSION_1_2; - -#endif - -#ifdef CL_USE_DEPRECATED_OPENCL_1_0_APIS - /* - * WARNING: - * This API introduces mutable state into the OpenCL implementation. It has been REMOVED - * to better facilitate thread safety. The 1.0 API is not thread safe. It is not tested by the - * OpenCL 1.1 conformance test, and consequently may not work or may not work dependably. - * It is likely to be non-performant. Use of this API is not advised. Use at your own risk. - * - * Software developers previously relying on this API are instructed to set the command queue - * properties when creating the queue, instead. - */ - extern CL_API_ENTRY cl_int CL_API_CALL - clSetCommandQueueProperty(cl_command_queue command_queue, - cl_command_queue_properties properties, - cl_bool enable, - cl_command_queue_properties * old_properties) CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED; -#endif /* CL_USE_DEPRECATED_OPENCL_1_0_APIS */ - -/* Deprecated OpenCL 1.1 APIs */ -extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_mem CL_API_CALL -clCreateImage2D(cl_context context, - cl_mem_flags flags, - const cl_image_format * image_format, - size_t image_width, - size_t image_height, - size_t image_row_pitch, - void * host_ptr, - cl_int * errcode_ret) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; - -extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_mem CL_API_CALL -clCreateImage3D(cl_context context, - cl_mem_flags flags, - const cl_image_format * image_format, - size_t image_width, - size_t image_height, - size_t image_depth, - size_t image_row_pitch, - size_t image_slice_pitch, - void * host_ptr, - cl_int * errcode_ret) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; - -extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL -clEnqueueMarker(cl_command_queue command_queue, - cl_event * event) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; - -extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL -clEnqueueWaitForEvents(cl_command_queue command_queue, - cl_uint num_events, - const cl_event * event_list) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; - -extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL -clEnqueueBarrier(cl_command_queue command_queue) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; - -extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL -clUnloadCompiler(void) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; - -extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED void * CL_API_CALL -clGetExtensionFunctionAddress(const char * func_name) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; - -/* Deprecated OpenCL 2.0 APIs */ -extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_2_DEPRECATED cl_command_queue CL_API_CALL -clCreateCommandQueue(cl_context context, - cl_device_id device, - cl_command_queue_properties properties, - cl_int * errcode_ret) CL_EXT_SUFFIX__VERSION_1_2_DEPRECATED; - -extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_2_DEPRECATED cl_sampler CL_API_CALL -clCreateSampler(cl_context context, - cl_bool normalized_coords, - cl_addressing_mode addressing_mode, - cl_filter_mode filter_mode, - cl_int * errcode_ret) CL_EXT_SUFFIX__VERSION_1_2_DEPRECATED; - -extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_2_DEPRECATED cl_int CL_API_CALL -clEnqueueTask(cl_command_queue command_queue, - cl_kernel kernel, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_EXT_SUFFIX__VERSION_1_2_DEPRECATED; - -#ifdef __cplusplus -} -#endif - -#endif /* __OPENCL_CL_H */ diff --git a/src/CL/cl_d3d10.h b/src/CL/cl_d3d10.h deleted file mode 100644 index cda5469..0000000 --- a/src/CL/cl_d3d10.h +++ /dev/null @@ -1,117 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008-2020 The Khronos Group Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -#ifndef __OPENCL_CL_D3D10_H -#define __OPENCL_CL_D3D10_H - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/****************************************************************************** - * cl_khr_d3d10_sharing */ -#define cl_khr_d3d10_sharing 1 - -typedef cl_uint cl_d3d10_device_source_khr; -typedef cl_uint cl_d3d10_device_set_khr; - -/******************************************************************************/ - -/* Error Codes */ -#define CL_INVALID_D3D10_DEVICE_KHR -1002 -#define CL_INVALID_D3D10_RESOURCE_KHR -1003 -#define CL_D3D10_RESOURCE_ALREADY_ACQUIRED_KHR -1004 -#define CL_D3D10_RESOURCE_NOT_ACQUIRED_KHR -1005 - -/* cl_d3d10_device_source_nv */ -#define CL_D3D10_DEVICE_KHR 0x4010 -#define CL_D3D10_DXGI_ADAPTER_KHR 0x4011 - -/* cl_d3d10_device_set_nv */ -#define CL_PREFERRED_DEVICES_FOR_D3D10_KHR 0x4012 -#define CL_ALL_DEVICES_FOR_D3D10_KHR 0x4013 - -/* cl_context_info */ -#define CL_CONTEXT_D3D10_DEVICE_KHR 0x4014 -#define CL_CONTEXT_D3D10_PREFER_SHARED_RESOURCES_KHR 0x402C - -/* cl_mem_info */ -#define CL_MEM_D3D10_RESOURCE_KHR 0x4015 - -/* cl_image_info */ -#define CL_IMAGE_D3D10_SUBRESOURCE_KHR 0x4016 - -/* cl_command_type */ -#define CL_COMMAND_ACQUIRE_D3D10_OBJECTS_KHR 0x4017 -#define CL_COMMAND_RELEASE_D3D10_OBJECTS_KHR 0x4018 - -/******************************************************************************/ - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clGetDeviceIDsFromD3D10KHR_fn)( - cl_platform_id platform, - cl_d3d10_device_source_khr d3d_device_source, - void * d3d_object, - cl_d3d10_device_set_khr d3d_device_set, - cl_uint num_entries, - cl_device_id * devices, - cl_uint * num_devices) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_mem (CL_API_CALL *clCreateFromD3D10BufferKHR_fn)( - cl_context context, - cl_mem_flags flags, - ID3D10Buffer * resource, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_mem (CL_API_CALL *clCreateFromD3D10Texture2DKHR_fn)( - cl_context context, - cl_mem_flags flags, - ID3D10Texture2D * resource, - UINT subresource, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_mem (CL_API_CALL *clCreateFromD3D10Texture3DKHR_fn)( - cl_context context, - cl_mem_flags flags, - ID3D10Texture3D * resource, - UINT subresource, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clEnqueueAcquireD3D10ObjectsKHR_fn)( - cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem * mem_objects, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clEnqueueReleaseD3D10ObjectsKHR_fn)( - cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem * mem_objects, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - -#ifdef __cplusplus -} -#endif - -#endif /* __OPENCL_CL_D3D10_H */ - diff --git a/src/CL/cl_d3d11.h b/src/CL/cl_d3d11.h deleted file mode 100644 index 6b7e2e9..0000000 --- a/src/CL/cl_d3d11.h +++ /dev/null @@ -1,117 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008-2020 The Khronos Group Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -#ifndef __OPENCL_CL_D3D11_H -#define __OPENCL_CL_D3D11_H - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/****************************************************************************** - * cl_khr_d3d11_sharing */ -#define cl_khr_d3d11_sharing 1 - -typedef cl_uint cl_d3d11_device_source_khr; -typedef cl_uint cl_d3d11_device_set_khr; - -/******************************************************************************/ - -/* Error Codes */ -#define CL_INVALID_D3D11_DEVICE_KHR -1006 -#define CL_INVALID_D3D11_RESOURCE_KHR -1007 -#define CL_D3D11_RESOURCE_ALREADY_ACQUIRED_KHR -1008 -#define CL_D3D11_RESOURCE_NOT_ACQUIRED_KHR -1009 - -/* cl_d3d11_device_source */ -#define CL_D3D11_DEVICE_KHR 0x4019 -#define CL_D3D11_DXGI_ADAPTER_KHR 0x401A - -/* cl_d3d11_device_set */ -#define CL_PREFERRED_DEVICES_FOR_D3D11_KHR 0x401B -#define CL_ALL_DEVICES_FOR_D3D11_KHR 0x401C - -/* cl_context_info */ -#define CL_CONTEXT_D3D11_DEVICE_KHR 0x401D -#define CL_CONTEXT_D3D11_PREFER_SHARED_RESOURCES_KHR 0x402D - -/* cl_mem_info */ -#define CL_MEM_D3D11_RESOURCE_KHR 0x401E - -/* cl_image_info */ -#define CL_IMAGE_D3D11_SUBRESOURCE_KHR 0x401F - -/* cl_command_type */ -#define CL_COMMAND_ACQUIRE_D3D11_OBJECTS_KHR 0x4020 -#define CL_COMMAND_RELEASE_D3D11_OBJECTS_KHR 0x4021 - -/******************************************************************************/ - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clGetDeviceIDsFromD3D11KHR_fn)( - cl_platform_id platform, - cl_d3d11_device_source_khr d3d_device_source, - void * d3d_object, - cl_d3d11_device_set_khr d3d_device_set, - cl_uint num_entries, - cl_device_id * devices, - cl_uint * num_devices) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_mem (CL_API_CALL *clCreateFromD3D11BufferKHR_fn)( - cl_context context, - cl_mem_flags flags, - ID3D11Buffer * resource, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_mem (CL_API_CALL *clCreateFromD3D11Texture2DKHR_fn)( - cl_context context, - cl_mem_flags flags, - ID3D11Texture2D * resource, - UINT subresource, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_mem (CL_API_CALL *clCreateFromD3D11Texture3DKHR_fn)( - cl_context context, - cl_mem_flags flags, - ID3D11Texture3D * resource, - UINT subresource, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clEnqueueAcquireD3D11ObjectsKHR_fn)( - cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem * mem_objects, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clEnqueueReleaseD3D11ObjectsKHR_fn)( - cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem * mem_objects, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_2; - -#ifdef __cplusplus -} -#endif - -#endif /* __OPENCL_CL_D3D11_H */ - diff --git a/src/CL/cl_dx9_media_sharing.h b/src/CL/cl_dx9_media_sharing.h deleted file mode 100644 index 0489370..0000000 --- a/src/CL/cl_dx9_media_sharing.h +++ /dev/null @@ -1,118 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008-2020 The Khronos Group Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -#ifndef __OPENCL_CL_DX9_MEDIA_SHARING_H -#define __OPENCL_CL_DX9_MEDIA_SHARING_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/******************************************************************************/ -/* cl_khr_dx9_media_sharing */ -#define cl_khr_dx9_media_sharing 1 - -typedef cl_uint cl_dx9_media_adapter_type_khr; -typedef cl_uint cl_dx9_media_adapter_set_khr; - -#if defined(_WIN32) -#include -typedef struct _cl_dx9_surface_info_khr -{ - IDirect3DSurface9 *resource; - HANDLE shared_handle; -} cl_dx9_surface_info_khr; -#endif - - -/******************************************************************************/ - -/* Error Codes */ -#define CL_INVALID_DX9_MEDIA_ADAPTER_KHR -1010 -#define CL_INVALID_DX9_MEDIA_SURFACE_KHR -1011 -#define CL_DX9_MEDIA_SURFACE_ALREADY_ACQUIRED_KHR -1012 -#define CL_DX9_MEDIA_SURFACE_NOT_ACQUIRED_KHR -1013 - -/* cl_media_adapter_type_khr */ -#define CL_ADAPTER_D3D9_KHR 0x2020 -#define CL_ADAPTER_D3D9EX_KHR 0x2021 -#define CL_ADAPTER_DXVA_KHR 0x2022 - -/* cl_media_adapter_set_khr */ -#define CL_PREFERRED_DEVICES_FOR_DX9_MEDIA_ADAPTER_KHR 0x2023 -#define CL_ALL_DEVICES_FOR_DX9_MEDIA_ADAPTER_KHR 0x2024 - -/* cl_context_info */ -#define CL_CONTEXT_ADAPTER_D3D9_KHR 0x2025 -#define CL_CONTEXT_ADAPTER_D3D9EX_KHR 0x2026 -#define CL_CONTEXT_ADAPTER_DXVA_KHR 0x2027 - -/* cl_mem_info */ -#define CL_MEM_DX9_MEDIA_ADAPTER_TYPE_KHR 0x2028 -#define CL_MEM_DX9_MEDIA_SURFACE_INFO_KHR 0x2029 - -/* cl_image_info */ -#define CL_IMAGE_DX9_MEDIA_PLANE_KHR 0x202A - -/* cl_command_type */ -#define CL_COMMAND_ACQUIRE_DX9_MEDIA_SURFACES_KHR 0x202B -#define CL_COMMAND_RELEASE_DX9_MEDIA_SURFACES_KHR 0x202C - -/******************************************************************************/ - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clGetDeviceIDsFromDX9MediaAdapterKHR_fn)( - cl_platform_id platform, - cl_uint num_media_adapters, - cl_dx9_media_adapter_type_khr * media_adapter_type, - void * media_adapters, - cl_dx9_media_adapter_set_khr media_adapter_set, - cl_uint num_entries, - cl_device_id * devices, - cl_uint * num_devices) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_mem (CL_API_CALL *clCreateFromDX9MediaSurfaceKHR_fn)( - cl_context context, - cl_mem_flags flags, - cl_dx9_media_adapter_type_khr adapter_type, - void * surface_info, - cl_uint plane, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clEnqueueAcquireDX9MediaSurfacesKHR_fn)( - cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem * mem_objects, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clEnqueueReleaseDX9MediaSurfacesKHR_fn)( - cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem * mem_objects, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_2; - -#ifdef __cplusplus -} -#endif - -#endif /* __OPENCL_CL_DX9_MEDIA_SHARING_H */ - diff --git a/src/CL/cl_dx9_media_sharing_intel.h b/src/CL/cl_dx9_media_sharing_intel.h deleted file mode 100644 index 4525a17..0000000 --- a/src/CL/cl_dx9_media_sharing_intel.h +++ /dev/null @@ -1,170 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008-2020 The Khronos Group Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -/*****************************************************************************\ - -Copyright (c) 2013-2019 Intel Corporation All Rights Reserved. - -THESE MATERIALS ARE PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR ITS -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THESE -MATERIALS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -File Name: cl_dx9_media_sharing_intel.h - -Abstract: - -Notes: - -\*****************************************************************************/ - -#ifndef __OPENCL_CL_DX9_MEDIA_SHARING_INTEL_H -#define __OPENCL_CL_DX9_MEDIA_SHARING_INTEL_H - -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/*************************************** -* cl_intel_dx9_media_sharing extension * -****************************************/ - -#define cl_intel_dx9_media_sharing 1 - -typedef cl_uint cl_dx9_device_source_intel; -typedef cl_uint cl_dx9_device_set_intel; - -/* error codes */ -#define CL_INVALID_DX9_DEVICE_INTEL -1010 -#define CL_INVALID_DX9_RESOURCE_INTEL -1011 -#define CL_DX9_RESOURCE_ALREADY_ACQUIRED_INTEL -1012 -#define CL_DX9_RESOURCE_NOT_ACQUIRED_INTEL -1013 - -/* cl_dx9_device_source_intel */ -#define CL_D3D9_DEVICE_INTEL 0x4022 -#define CL_D3D9EX_DEVICE_INTEL 0x4070 -#define CL_DXVA_DEVICE_INTEL 0x4071 - -/* cl_dx9_device_set_intel */ -#define CL_PREFERRED_DEVICES_FOR_DX9_INTEL 0x4024 -#define CL_ALL_DEVICES_FOR_DX9_INTEL 0x4025 - -/* cl_context_info */ -#define CL_CONTEXT_D3D9_DEVICE_INTEL 0x4026 -#define CL_CONTEXT_D3D9EX_DEVICE_INTEL 0x4072 -#define CL_CONTEXT_DXVA_DEVICE_INTEL 0x4073 - -/* cl_mem_info */ -#define CL_MEM_DX9_RESOURCE_INTEL 0x4027 -#define CL_MEM_DX9_SHARED_HANDLE_INTEL 0x4074 - -/* cl_image_info */ -#define CL_IMAGE_DX9_PLANE_INTEL 0x4075 - -/* cl_command_type */ -#define CL_COMMAND_ACQUIRE_DX9_OBJECTS_INTEL 0x402A -#define CL_COMMAND_RELEASE_DX9_OBJECTS_INTEL 0x402B -/******************************************************************************/ - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetDeviceIDsFromDX9INTEL( - cl_platform_id platform, - cl_dx9_device_source_intel dx9_device_source, - void* dx9_object, - cl_dx9_device_set_intel dx9_device_set, - cl_uint num_entries, - cl_device_id* devices, - cl_uint* num_devices) CL_EXT_SUFFIX__VERSION_1_1; - -typedef CL_API_ENTRY cl_int (CL_API_CALL* clGetDeviceIDsFromDX9INTEL_fn)( - cl_platform_id platform, - cl_dx9_device_source_intel dx9_device_source, - void* dx9_object, - cl_dx9_device_set_intel dx9_device_set, - cl_uint num_entries, - cl_device_id* devices, - cl_uint* num_devices) CL_EXT_SUFFIX__VERSION_1_1; - -extern CL_API_ENTRY cl_mem CL_API_CALL -clCreateFromDX9MediaSurfaceINTEL( - cl_context context, - cl_mem_flags flags, - IDirect3DSurface9* resource, - HANDLE sharedHandle, - UINT plane, - cl_int* errcode_ret) CL_EXT_SUFFIX__VERSION_1_1; - -typedef CL_API_ENTRY cl_mem (CL_API_CALL *clCreateFromDX9MediaSurfaceINTEL_fn)( - cl_context context, - cl_mem_flags flags, - IDirect3DSurface9* resource, - HANDLE sharedHandle, - UINT plane, - cl_int* errcode_ret) CL_EXT_SUFFIX__VERSION_1_1; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueAcquireDX9ObjectsINTEL( - cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem* mem_objects, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event) CL_EXT_SUFFIX__VERSION_1_1; - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clEnqueueAcquireDX9ObjectsINTEL_fn)( - cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem* mem_objects, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event) CL_EXT_SUFFIX__VERSION_1_1; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueReleaseDX9ObjectsINTEL( - cl_command_queue command_queue, - cl_uint num_objects, - cl_mem* mem_objects, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event) CL_EXT_SUFFIX__VERSION_1_1; - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clEnqueueReleaseDX9ObjectsINTEL_fn)( - cl_command_queue command_queue, - cl_uint num_objects, - cl_mem* mem_objects, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event) CL_EXT_SUFFIX__VERSION_1_1; - -#ifdef __cplusplus -} -#endif - -#endif /* __OPENCL_CL_DX9_MEDIA_SHARING_INTEL_H */ - diff --git a/src/CL/cl_egl.h b/src/CL/cl_egl.h deleted file mode 100644 index c8bde80..0000000 --- a/src/CL/cl_egl.h +++ /dev/null @@ -1,120 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008-2020 The Khronos Group Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -#ifndef __OPENCL_CL_EGL_H -#define __OPENCL_CL_EGL_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - - -/* Command type for events created with clEnqueueAcquireEGLObjectsKHR */ -#define CL_COMMAND_EGL_FENCE_SYNC_OBJECT_KHR 0x202F -#define CL_COMMAND_ACQUIRE_EGL_OBJECTS_KHR 0x202D -#define CL_COMMAND_RELEASE_EGL_OBJECTS_KHR 0x202E - -/* Error type for clCreateFromEGLImageKHR */ -#define CL_INVALID_EGL_OBJECT_KHR -1093 -#define CL_EGL_RESOURCE_NOT_ACQUIRED_KHR -1092 - -/* CLeglImageKHR is an opaque handle to an EGLImage */ -typedef void* CLeglImageKHR; - -/* CLeglDisplayKHR is an opaque handle to an EGLDisplay */ -typedef void* CLeglDisplayKHR; - -/* CLeglSyncKHR is an opaque handle to an EGLSync object */ -typedef void* CLeglSyncKHR; - -/* properties passed to clCreateFromEGLImageKHR */ -typedef intptr_t cl_egl_image_properties_khr; - - -#define cl_khr_egl_image 1 - -extern CL_API_ENTRY cl_mem CL_API_CALL -clCreateFromEGLImageKHR(cl_context context, - CLeglDisplayKHR egldisplay, - CLeglImageKHR eglimage, - cl_mem_flags flags, - const cl_egl_image_properties_khr * properties, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_mem (CL_API_CALL *clCreateFromEGLImageKHR_fn)( - cl_context context, - CLeglDisplayKHR egldisplay, - CLeglImageKHR eglimage, - cl_mem_flags flags, - const cl_egl_image_properties_khr * properties, - cl_int * errcode_ret); - - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueAcquireEGLObjectsKHR(cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem * mem_objects, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clEnqueueAcquireEGLObjectsKHR_fn)( - cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem * mem_objects, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event); - - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueReleaseEGLObjectsKHR(cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem * mem_objects, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clEnqueueReleaseEGLObjectsKHR_fn)( - cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem * mem_objects, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event); - - -#define cl_khr_egl_event 1 - -extern CL_API_ENTRY cl_event CL_API_CALL -clCreateEventFromEGLSyncKHR(cl_context context, - CLeglSyncKHR sync, - CLeglDisplayKHR display, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_event (CL_API_CALL *clCreateEventFromEGLSyncKHR_fn)( - cl_context context, - CLeglSyncKHR sync, - CLeglDisplayKHR display, - cl_int * errcode_ret); - -#ifdef __cplusplus -} -#endif - -#endif /* __OPENCL_CL_EGL_H */ diff --git a/src/CL/cl_ext.h b/src/CL/cl_ext.h deleted file mode 100644 index cd86843..0000000 --- a/src/CL/cl_ext.h +++ /dev/null @@ -1,841 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008-2020 The Khronos Group Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -/* cl_ext.h contains OpenCL extensions which don't have external */ -/* (OpenGL, D3D) dependencies. */ - -#ifndef __CL_EXT_H -#define __CL_EXT_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/* cl_khr_fp64 extension - no extension #define since it has no functions */ -/* CL_DEVICE_DOUBLE_FP_CONFIG is defined in CL.h for OpenCL >= 120 */ - -#if CL_TARGET_OPENCL_VERSION <= 110 -#define CL_DEVICE_DOUBLE_FP_CONFIG 0x1032 -#endif - -/* cl_khr_fp16 extension - no extension #define since it has no functions */ -#define CL_DEVICE_HALF_FP_CONFIG 0x1033 - -/* Memory object destruction - * - * Apple extension for use to manage externally allocated buffers used with cl_mem objects with CL_MEM_USE_HOST_PTR - * - * Registers a user callback function that will be called when the memory object is deleted and its resources - * freed. Each call to clSetMemObjectCallbackFn registers the specified user callback function on a callback - * stack associated with memobj. The registered user callback functions are called in the reverse order in - * which they were registered. The user callback functions are called and then the memory object is deleted - * and its resources freed. This provides a mechanism for the application (and libraries) using memobj to be - * notified when the memory referenced by host_ptr, specified when the memory object is created and used as - * the storage bits for the memory object, can be reused or freed. - * - * The application may not call CL api's with the cl_mem object passed to the pfn_notify. - * - * Please check for the "cl_APPLE_SetMemObjectDestructor" extension using clGetDeviceInfo(CL_DEVICE_EXTENSIONS) - * before using. - */ -#define cl_APPLE_SetMemObjectDestructor 1 -cl_int CL_API_ENTRY clSetMemObjectDestructorAPPLE( cl_mem memobj, - void (* pfn_notify)(cl_mem memobj, void * user_data), - void * user_data) CL_EXT_SUFFIX__VERSION_1_0; - - -/* Context Logging Functions - * - * The next three convenience functions are intended to be used as the pfn_notify parameter to clCreateContext(). - * Please check for the "cl_APPLE_ContextLoggingFunctions" extension using clGetDeviceInfo(CL_DEVICE_EXTENSIONS) - * before using. - * - * clLogMessagesToSystemLog forwards on all log messages to the Apple System Logger - */ -#define cl_APPLE_ContextLoggingFunctions 1 -extern void CL_API_ENTRY clLogMessagesToSystemLogAPPLE( const char * errstr, - const void * private_info, - size_t cb, - void * user_data) CL_EXT_SUFFIX__VERSION_1_0; - -/* clLogMessagesToStdout sends all log messages to the file descriptor stdout */ -extern void CL_API_ENTRY clLogMessagesToStdoutAPPLE( const char * errstr, - const void * private_info, - size_t cb, - void * user_data) CL_EXT_SUFFIX__VERSION_1_0; - -/* clLogMessagesToStderr sends all log messages to the file descriptor stderr */ -extern void CL_API_ENTRY clLogMessagesToStderrAPPLE( const char * errstr, - const void * private_info, - size_t cb, - void * user_data) CL_EXT_SUFFIX__VERSION_1_0; - - -/************************ -* cl_khr_icd extension * -************************/ -#define cl_khr_icd 1 - -/* cl_platform_info */ -#define CL_PLATFORM_ICD_SUFFIX_KHR 0x0920 - -/* Additional Error Codes */ -#define CL_PLATFORM_NOT_FOUND_KHR -1001 - -extern CL_API_ENTRY cl_int CL_API_CALL -clIcdGetPlatformIDsKHR(cl_uint num_entries, - cl_platform_id * platforms, - cl_uint * num_platforms); - -typedef CL_API_ENTRY cl_int -(CL_API_CALL *clIcdGetPlatformIDsKHR_fn)(cl_uint num_entries, - cl_platform_id * platforms, - cl_uint * num_platforms); - - -/******************************* - * cl_khr_il_program extension * - *******************************/ -#define cl_khr_il_program 1 - -/* New property to clGetDeviceInfo for retrieving supported intermediate - * languages - */ -#define CL_DEVICE_IL_VERSION_KHR 0x105B - -/* New property to clGetProgramInfo for retrieving for retrieving the IL of a - * program - */ -#define CL_PROGRAM_IL_KHR 0x1169 - -extern CL_API_ENTRY cl_program CL_API_CALL -clCreateProgramWithILKHR(cl_context context, - const void * il, - size_t length, - cl_int * errcode_ret); - -typedef CL_API_ENTRY cl_program -(CL_API_CALL *clCreateProgramWithILKHR_fn)(cl_context context, - const void * il, - size_t length, - cl_int * errcode_ret) CL_EXT_SUFFIX__VERSION_1_2; - -/* Extension: cl_khr_image2d_from_buffer - * - * This extension allows a 2D image to be created from a cl_mem buffer without - * a copy. The type associated with a 2D image created from a buffer in an - * OpenCL program is image2d_t. Both the sampler and sampler-less read_image - * built-in functions are supported for 2D images and 2D images created from - * a buffer. Similarly, the write_image built-ins are also supported for 2D - * images created from a buffer. - * - * When the 2D image from buffer is created, the client must specify the - * width, height, image format (i.e. channel order and channel data type) - * and optionally the row pitch. - * - * The pitch specified must be a multiple of - * CL_DEVICE_IMAGE_PITCH_ALIGNMENT_KHR pixels. - * The base address of the buffer must be aligned to - * CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT_KHR pixels. - */ - -#define CL_DEVICE_IMAGE_PITCH_ALIGNMENT_KHR 0x104A -#define CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT_KHR 0x104B - - -/************************************** - * cl_khr_initialize_memory extension * - **************************************/ - -#define CL_CONTEXT_MEMORY_INITIALIZE_KHR 0x2030 - - -/************************************** - * cl_khr_terminate_context extension * - **************************************/ - -#define CL_CONTEXT_TERMINATED_KHR -1121 - -#define CL_DEVICE_TERMINATE_CAPABILITY_KHR 0x2031 -#define CL_CONTEXT_TERMINATE_KHR 0x2032 - -#define cl_khr_terminate_context 1 -extern CL_API_ENTRY cl_int CL_API_CALL -clTerminateContextKHR(cl_context context) CL_EXT_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_int -(CL_API_CALL *clTerminateContextKHR_fn)(cl_context context) CL_EXT_SUFFIX__VERSION_1_2; - - -/* - * Extension: cl_khr_spir - * - * This extension adds support to create an OpenCL program object from a - * Standard Portable Intermediate Representation (SPIR) instance - */ - -#define CL_DEVICE_SPIR_VERSIONS 0x40E0 -#define CL_PROGRAM_BINARY_TYPE_INTERMEDIATE 0x40E1 - - -/***************************************** - * cl_khr_create_command_queue extension * - *****************************************/ -#define cl_khr_create_command_queue 1 - -typedef cl_bitfield cl_queue_properties_khr; - -extern CL_API_ENTRY cl_command_queue CL_API_CALL -clCreateCommandQueueWithPropertiesKHR(cl_context context, - cl_device_id device, - const cl_queue_properties_khr* properties, - cl_int* errcode_ret) CL_EXT_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_command_queue -(CL_API_CALL *clCreateCommandQueueWithPropertiesKHR_fn)(cl_context context, - cl_device_id device, - const cl_queue_properties_khr* properties, - cl_int* errcode_ret) CL_EXT_SUFFIX__VERSION_1_2; - - -/****************************************** -* cl_nv_device_attribute_query extension * -******************************************/ - -/* cl_nv_device_attribute_query extension - no extension #define since it has no functions */ -#define CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV 0x4000 -#define CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV 0x4001 -#define CL_DEVICE_REGISTERS_PER_BLOCK_NV 0x4002 -#define CL_DEVICE_WARP_SIZE_NV 0x4003 -#define CL_DEVICE_GPU_OVERLAP_NV 0x4004 -#define CL_DEVICE_KERNEL_EXEC_TIMEOUT_NV 0x4005 -#define CL_DEVICE_INTEGRATED_MEMORY_NV 0x4006 - - -/********************************* -* cl_amd_device_attribute_query * -*********************************/ - -#define CL_DEVICE_PROFILING_TIMER_OFFSET_AMD 0x4036 -#define CL_DEVICE_TOPOLOGY_AMD 0x4037 -#define CL_DEVICE_BOARD_NAME_AMD 0x4038 -#define CL_DEVICE_GLOBAL_FREE_MEMORY_AMD 0x4039 -#define CL_DEVICE_SIMD_PER_COMPUTE_UNIT_AMD 0x4040 -#define CL_DEVICE_SIMD_WIDTH_AMD 0x4041 -#define CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD 0x4042 -#define CL_DEVICE_WAVEFRONT_WIDTH_AMD 0x4043 -#define CL_DEVICE_GLOBAL_MEM_CHANNELS_AMD 0x4044 -#define CL_DEVICE_GLOBAL_MEM_CHANNEL_BANKS_AMD 0x4045 -#define CL_DEVICE_GLOBAL_MEM_CHANNEL_BANK_WIDTH_AMD 0x4046 -#define CL_DEVICE_LOCAL_MEM_SIZE_PER_COMPUTE_UNIT_AMD 0x4047 -#define CL_DEVICE_LOCAL_MEM_BANKS_AMD 0x4048 -#define CL_DEVICE_THREAD_TRACE_SUPPORTED_AMD 0x4049 -#define CL_DEVICE_GFXIP_MAJOR_AMD 0x404A -#define CL_DEVICE_GFXIP_MINOR_AMD 0x404B -#define CL_DEVICE_AVAILABLE_ASYNC_QUEUES_AMD 0x404C -#define CL_DEVICE_PREFERRED_WORK_GROUP_SIZE_AMD 0x4030 -#define CL_DEVICE_MAX_WORK_GROUP_SIZE_AMD 0x4031 -#define CL_DEVICE_PREFERRED_CONSTANT_BUFFER_SIZE_AMD 0x4033 -#define CL_DEVICE_PCIE_ID_AMD 0x4034 - - -/********************************* -* cl_arm_printf extension -*********************************/ - -#define CL_PRINTF_CALLBACK_ARM 0x40B0 -#define CL_PRINTF_BUFFERSIZE_ARM 0x40B1 - - -/*********************************** -* cl_ext_device_fission extension -***********************************/ -#define cl_ext_device_fission 1 - -extern CL_API_ENTRY cl_int CL_API_CALL -clReleaseDeviceEXT(cl_device_id device) CL_EXT_SUFFIX__VERSION_1_1; - -typedef CL_API_ENTRY cl_int -(CL_API_CALL *clReleaseDeviceEXT_fn)(cl_device_id device) CL_EXT_SUFFIX__VERSION_1_1; - -extern CL_API_ENTRY cl_int CL_API_CALL -clRetainDeviceEXT(cl_device_id device) CL_EXT_SUFFIX__VERSION_1_1; - -typedef CL_API_ENTRY cl_int -(CL_API_CALL *clRetainDeviceEXT_fn)(cl_device_id device) CL_EXT_SUFFIX__VERSION_1_1; - -typedef cl_ulong cl_device_partition_property_ext; -extern CL_API_ENTRY cl_int CL_API_CALL -clCreateSubDevicesEXT(cl_device_id in_device, - const cl_device_partition_property_ext * properties, - cl_uint num_entries, - cl_device_id * out_devices, - cl_uint * num_devices) CL_EXT_SUFFIX__VERSION_1_1; - -typedef CL_API_ENTRY cl_int -(CL_API_CALL * clCreateSubDevicesEXT_fn)(cl_device_id in_device, - const cl_device_partition_property_ext * properties, - cl_uint num_entries, - cl_device_id * out_devices, - cl_uint * num_devices) CL_EXT_SUFFIX__VERSION_1_1; - -/* cl_device_partition_property_ext */ -#define CL_DEVICE_PARTITION_EQUALLY_EXT 0x4050 -#define CL_DEVICE_PARTITION_BY_COUNTS_EXT 0x4051 -#define CL_DEVICE_PARTITION_BY_NAMES_EXT 0x4052 -#define CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN_EXT 0x4053 - -/* clDeviceGetInfo selectors */ -#define CL_DEVICE_PARENT_DEVICE_EXT 0x4054 -#define CL_DEVICE_PARTITION_TYPES_EXT 0x4055 -#define CL_DEVICE_AFFINITY_DOMAINS_EXT 0x4056 -#define CL_DEVICE_REFERENCE_COUNT_EXT 0x4057 -#define CL_DEVICE_PARTITION_STYLE_EXT 0x4058 - -/* error codes */ -#define CL_DEVICE_PARTITION_FAILED_EXT -1057 -#define CL_INVALID_PARTITION_COUNT_EXT -1058 -#define CL_INVALID_PARTITION_NAME_EXT -1059 - -/* CL_AFFINITY_DOMAINs */ -#define CL_AFFINITY_DOMAIN_L1_CACHE_EXT 0x1 -#define CL_AFFINITY_DOMAIN_L2_CACHE_EXT 0x2 -#define CL_AFFINITY_DOMAIN_L3_CACHE_EXT 0x3 -#define CL_AFFINITY_DOMAIN_L4_CACHE_EXT 0x4 -#define CL_AFFINITY_DOMAIN_NUMA_EXT 0x10 -#define CL_AFFINITY_DOMAIN_NEXT_FISSIONABLE_EXT 0x100 - -/* cl_device_partition_property_ext list terminators */ -#define CL_PROPERTIES_LIST_END_EXT ((cl_device_partition_property_ext) 0) -#define CL_PARTITION_BY_COUNTS_LIST_END_EXT ((cl_device_partition_property_ext) 0) -#define CL_PARTITION_BY_NAMES_LIST_END_EXT ((cl_device_partition_property_ext) 0 - 1) - - -/*********************************** - * cl_ext_migrate_memobject extension definitions - ***********************************/ -#define cl_ext_migrate_memobject 1 - -typedef cl_bitfield cl_mem_migration_flags_ext; - -#define CL_MIGRATE_MEM_OBJECT_HOST_EXT 0x1 - -#define CL_COMMAND_MIGRATE_MEM_OBJECT_EXT 0x4040 - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueMigrateMemObjectEXT(cl_command_queue command_queue, - cl_uint num_mem_objects, - const cl_mem * mem_objects, - cl_mem_migration_flags_ext flags, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event); - -typedef CL_API_ENTRY cl_int -(CL_API_CALL *clEnqueueMigrateMemObjectEXT_fn)(cl_command_queue command_queue, - cl_uint num_mem_objects, - const cl_mem * mem_objects, - cl_mem_migration_flags_ext flags, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event); - - -/********************************* -* cl_qcom_ext_host_ptr extension -*********************************/ -#define cl_qcom_ext_host_ptr 1 - -#define CL_MEM_EXT_HOST_PTR_QCOM (1 << 29) - -#define CL_DEVICE_EXT_MEM_PADDING_IN_BYTES_QCOM 0x40A0 -#define CL_DEVICE_PAGE_SIZE_QCOM 0x40A1 -#define CL_IMAGE_ROW_ALIGNMENT_QCOM 0x40A2 -#define CL_IMAGE_SLICE_ALIGNMENT_QCOM 0x40A3 -#define CL_MEM_HOST_UNCACHED_QCOM 0x40A4 -#define CL_MEM_HOST_WRITEBACK_QCOM 0x40A5 -#define CL_MEM_HOST_WRITETHROUGH_QCOM 0x40A6 -#define CL_MEM_HOST_WRITE_COMBINING_QCOM 0x40A7 - -typedef cl_uint cl_image_pitch_info_qcom; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetDeviceImageInfoQCOM(cl_device_id device, - size_t image_width, - size_t image_height, - const cl_image_format *image_format, - cl_image_pitch_info_qcom param_name, - size_t param_value_size, - void *param_value, - size_t *param_value_size_ret); - -typedef struct _cl_mem_ext_host_ptr -{ - /* Type of external memory allocation. */ - /* Legal values will be defined in layered extensions. */ - cl_uint allocation_type; - - /* Host cache policy for this external memory allocation. */ - cl_uint host_cache_policy; - -} cl_mem_ext_host_ptr; - - -/******************************************* -* cl_qcom_ext_host_ptr_iocoherent extension -********************************************/ - -/* Cache policy specifying io-coherence */ -#define CL_MEM_HOST_IOCOHERENT_QCOM 0x40A9 - - -/********************************* -* cl_qcom_ion_host_ptr extension -*********************************/ - -#define CL_MEM_ION_HOST_PTR_QCOM 0x40A8 - -typedef struct _cl_mem_ion_host_ptr -{ - /* Type of external memory allocation. */ - /* Must be CL_MEM_ION_HOST_PTR_QCOM for ION allocations. */ - cl_mem_ext_host_ptr ext_host_ptr; - - /* ION file descriptor */ - int ion_filedesc; - - /* Host pointer to the ION allocated memory */ - void* ion_hostptr; - -} cl_mem_ion_host_ptr; - - -/********************************* -* cl_qcom_android_native_buffer_host_ptr extension -*********************************/ - -#define CL_MEM_ANDROID_NATIVE_BUFFER_HOST_PTR_QCOM 0x40C6 - -typedef struct _cl_mem_android_native_buffer_host_ptr -{ - /* Type of external memory allocation. */ - /* Must be CL_MEM_ANDROID_NATIVE_BUFFER_HOST_PTR_QCOM for Android native buffers. */ - cl_mem_ext_host_ptr ext_host_ptr; - - /* Virtual pointer to the android native buffer */ - void* anb_ptr; - -} cl_mem_android_native_buffer_host_ptr; - - -/****************************************** - * cl_img_yuv_image extension * - ******************************************/ - -/* Image formats used in clCreateImage */ -#define CL_NV21_IMG 0x40D0 -#define CL_YV12_IMG 0x40D1 - - -/****************************************** - * cl_img_cached_allocations extension * - ******************************************/ - -/* Flag values used by clCreateBuffer */ -#define CL_MEM_USE_UNCACHED_CPU_MEMORY_IMG (1 << 26) -#define CL_MEM_USE_CACHED_CPU_MEMORY_IMG (1 << 27) - - -/****************************************** - * cl_img_use_gralloc_ptr extension * - ******************************************/ -#define cl_img_use_gralloc_ptr 1 - -/* Flag values used by clCreateBuffer */ -#define CL_MEM_USE_GRALLOC_PTR_IMG (1 << 28) - -/* To be used by clGetEventInfo: */ -#define CL_COMMAND_ACQUIRE_GRALLOC_OBJECTS_IMG 0x40D2 -#define CL_COMMAND_RELEASE_GRALLOC_OBJECTS_IMG 0x40D3 - -/* Error code from clEnqueueReleaseGrallocObjectsIMG */ -#define CL_GRALLOC_RESOURCE_NOT_ACQUIRED_IMG 0x40D4 - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueAcquireGrallocObjectsIMG(cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem * mem_objects, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_EXT_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueReleaseGrallocObjectsIMG(cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem * mem_objects, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_EXT_SUFFIX__VERSION_1_2; - - -/********************************* -* cl_khr_subgroups extension -*********************************/ -#define cl_khr_subgroups 1 - -#if !defined(CL_VERSION_2_1) -/* For OpenCL 2.1 and newer, cl_kernel_sub_group_info is declared in CL.h. - In hindsight, there should have been a khr suffix on this type for - the extension, but keeping it un-suffixed to maintain backwards - compatibility. */ -typedef cl_uint cl_kernel_sub_group_info; -#endif - -/* cl_kernel_sub_group_info */ -#define CL_KERNEL_MAX_SUB_GROUP_SIZE_FOR_NDRANGE_KHR 0x2033 -#define CL_KERNEL_SUB_GROUP_COUNT_FOR_NDRANGE_KHR 0x2034 - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetKernelSubGroupInfoKHR(cl_kernel in_kernel, - cl_device_id in_device, - cl_kernel_sub_group_info param_name, - size_t input_value_size, - const void * input_value, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_EXT_SUFFIX__VERSION_2_0_DEPRECATED; - -typedef CL_API_ENTRY cl_int -(CL_API_CALL * clGetKernelSubGroupInfoKHR_fn)(cl_kernel in_kernel, - cl_device_id in_device, - cl_kernel_sub_group_info param_name, - size_t input_value_size, - const void * input_value, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_EXT_SUFFIX__VERSION_2_0_DEPRECATED; - - -/********************************* -* cl_khr_mipmap_image extension -*********************************/ - -/* cl_sampler_properties */ -#define CL_SAMPLER_MIP_FILTER_MODE_KHR 0x1155 -#define CL_SAMPLER_LOD_MIN_KHR 0x1156 -#define CL_SAMPLER_LOD_MAX_KHR 0x1157 - - -/********************************* -* cl_khr_priority_hints extension -*********************************/ -/* This extension define is for backwards compatibility. - It shouldn't be required since this extension has no new functions. */ -#define cl_khr_priority_hints 1 - -typedef cl_uint cl_queue_priority_khr; - -/* cl_command_queue_properties */ -#define CL_QUEUE_PRIORITY_KHR 0x1096 - -/* cl_queue_priority_khr */ -#define CL_QUEUE_PRIORITY_HIGH_KHR (1<<0) -#define CL_QUEUE_PRIORITY_MED_KHR (1<<1) -#define CL_QUEUE_PRIORITY_LOW_KHR (1<<2) - - -/********************************* -* cl_khr_throttle_hints extension -*********************************/ -/* This extension define is for backwards compatibility. - It shouldn't be required since this extension has no new functions. */ -#define cl_khr_throttle_hints 1 - -typedef cl_uint cl_queue_throttle_khr; - -/* cl_command_queue_properties */ -#define CL_QUEUE_THROTTLE_KHR 0x1097 - -/* cl_queue_throttle_khr */ -#define CL_QUEUE_THROTTLE_HIGH_KHR (1<<0) -#define CL_QUEUE_THROTTLE_MED_KHR (1<<1) -#define CL_QUEUE_THROTTLE_LOW_KHR (1<<2) - - -/********************************* -* cl_khr_subgroup_named_barrier -*********************************/ -/* This extension define is for backwards compatibility. - It shouldn't be required since this extension has no new functions. */ -#define cl_khr_subgroup_named_barrier 1 - -/* cl_device_info */ -#define CL_DEVICE_MAX_NAMED_BARRIER_COUNT_KHR 0x2035 - - -/********************************* -* cl_khr_extended_versioning -*********************************/ - -#define cl_khr_extended_versioning 1 - -#define CL_VERSION_MAJOR_BITS_KHR (10) -#define CL_VERSION_MINOR_BITS_KHR (10) -#define CL_VERSION_PATCH_BITS_KHR (12) - -#define CL_VERSION_MAJOR_MASK_KHR ((1 << CL_VERSION_MAJOR_BITS_KHR) - 1) -#define CL_VERSION_MINOR_MASK_KHR ((1 << CL_VERSION_MINOR_BITS_KHR) - 1) -#define CL_VERSION_PATCH_MASK_KHR ((1 << CL_VERSION_PATCH_BITS_KHR) - 1) - -#define CL_VERSION_MAJOR_KHR(version) ((version) >> (CL_VERSION_MINOR_BITS_KHR + CL_VERSION_PATCH_BITS_KHR)) -#define CL_VERSION_MINOR_KHR(version) (((version) >> CL_VERSION_PATCH_BITS_KHR) & CL_VERSION_MINOR_MASK_KHR) -#define CL_VERSION_PATCH_KHR(version) ((version) & CL_VERSION_PATCH_MASK_KHR) - -#define CL_MAKE_VERSION_KHR(major, minor, patch) \ - ((((major) & CL_VERSION_MAJOR_MASK_KHR) << (CL_VERSION_MINOR_BITS_KHR + CL_VERSION_PATCH_BITS_KHR)) | \ - (((minor) & CL_VERSION_MINOR_MASK_KHR) << CL_VERSION_PATCH_BITS_KHR) | \ - ((patch) & CL_VERSION_PATCH_MASK_KHR)) - -typedef cl_uint cl_version_khr; - -#define CL_NAME_VERSION_MAX_NAME_SIZE_KHR 64 - -typedef struct _cl_name_version_khr -{ - cl_version_khr version; - char name[CL_NAME_VERSION_MAX_NAME_SIZE_KHR]; -} cl_name_version_khr; - -/* cl_platform_info */ -#define CL_PLATFORM_NUMERIC_VERSION_KHR 0x0906 -#define CL_PLATFORM_EXTENSIONS_WITH_VERSION_KHR 0x0907 - -/* cl_device_info */ -#define CL_DEVICE_NUMERIC_VERSION_KHR 0x105E -#define CL_DEVICE_OPENCL_C_NUMERIC_VERSION_KHR 0x105F -#define CL_DEVICE_EXTENSIONS_WITH_VERSION_KHR 0x1060 -#define CL_DEVICE_ILS_WITH_VERSION_KHR 0x1061 -#define CL_DEVICE_BUILT_IN_KERNELS_WITH_VERSION_KHR 0x1062 - - -/********************************* -* cl_khr_device_uuid extension -*********************************/ -#define cl_khr_device_uuid 1 - -#define CL_UUID_SIZE_KHR 16 -#define CL_LUID_SIZE_KHR 8 - -#define CL_DEVICE_UUID_KHR 0x106A -#define CL_DRIVER_UUID_KHR 0x106B -#define CL_DEVICE_LUID_VALID_KHR 0x106C -#define CL_DEVICE_LUID_KHR 0x106D -#define CL_DEVICE_NODE_MASK_KHR 0x106E - - -/********************************** - * cl_arm_import_memory extension * - **********************************/ -#define cl_arm_import_memory 1 - -typedef intptr_t cl_import_properties_arm; - -/* Default and valid proporties name for cl_arm_import_memory */ -#define CL_IMPORT_TYPE_ARM 0x40B2 - -/* Host process memory type default value for CL_IMPORT_TYPE_ARM property */ -#define CL_IMPORT_TYPE_HOST_ARM 0x40B3 - -/* DMA BUF memory type value for CL_IMPORT_TYPE_ARM property */ -#define CL_IMPORT_TYPE_DMA_BUF_ARM 0x40B4 - -/* Protected memory property */ -#define CL_IMPORT_TYPE_PROTECTED_ARM 0x40B5 - -/* Android hardware buffer type value for CL_IMPORT_TYPE_ARM property */ -#define CL_IMPORT_TYPE_ANDROID_HARDWARE_BUFFER_ARM 0x41E2 - -/* Data consistency with host property */ -#define CL_IMPORT_DMA_BUF_DATA_CONSISTENCY_WITH_HOST_ARM 0x41E3 - -/* Import memory size value to indicate a size for the whole buffer */ -#define CL_IMPORT_MEMORY_WHOLE_ALLOCATION_ARM SIZE_MAX - -/* This extension adds a new function that allows for direct memory import into - * OpenCL via the clImportMemoryARM function. - * - * Memory imported through this interface will be mapped into the device's page - * tables directly, providing zero copy access. It will never fall back to copy - * operations and aliased buffers. - * - * Types of memory supported for import are specified as additional extension - * strings. - * - * This extension produces cl_mem allocations which are compatible with all other - * users of cl_mem in the standard API. - * - * This extension maps pages with the same properties as the normal buffer creation - * function clCreateBuffer. - */ -extern CL_API_ENTRY cl_mem CL_API_CALL -clImportMemoryARM( cl_context context, - cl_mem_flags flags, - const cl_import_properties_arm *properties, - void *memory, - size_t size, - cl_int *errcode_ret) CL_EXT_SUFFIX__VERSION_1_0; - - -/****************************************** - * cl_arm_shared_virtual_memory extension * - ******************************************/ -#define cl_arm_shared_virtual_memory 1 - -/* Used by clGetDeviceInfo */ -#define CL_DEVICE_SVM_CAPABILITIES_ARM 0x40B6 - -/* Used by clGetMemObjectInfo */ -#define CL_MEM_USES_SVM_POINTER_ARM 0x40B7 - -/* Used by clSetKernelExecInfoARM: */ -#define CL_KERNEL_EXEC_INFO_SVM_PTRS_ARM 0x40B8 -#define CL_KERNEL_EXEC_INFO_SVM_FINE_GRAIN_SYSTEM_ARM 0x40B9 - -/* To be used by clGetEventInfo: */ -#define CL_COMMAND_SVM_FREE_ARM 0x40BA -#define CL_COMMAND_SVM_MEMCPY_ARM 0x40BB -#define CL_COMMAND_SVM_MEMFILL_ARM 0x40BC -#define CL_COMMAND_SVM_MAP_ARM 0x40BD -#define CL_COMMAND_SVM_UNMAP_ARM 0x40BE - -/* Flag values returned by clGetDeviceInfo with CL_DEVICE_SVM_CAPABILITIES_ARM as the param_name. */ -#define CL_DEVICE_SVM_COARSE_GRAIN_BUFFER_ARM (1 << 0) -#define CL_DEVICE_SVM_FINE_GRAIN_BUFFER_ARM (1 << 1) -#define CL_DEVICE_SVM_FINE_GRAIN_SYSTEM_ARM (1 << 2) -#define CL_DEVICE_SVM_ATOMICS_ARM (1 << 3) - -/* Flag values used by clSVMAllocARM: */ -#define CL_MEM_SVM_FINE_GRAIN_BUFFER_ARM (1 << 10) -#define CL_MEM_SVM_ATOMICS_ARM (1 << 11) - -typedef cl_bitfield cl_svm_mem_flags_arm; -typedef cl_uint cl_kernel_exec_info_arm; -typedef cl_bitfield cl_device_svm_capabilities_arm; - -extern CL_API_ENTRY void * CL_API_CALL -clSVMAllocARM(cl_context context, - cl_svm_mem_flags_arm flags, - size_t size, - cl_uint alignment) CL_EXT_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY void CL_API_CALL -clSVMFreeARM(cl_context context, - void * svm_pointer) CL_EXT_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueSVMFreeARM(cl_command_queue command_queue, - cl_uint num_svm_pointers, - void * svm_pointers[], - void (CL_CALLBACK * pfn_free_func)(cl_command_queue queue, - cl_uint num_svm_pointers, - void * svm_pointers[], - void * user_data), - void * user_data, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_EXT_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueSVMMemcpyARM(cl_command_queue command_queue, - cl_bool blocking_copy, - void * dst_ptr, - const void * src_ptr, - size_t size, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_EXT_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueSVMMemFillARM(cl_command_queue command_queue, - void * svm_ptr, - const void * pattern, - size_t pattern_size, - size_t size, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_EXT_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueSVMMapARM(cl_command_queue command_queue, - cl_bool blocking_map, - cl_map_flags flags, - void * svm_ptr, - size_t size, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_EXT_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueSVMUnmapARM(cl_command_queue command_queue, - void * svm_ptr, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_EXT_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clSetKernelArgSVMPointerARM(cl_kernel kernel, - cl_uint arg_index, - const void * arg_value) CL_EXT_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clSetKernelExecInfoARM(cl_kernel kernel, - cl_kernel_exec_info_arm param_name, - size_t param_value_size, - const void * param_value) CL_EXT_SUFFIX__VERSION_1_2; - -/******************************** - * cl_arm_get_core_id extension * - ********************************/ - -#ifdef CL_VERSION_1_2 - -#define cl_arm_get_core_id 1 - -/* Device info property for bitfield of cores present */ -#define CL_DEVICE_COMPUTE_UNITS_BITFIELD_ARM 0x40BF - -#endif /* CL_VERSION_1_2 */ - -/********************************* -* cl_arm_job_slot_selection -*********************************/ - -#define cl_arm_job_slot_selection 1 - -/* cl_device_info */ -#define CL_DEVICE_JOB_SLOTS_ARM 0x41E0 - -/* cl_command_queue_properties */ -#define CL_QUEUE_JOB_SLOT_ARM 0x41E1 - -#ifdef __cplusplus -} -#endif - - -#endif /* __CL_EXT_H */ diff --git a/src/CL/cl_ext_intel.h b/src/CL/cl_ext_intel.h deleted file mode 100644 index f044684..0000000 --- a/src/CL/cl_ext_intel.h +++ /dev/null @@ -1,682 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008-2020 The Khronos Group Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ******************************************************************************/ -/*****************************************************************************\ - -Copyright (c) 2013-2020 Intel Corporation All Rights Reserved. - -THESE MATERIALS ARE PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR ITS -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THESE -MATERIALS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -File Name: cl_ext_intel.h - -Abstract: - -Notes: - -\*****************************************************************************/ - -#ifndef __CL_EXT_INTEL_H -#define __CL_EXT_INTEL_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/*************************************** -* cl_intel_thread_local_exec extension * -****************************************/ - -#define cl_intel_thread_local_exec 1 - -#define CL_QUEUE_THREAD_LOCAL_EXEC_ENABLE_INTEL (((cl_bitfield)1) << 31) - -/*********************************************** -* cl_intel_device_partition_by_names extension * -************************************************/ - -#define cl_intel_device_partition_by_names 1 - -#define CL_DEVICE_PARTITION_BY_NAMES_INTEL 0x4052 -#define CL_PARTITION_BY_NAMES_LIST_END_INTEL -1 - -/************************************************ -* cl_intel_accelerator extension * -* cl_intel_motion_estimation extension * -* cl_intel_advanced_motion_estimation extension * -*************************************************/ - -#define cl_intel_accelerator 1 -#define cl_intel_motion_estimation 1 -#define cl_intel_advanced_motion_estimation 1 - -typedef struct _cl_accelerator_intel* cl_accelerator_intel; -typedef cl_uint cl_accelerator_type_intel; -typedef cl_uint cl_accelerator_info_intel; - -typedef struct _cl_motion_estimation_desc_intel { - cl_uint mb_block_type; - cl_uint subpixel_mode; - cl_uint sad_adjust_mode; - cl_uint search_path_type; -} cl_motion_estimation_desc_intel; - -/* error codes */ -#define CL_INVALID_ACCELERATOR_INTEL -1094 -#define CL_INVALID_ACCELERATOR_TYPE_INTEL -1095 -#define CL_INVALID_ACCELERATOR_DESCRIPTOR_INTEL -1096 -#define CL_ACCELERATOR_TYPE_NOT_SUPPORTED_INTEL -1097 - -/* cl_accelerator_type_intel */ -#define CL_ACCELERATOR_TYPE_MOTION_ESTIMATION_INTEL 0x0 - -/* cl_accelerator_info_intel */ -#define CL_ACCELERATOR_DESCRIPTOR_INTEL 0x4090 -#define CL_ACCELERATOR_REFERENCE_COUNT_INTEL 0x4091 -#define CL_ACCELERATOR_CONTEXT_INTEL 0x4092 -#define CL_ACCELERATOR_TYPE_INTEL 0x4093 - -/* cl_motion_detect_desc_intel flags */ -#define CL_ME_MB_TYPE_16x16_INTEL 0x0 -#define CL_ME_MB_TYPE_8x8_INTEL 0x1 -#define CL_ME_MB_TYPE_4x4_INTEL 0x2 - -#define CL_ME_SUBPIXEL_MODE_INTEGER_INTEL 0x0 -#define CL_ME_SUBPIXEL_MODE_HPEL_INTEL 0x1 -#define CL_ME_SUBPIXEL_MODE_QPEL_INTEL 0x2 - -#define CL_ME_SAD_ADJUST_MODE_NONE_INTEL 0x0 -#define CL_ME_SAD_ADJUST_MODE_HAAR_INTEL 0x1 - -#define CL_ME_SEARCH_PATH_RADIUS_2_2_INTEL 0x0 -#define CL_ME_SEARCH_PATH_RADIUS_4_4_INTEL 0x1 -#define CL_ME_SEARCH_PATH_RADIUS_16_12_INTEL 0x5 - -#define CL_ME_SKIP_BLOCK_TYPE_16x16_INTEL 0x0 -#define CL_ME_CHROMA_INTRA_PREDICT_ENABLED_INTEL 0x1 -#define CL_ME_LUMA_INTRA_PREDICT_ENABLED_INTEL 0x2 -#define CL_ME_SKIP_BLOCK_TYPE_8x8_INTEL 0x4 - -#define CL_ME_FORWARD_INPUT_MODE_INTEL 0x1 -#define CL_ME_BACKWARD_INPUT_MODE_INTEL 0x2 -#define CL_ME_BIDIRECTION_INPUT_MODE_INTEL 0x3 - -#define CL_ME_BIDIR_WEIGHT_QUARTER_INTEL 16 -#define CL_ME_BIDIR_WEIGHT_THIRD_INTEL 21 -#define CL_ME_BIDIR_WEIGHT_HALF_INTEL 32 -#define CL_ME_BIDIR_WEIGHT_TWO_THIRD_INTEL 43 -#define CL_ME_BIDIR_WEIGHT_THREE_QUARTER_INTEL 48 - -#define CL_ME_COST_PENALTY_NONE_INTEL 0x0 -#define CL_ME_COST_PENALTY_LOW_INTEL 0x1 -#define CL_ME_COST_PENALTY_NORMAL_INTEL 0x2 -#define CL_ME_COST_PENALTY_HIGH_INTEL 0x3 - -#define CL_ME_COST_PRECISION_QPEL_INTEL 0x0 -#define CL_ME_COST_PRECISION_HPEL_INTEL 0x1 -#define CL_ME_COST_PRECISION_PEL_INTEL 0x2 -#define CL_ME_COST_PRECISION_DPEL_INTEL 0x3 - -#define CL_ME_LUMA_PREDICTOR_MODE_VERTICAL_INTEL 0x0 -#define CL_ME_LUMA_PREDICTOR_MODE_HORIZONTAL_INTEL 0x1 -#define CL_ME_LUMA_PREDICTOR_MODE_DC_INTEL 0x2 -#define CL_ME_LUMA_PREDICTOR_MODE_DIAGONAL_DOWN_LEFT_INTEL 0x3 - -#define CL_ME_LUMA_PREDICTOR_MODE_DIAGONAL_DOWN_RIGHT_INTEL 0x4 -#define CL_ME_LUMA_PREDICTOR_MODE_PLANE_INTEL 0x4 -#define CL_ME_LUMA_PREDICTOR_MODE_VERTICAL_RIGHT_INTEL 0x5 -#define CL_ME_LUMA_PREDICTOR_MODE_HORIZONTAL_DOWN_INTEL 0x6 -#define CL_ME_LUMA_PREDICTOR_MODE_VERTICAL_LEFT_INTEL 0x7 -#define CL_ME_LUMA_PREDICTOR_MODE_HORIZONTAL_UP_INTEL 0x8 - -#define CL_ME_CHROMA_PREDICTOR_MODE_DC_INTEL 0x0 -#define CL_ME_CHROMA_PREDICTOR_MODE_HORIZONTAL_INTEL 0x1 -#define CL_ME_CHROMA_PREDICTOR_MODE_VERTICAL_INTEL 0x2 -#define CL_ME_CHROMA_PREDICTOR_MODE_PLANE_INTEL 0x3 - -/* cl_device_info */ -#define CL_DEVICE_ME_VERSION_INTEL 0x407E - -#define CL_ME_VERSION_LEGACY_INTEL 0x0 -#define CL_ME_VERSION_ADVANCED_VER_1_INTEL 0x1 -#define CL_ME_VERSION_ADVANCED_VER_2_INTEL 0x2 - -extern CL_API_ENTRY cl_accelerator_intel CL_API_CALL -clCreateAcceleratorINTEL( - cl_context context, - cl_accelerator_type_intel accelerator_type, - size_t descriptor_size, - const void* descriptor, - cl_int* errcode_ret) CL_EXT_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_accelerator_intel (CL_API_CALL *clCreateAcceleratorINTEL_fn)( - cl_context context, - cl_accelerator_type_intel accelerator_type, - size_t descriptor_size, - const void* descriptor, - cl_int* errcode_ret) CL_EXT_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetAcceleratorInfoINTEL( - cl_accelerator_intel accelerator, - cl_accelerator_info_intel param_name, - size_t param_value_size, - void* param_value, - size_t* param_value_size_ret) CL_EXT_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clGetAcceleratorInfoINTEL_fn)( - cl_accelerator_intel accelerator, - cl_accelerator_info_intel param_name, - size_t param_value_size, - void* param_value, - size_t* param_value_size_ret) CL_EXT_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clRetainAcceleratorINTEL( - cl_accelerator_intel accelerator) CL_EXT_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clRetainAcceleratorINTEL_fn)( - cl_accelerator_intel accelerator) CL_EXT_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clReleaseAcceleratorINTEL( - cl_accelerator_intel accelerator) CL_EXT_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clReleaseAcceleratorINTEL_fn)( - cl_accelerator_intel accelerator) CL_EXT_SUFFIX__VERSION_1_2; - -/****************************************** -* cl_intel_simultaneous_sharing extension * -*******************************************/ - -#define cl_intel_simultaneous_sharing 1 - -#define CL_DEVICE_SIMULTANEOUS_INTEROPS_INTEL 0x4104 -#define CL_DEVICE_NUM_SIMULTANEOUS_INTEROPS_INTEL 0x4105 - -/*********************************** -* cl_intel_egl_image_yuv extension * -************************************/ - -#define cl_intel_egl_image_yuv 1 - -#define CL_EGL_YUV_PLANE_INTEL 0x4107 - -/******************************** -* cl_intel_packed_yuv extension * -*********************************/ - -#define cl_intel_packed_yuv 1 - -#define CL_YUYV_INTEL 0x4076 -#define CL_UYVY_INTEL 0x4077 -#define CL_YVYU_INTEL 0x4078 -#define CL_VYUY_INTEL 0x4079 - -/******************************************** -* cl_intel_required_subgroup_size extension * -*********************************************/ - -#define cl_intel_required_subgroup_size 1 - -#define CL_DEVICE_SUB_GROUP_SIZES_INTEL 0x4108 -#define CL_KERNEL_SPILL_MEM_SIZE_INTEL 0x4109 -#define CL_KERNEL_COMPILE_SUB_GROUP_SIZE_INTEL 0x410A - -/**************************************** -* cl_intel_driver_diagnostics extension * -*****************************************/ - -#define cl_intel_driver_diagnostics 1 - -typedef cl_uint cl_diagnostics_verbose_level; - -#define CL_CONTEXT_SHOW_DIAGNOSTICS_INTEL 0x4106 - -#define CL_CONTEXT_DIAGNOSTICS_LEVEL_ALL_INTEL ( 0xff ) -#define CL_CONTEXT_DIAGNOSTICS_LEVEL_GOOD_INTEL ( 1 ) -#define CL_CONTEXT_DIAGNOSTICS_LEVEL_BAD_INTEL ( 1 << 1 ) -#define CL_CONTEXT_DIAGNOSTICS_LEVEL_NEUTRAL_INTEL ( 1 << 2 ) - -/******************************** -* cl_intel_planar_yuv extension * -*********************************/ - -#define CL_NV12_INTEL 0x410E - -#define CL_MEM_NO_ACCESS_INTEL ( 1 << 24 ) -#define CL_MEM_ACCESS_FLAGS_UNRESTRICTED_INTEL ( 1 << 25 ) - -#define CL_DEVICE_PLANAR_YUV_MAX_WIDTH_INTEL 0x417E -#define CL_DEVICE_PLANAR_YUV_MAX_HEIGHT_INTEL 0x417F - -/******************************************************* -* cl_intel_device_side_avc_motion_estimation extension * -********************************************************/ - -#define CL_DEVICE_AVC_ME_VERSION_INTEL 0x410B -#define CL_DEVICE_AVC_ME_SUPPORTS_TEXTURE_SAMPLER_USE_INTEL 0x410C -#define CL_DEVICE_AVC_ME_SUPPORTS_PREEMPTION_INTEL 0x410D - -#define CL_AVC_ME_VERSION_0_INTEL 0x0 /* No support. */ -#define CL_AVC_ME_VERSION_1_INTEL 0x1 /* First supported version. */ - -#define CL_AVC_ME_MAJOR_16x16_INTEL 0x0 -#define CL_AVC_ME_MAJOR_16x8_INTEL 0x1 -#define CL_AVC_ME_MAJOR_8x16_INTEL 0x2 -#define CL_AVC_ME_MAJOR_8x8_INTEL 0x3 - -#define CL_AVC_ME_MINOR_8x8_INTEL 0x0 -#define CL_AVC_ME_MINOR_8x4_INTEL 0x1 -#define CL_AVC_ME_MINOR_4x8_INTEL 0x2 -#define CL_AVC_ME_MINOR_4x4_INTEL 0x3 - -#define CL_AVC_ME_MAJOR_FORWARD_INTEL 0x0 -#define CL_AVC_ME_MAJOR_BACKWARD_INTEL 0x1 -#define CL_AVC_ME_MAJOR_BIDIRECTIONAL_INTEL 0x2 - -#define CL_AVC_ME_PARTITION_MASK_ALL_INTEL 0x0 -#define CL_AVC_ME_PARTITION_MASK_16x16_INTEL 0x7E -#define CL_AVC_ME_PARTITION_MASK_16x8_INTEL 0x7D -#define CL_AVC_ME_PARTITION_MASK_8x16_INTEL 0x7B -#define CL_AVC_ME_PARTITION_MASK_8x8_INTEL 0x77 -#define CL_AVC_ME_PARTITION_MASK_8x4_INTEL 0x6F -#define CL_AVC_ME_PARTITION_MASK_4x8_INTEL 0x5F -#define CL_AVC_ME_PARTITION_MASK_4x4_INTEL 0x3F - -#define CL_AVC_ME_SEARCH_WINDOW_EXHAUSTIVE_INTEL 0x0 -#define CL_AVC_ME_SEARCH_WINDOW_SMALL_INTEL 0x1 -#define CL_AVC_ME_SEARCH_WINDOW_TINY_INTEL 0x2 -#define CL_AVC_ME_SEARCH_WINDOW_EXTRA_TINY_INTEL 0x3 -#define CL_AVC_ME_SEARCH_WINDOW_DIAMOND_INTEL 0x4 -#define CL_AVC_ME_SEARCH_WINDOW_LARGE_DIAMOND_INTEL 0x5 -#define CL_AVC_ME_SEARCH_WINDOW_RESERVED0_INTEL 0x6 -#define CL_AVC_ME_SEARCH_WINDOW_RESERVED1_INTEL 0x7 -#define CL_AVC_ME_SEARCH_WINDOW_CUSTOM_INTEL 0x8 -#define CL_AVC_ME_SEARCH_WINDOW_16x12_RADIUS_INTEL 0x9 -#define CL_AVC_ME_SEARCH_WINDOW_4x4_RADIUS_INTEL 0x2 -#define CL_AVC_ME_SEARCH_WINDOW_2x2_RADIUS_INTEL 0xa - -#define CL_AVC_ME_SAD_ADJUST_MODE_NONE_INTEL 0x0 -#define CL_AVC_ME_SAD_ADJUST_MODE_HAAR_INTEL 0x2 - -#define CL_AVC_ME_SUBPIXEL_MODE_INTEGER_INTEL 0x0 -#define CL_AVC_ME_SUBPIXEL_MODE_HPEL_INTEL 0x1 -#define CL_AVC_ME_SUBPIXEL_MODE_QPEL_INTEL 0x3 - -#define CL_AVC_ME_COST_PRECISION_QPEL_INTEL 0x0 -#define CL_AVC_ME_COST_PRECISION_HPEL_INTEL 0x1 -#define CL_AVC_ME_COST_PRECISION_PEL_INTEL 0x2 -#define CL_AVC_ME_COST_PRECISION_DPEL_INTEL 0x3 - -#define CL_AVC_ME_BIDIR_WEIGHT_QUARTER_INTEL 0x10 -#define CL_AVC_ME_BIDIR_WEIGHT_THIRD_INTEL 0x15 -#define CL_AVC_ME_BIDIR_WEIGHT_HALF_INTEL 0x20 -#define CL_AVC_ME_BIDIR_WEIGHT_TWO_THIRD_INTEL 0x2B -#define CL_AVC_ME_BIDIR_WEIGHT_THREE_QUARTER_INTEL 0x30 - -#define CL_AVC_ME_BORDER_REACHED_LEFT_INTEL 0x0 -#define CL_AVC_ME_BORDER_REACHED_RIGHT_INTEL 0x2 -#define CL_AVC_ME_BORDER_REACHED_TOP_INTEL 0x4 -#define CL_AVC_ME_BORDER_REACHED_BOTTOM_INTEL 0x8 - -#define CL_AVC_ME_SKIP_BLOCK_PARTITION_16x16_INTEL 0x0 -#define CL_AVC_ME_SKIP_BLOCK_PARTITION_8x8_INTEL 0x4000 - -#define CL_AVC_ME_SKIP_BLOCK_16x16_FORWARD_ENABLE_INTEL ( 0x1 << 24 ) -#define CL_AVC_ME_SKIP_BLOCK_16x16_BACKWARD_ENABLE_INTEL ( 0x2 << 24 ) -#define CL_AVC_ME_SKIP_BLOCK_16x16_DUAL_ENABLE_INTEL ( 0x3 << 24 ) -#define CL_AVC_ME_SKIP_BLOCK_8x8_FORWARD_ENABLE_INTEL ( 0x55 << 24 ) -#define CL_AVC_ME_SKIP_BLOCK_8x8_BACKWARD_ENABLE_INTEL ( 0xAA << 24 ) -#define CL_AVC_ME_SKIP_BLOCK_8x8_DUAL_ENABLE_INTEL ( 0xFF << 24 ) -#define CL_AVC_ME_SKIP_BLOCK_8x8_0_FORWARD_ENABLE_INTEL ( 0x1 << 24 ) -#define CL_AVC_ME_SKIP_BLOCK_8x8_0_BACKWARD_ENABLE_INTEL ( 0x2 << 24 ) -#define CL_AVC_ME_SKIP_BLOCK_8x8_1_FORWARD_ENABLE_INTEL ( 0x1 << 26 ) -#define CL_AVC_ME_SKIP_BLOCK_8x8_1_BACKWARD_ENABLE_INTEL ( 0x2 << 26 ) -#define CL_AVC_ME_SKIP_BLOCK_8x8_2_FORWARD_ENABLE_INTEL ( 0x1 << 28 ) -#define CL_AVC_ME_SKIP_BLOCK_8x8_2_BACKWARD_ENABLE_INTEL ( 0x2 << 28 ) -#define CL_AVC_ME_SKIP_BLOCK_8x8_3_FORWARD_ENABLE_INTEL ( 0x1 << 30 ) -#define CL_AVC_ME_SKIP_BLOCK_8x8_3_BACKWARD_ENABLE_INTEL ( 0x2 << 30 ) - -#define CL_AVC_ME_BLOCK_BASED_SKIP_4x4_INTEL 0x00 -#define CL_AVC_ME_BLOCK_BASED_SKIP_8x8_INTEL 0x80 - -#define CL_AVC_ME_INTRA_16x16_INTEL 0x0 -#define CL_AVC_ME_INTRA_8x8_INTEL 0x1 -#define CL_AVC_ME_INTRA_4x4_INTEL 0x2 - -#define CL_AVC_ME_INTRA_LUMA_PARTITION_MASK_16x16_INTEL 0x6 -#define CL_AVC_ME_INTRA_LUMA_PARTITION_MASK_8x8_INTEL 0x5 -#define CL_AVC_ME_INTRA_LUMA_PARTITION_MASK_4x4_INTEL 0x3 - -#define CL_AVC_ME_INTRA_NEIGHBOR_LEFT_MASK_ENABLE_INTEL 0x60 -#define CL_AVC_ME_INTRA_NEIGHBOR_UPPER_MASK_ENABLE_INTEL 0x10 -#define CL_AVC_ME_INTRA_NEIGHBOR_UPPER_RIGHT_MASK_ENABLE_INTEL 0x8 -#define CL_AVC_ME_INTRA_NEIGHBOR_UPPER_LEFT_MASK_ENABLE_INTEL 0x4 - -#define CL_AVC_ME_LUMA_PREDICTOR_MODE_VERTICAL_INTEL 0x0 -#define CL_AVC_ME_LUMA_PREDICTOR_MODE_HORIZONTAL_INTEL 0x1 -#define CL_AVC_ME_LUMA_PREDICTOR_MODE_DC_INTEL 0x2 -#define CL_AVC_ME_LUMA_PREDICTOR_MODE_DIAGONAL_DOWN_LEFT_INTEL 0x3 -#define CL_AVC_ME_LUMA_PREDICTOR_MODE_DIAGONAL_DOWN_RIGHT_INTEL 0x4 -#define CL_AVC_ME_LUMA_PREDICTOR_MODE_PLANE_INTEL 0x4 -#define CL_AVC_ME_LUMA_PREDICTOR_MODE_VERTICAL_RIGHT_INTEL 0x5 -#define CL_AVC_ME_LUMA_PREDICTOR_MODE_HORIZONTAL_DOWN_INTEL 0x6 -#define CL_AVC_ME_LUMA_PREDICTOR_MODE_VERTICAL_LEFT_INTEL 0x7 -#define CL_AVC_ME_LUMA_PREDICTOR_MODE_HORIZONTAL_UP_INTEL 0x8 -#define CL_AVC_ME_CHROMA_PREDICTOR_MODE_DC_INTEL 0x0 -#define CL_AVC_ME_CHROMA_PREDICTOR_MODE_HORIZONTAL_INTEL 0x1 -#define CL_AVC_ME_CHROMA_PREDICTOR_MODE_VERTICAL_INTEL 0x2 -#define CL_AVC_ME_CHROMA_PREDICTOR_MODE_PLANE_INTEL 0x3 - -#define CL_AVC_ME_FRAME_FORWARD_INTEL 0x1 -#define CL_AVC_ME_FRAME_BACKWARD_INTEL 0x2 -#define CL_AVC_ME_FRAME_DUAL_INTEL 0x3 - -#define CL_AVC_ME_SLICE_TYPE_PRED_INTEL 0x0 -#define CL_AVC_ME_SLICE_TYPE_BPRED_INTEL 0x1 -#define CL_AVC_ME_SLICE_TYPE_INTRA_INTEL 0x2 - -#define CL_AVC_ME_INTERLACED_SCAN_TOP_FIELD_INTEL 0x0 -#define CL_AVC_ME_INTERLACED_SCAN_BOTTOM_FIELD_INTEL 0x1 - -/******************************************* -* cl_intel_unified_shared_memory extension * -********************************************/ - -/* These APIs are in sync with Revision O of the cl_intel_unified_shared_memory spec! */ - -#define cl_intel_unified_shared_memory 1 - -/* cl_device_info */ -#define CL_DEVICE_HOST_MEM_CAPABILITIES_INTEL 0x4190 -#define CL_DEVICE_DEVICE_MEM_CAPABILITIES_INTEL 0x4191 -#define CL_DEVICE_SINGLE_DEVICE_SHARED_MEM_CAPABILITIES_INTEL 0x4192 -#define CL_DEVICE_CROSS_DEVICE_SHARED_MEM_CAPABILITIES_INTEL 0x4193 -#define CL_DEVICE_SHARED_SYSTEM_MEM_CAPABILITIES_INTEL 0x4194 - -typedef cl_bitfield cl_device_unified_shared_memory_capabilities_intel; - -/* cl_device_unified_shared_memory_capabilities_intel - bitfield */ -#define CL_UNIFIED_SHARED_MEMORY_ACCESS_INTEL (1 << 0) -#define CL_UNIFIED_SHARED_MEMORY_ATOMIC_ACCESS_INTEL (1 << 1) -#define CL_UNIFIED_SHARED_MEMORY_CONCURRENT_ACCESS_INTEL (1 << 2) -#define CL_UNIFIED_SHARED_MEMORY_CONCURRENT_ATOMIC_ACCESS_INTEL (1 << 3) - -typedef cl_bitfield cl_mem_properties_intel; - -/* cl_mem_properties_intel */ -#define CL_MEM_ALLOC_FLAGS_INTEL 0x4195 - -typedef cl_bitfield cl_mem_alloc_flags_intel; - -/* cl_mem_alloc_flags_intel - bitfield */ -#define CL_MEM_ALLOC_WRITE_COMBINED_INTEL (1 << 0) - -typedef cl_uint cl_mem_info_intel; - -/* cl_mem_alloc_info_intel */ -#define CL_MEM_ALLOC_TYPE_INTEL 0x419A -#define CL_MEM_ALLOC_BASE_PTR_INTEL 0x419B -#define CL_MEM_ALLOC_SIZE_INTEL 0x419C -#define CL_MEM_ALLOC_DEVICE_INTEL 0x419D -/* Enum values 0x419E-0x419F are reserved for future queries. */ - -typedef cl_uint cl_unified_shared_memory_type_intel; - -/* cl_unified_shared_memory_type_intel */ -#define CL_MEM_TYPE_UNKNOWN_INTEL 0x4196 -#define CL_MEM_TYPE_HOST_INTEL 0x4197 -#define CL_MEM_TYPE_DEVICE_INTEL 0x4198 -#define CL_MEM_TYPE_SHARED_INTEL 0x4199 - -typedef cl_uint cl_mem_advice_intel; - -/* cl_mem_advice_intel */ -/* Enum values 0x4208-0x420F are reserved for future memory advices. */ - -/* cl_kernel_exec_info */ -#define CL_KERNEL_EXEC_INFO_INDIRECT_HOST_ACCESS_INTEL 0x4200 -#define CL_KERNEL_EXEC_INFO_INDIRECT_DEVICE_ACCESS_INTEL 0x4201 -#define CL_KERNEL_EXEC_INFO_INDIRECT_SHARED_ACCESS_INTEL 0x4202 -#define CL_KERNEL_EXEC_INFO_USM_PTRS_INTEL 0x4203 - -/* cl_command_type */ -#define CL_COMMAND_MEMFILL_INTEL 0x4204 -#define CL_COMMAND_MEMCPY_INTEL 0x4205 -#define CL_COMMAND_MIGRATEMEM_INTEL 0x4206 -#define CL_COMMAND_MEMADVISE_INTEL 0x4207 - -extern CL_API_ENTRY void* CL_API_CALL -clHostMemAllocINTEL( - cl_context context, - const cl_mem_properties_intel* properties, - size_t size, - cl_uint alignment, - cl_int* errcode_ret); - -typedef CL_API_ENTRY void* (CL_API_CALL * -clHostMemAllocINTEL_fn)( - cl_context context, - const cl_mem_properties_intel* properties, - size_t size, - cl_uint alignment, - cl_int* errcode_ret); - -extern CL_API_ENTRY void* CL_API_CALL -clDeviceMemAllocINTEL( - cl_context context, - cl_device_id device, - const cl_mem_properties_intel* properties, - size_t size, - cl_uint alignment, - cl_int* errcode_ret); - -typedef CL_API_ENTRY void* (CL_API_CALL * -clDeviceMemAllocINTEL_fn)( - cl_context context, - cl_device_id device, - const cl_mem_properties_intel* properties, - size_t size, - cl_uint alignment, - cl_int* errcode_ret); - -extern CL_API_ENTRY void* CL_API_CALL -clSharedMemAllocINTEL( - cl_context context, - cl_device_id device, - const cl_mem_properties_intel* properties, - size_t size, - cl_uint alignment, - cl_int* errcode_ret); - -typedef CL_API_ENTRY void* (CL_API_CALL * -clSharedMemAllocINTEL_fn)( - cl_context context, - cl_device_id device, - const cl_mem_properties_intel* properties, - size_t size, - cl_uint alignment, - cl_int* errcode_ret); - -extern CL_API_ENTRY cl_int CL_API_CALL -clMemFreeINTEL( - cl_context context, - void* ptr); - -typedef CL_API_ENTRY cl_int (CL_API_CALL * -clMemFreeINTEL_fn)( - cl_context context, - void* ptr); - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetMemAllocInfoINTEL( - cl_context context, - const void* ptr, - cl_mem_info_intel param_name, - size_t param_value_size, - void* param_value, - size_t* param_value_size_ret); - -typedef CL_API_ENTRY cl_int (CL_API_CALL * -clGetMemAllocInfoINTEL_fn)( - cl_context context, - const void* ptr, - cl_mem_info_intel param_name, - size_t param_value_size, - void* param_value, - size_t* param_value_size_ret); - -extern CL_API_ENTRY cl_int CL_API_CALL -clSetKernelArgMemPointerINTEL( - cl_kernel kernel, - cl_uint arg_index, - const void* arg_value); - -typedef CL_API_ENTRY cl_int (CL_API_CALL * -clSetKernelArgMemPointerINTEL_fn)( - cl_kernel kernel, - cl_uint arg_index, - const void* arg_value); - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueMemsetINTEL( /* Deprecated */ - cl_command_queue command_queue, - void* dst_ptr, - cl_int value, - size_t size, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event); - -typedef CL_API_ENTRY cl_int (CL_API_CALL * -clEnqueueMemsetINTEL_fn)( /* Deprecated */ - cl_command_queue command_queue, - void* dst_ptr, - cl_int value, - size_t size, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event); - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueMemFillINTEL( - cl_command_queue command_queue, - void* dst_ptr, - const void* pattern, - size_t pattern_size, - size_t size, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event); - -typedef CL_API_ENTRY cl_int (CL_API_CALL * -clEnqueueMemFillINTEL_fn)( - cl_command_queue command_queue, - void* dst_ptr, - const void* pattern, - size_t pattern_size, - size_t size, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event); - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueMemcpyINTEL( - cl_command_queue command_queue, - cl_bool blocking, - void* dst_ptr, - const void* src_ptr, - size_t size, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event); - -typedef CL_API_ENTRY cl_int (CL_API_CALL * -clEnqueueMemcpyINTEL_fn)( - cl_command_queue command_queue, - cl_bool blocking, - void* dst_ptr, - const void* src_ptr, - size_t size, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event); - -#ifdef CL_VERSION_1_2 - -/* Because these APIs use cl_mem_migration_flags, they require - OpenCL 1.2: */ - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueMigrateMemINTEL( - cl_command_queue command_queue, - const void* ptr, - size_t size, - cl_mem_migration_flags flags, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event); - -typedef CL_API_ENTRY cl_int (CL_API_CALL * -clEnqueueMigrateMemINTEL_fn)( - cl_command_queue command_queue, - const void* ptr, - size_t size, - cl_mem_migration_flags flags, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event); - -#endif - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueMemAdviseINTEL( - cl_command_queue command_queue, - const void* ptr, - size_t size, - cl_mem_advice_intel advice, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event); - -typedef CL_API_ENTRY cl_int (CL_API_CALL * -clEnqueueMemAdviseINTEL_fn)( - cl_command_queue command_queue, - const void* ptr, - size_t size, - cl_mem_advice_intel advice, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event); - -#ifdef __cplusplus -} -#endif - -#endif /* __CL_EXT_INTEL_H */ diff --git a/src/CL/cl_gl.h b/src/CL/cl_gl.h deleted file mode 100644 index b587f02..0000000 --- a/src/CL/cl_gl.h +++ /dev/null @@ -1,159 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008-2020 The Khronos Group Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -#ifndef __OPENCL_CL_GL_H -#define __OPENCL_CL_GL_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef cl_uint cl_gl_object_type; -typedef cl_uint cl_gl_texture_info; -typedef cl_uint cl_gl_platform_info; -typedef struct __GLsync *cl_GLsync; - -/* cl_gl_object_type = 0x2000 - 0x200F enum values are currently taken */ -#define CL_GL_OBJECT_BUFFER 0x2000 -#define CL_GL_OBJECT_TEXTURE2D 0x2001 -#define CL_GL_OBJECT_TEXTURE3D 0x2002 -#define CL_GL_OBJECT_RENDERBUFFER 0x2003 -#ifdef CL_VERSION_1_2 -#define CL_GL_OBJECT_TEXTURE2D_ARRAY 0x200E -#define CL_GL_OBJECT_TEXTURE1D 0x200F -#define CL_GL_OBJECT_TEXTURE1D_ARRAY 0x2010 -#define CL_GL_OBJECT_TEXTURE_BUFFER 0x2011 -#endif - -/* cl_gl_texture_info */ -#define CL_GL_TEXTURE_TARGET 0x2004 -#define CL_GL_MIPMAP_LEVEL 0x2005 -#ifdef CL_VERSION_1_2 -#define CL_GL_NUM_SAMPLES 0x2012 -#endif - - -extern CL_API_ENTRY cl_mem CL_API_CALL -clCreateFromGLBuffer(cl_context context, - cl_mem_flags flags, - cl_GLuint bufobj, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_2 - -extern CL_API_ENTRY cl_mem CL_API_CALL -clCreateFromGLTexture(cl_context context, - cl_mem_flags flags, - cl_GLenum target, - cl_GLint miplevel, - cl_GLuint texture, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_2; - -#endif - -extern CL_API_ENTRY cl_mem CL_API_CALL -clCreateFromGLRenderbuffer(cl_context context, - cl_mem_flags flags, - cl_GLuint renderbuffer, - cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetGLObjectInfo(cl_mem memobj, - cl_gl_object_type * gl_object_type, - cl_GLuint * gl_object_name) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetGLTextureInfo(cl_mem memobj, - cl_gl_texture_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueAcquireGLObjects(cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem * mem_objects, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueReleaseGLObjects(cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem * mem_objects, - cl_uint num_events_in_wait_list, - const cl_event * event_wait_list, - cl_event * event) CL_API_SUFFIX__VERSION_1_0; - - -/* Deprecated OpenCL 1.1 APIs */ -extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_mem CL_API_CALL -clCreateFromGLTexture2D(cl_context context, - cl_mem_flags flags, - cl_GLenum target, - cl_GLint miplevel, - cl_GLuint texture, - cl_int * errcode_ret) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; - -extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_mem CL_API_CALL -clCreateFromGLTexture3D(cl_context context, - cl_mem_flags flags, - cl_GLenum target, - cl_GLint miplevel, - cl_GLuint texture, - cl_int * errcode_ret) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; - -/* cl_khr_gl_sharing extension */ - -#define cl_khr_gl_sharing 1 - -typedef cl_uint cl_gl_context_info; - -/* Additional Error Codes */ -#define CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR -1000 - -/* cl_gl_context_info */ -#define CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR 0x2006 -#define CL_DEVICES_FOR_GL_CONTEXT_KHR 0x2007 - -/* Additional cl_context_properties */ -#define CL_GL_CONTEXT_KHR 0x2008 -#define CL_EGL_DISPLAY_KHR 0x2009 -#define CL_GLX_DISPLAY_KHR 0x200A -#define CL_WGL_HDC_KHR 0x200B -#define CL_CGL_SHAREGROUP_KHR 0x200C - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetGLContextInfoKHR(const cl_context_properties * properties, - cl_gl_context_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clGetGLContextInfoKHR_fn)( - const cl_context_properties * properties, - cl_gl_context_info param_name, - size_t param_value_size, - void * param_value, - size_t * param_value_size_ret); - -#ifdef __cplusplus -} -#endif - -#endif /* __OPENCL_CL_GL_H */ diff --git a/src/CL/cl_gl_ext.h b/src/CL/cl_gl_ext.h deleted file mode 100644 index 9bb7540..0000000 --- a/src/CL/cl_gl_ext.h +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008-2020 The Khronos Group Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -#ifndef __OPENCL_CL_GL_EXT_H -#define __OPENCL_CL_GL_EXT_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/* - * cl_khr_gl_event extension - */ -#define CL_COMMAND_GL_FENCE_SYNC_OBJECT_KHR 0x200D - -extern CL_API_ENTRY cl_event CL_API_CALL -clCreateEventFromGLsyncKHR(cl_context context, - cl_GLsync cl_GLsync, - cl_int * errcode_ret) CL_EXT_SUFFIX__VERSION_1_1; - -#ifdef __cplusplus -} -#endif - -#endif /* __OPENCL_CL_GL_EXT_H */ diff --git a/src/CL/cl_half.h b/src/CL/cl_half.h deleted file mode 100644 index f748d9e..0000000 --- a/src/CL/cl_half.h +++ /dev/null @@ -1,440 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019-2020 The Khronos Group Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -/** - * This is a header-only utility library that provides OpenCL host code with - * routines for converting to/from cl_half values. - * - * Example usage: - * - * #include - * ... - * cl_half h = cl_half_from_float(0.5f, CL_HALF_RTE); - * cl_float f = cl_half_to_float(h); - */ - -#ifndef OPENCL_CL_HALF_H -#define OPENCL_CL_HALF_H - -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * Rounding mode used when converting to cl_half. - */ -typedef enum -{ - CL_HALF_RTE, // round to nearest even - CL_HALF_RTZ, // round towards zero - CL_HALF_RTP, // round towards positive infinity - CL_HALF_RTN, // round towards negative infinity -} cl_half_rounding_mode; - - -/* Private utility macros. */ -#define CL_HALF_EXP_MASK 0x7C00 -#define CL_HALF_MAX_FINITE_MAG 0x7BFF - - -/* - * Utility to deal with values that overflow when converting to half precision. - */ -static inline cl_half cl_half_handle_overflow(cl_half_rounding_mode rounding_mode, - uint16_t sign) -{ - if (rounding_mode == CL_HALF_RTZ) - { - // Round overflow towards zero -> largest finite number (preserving sign) - return (sign << 15) | CL_HALF_MAX_FINITE_MAG; - } - else if (rounding_mode == CL_HALF_RTP && sign) - { - // Round negative overflow towards positive infinity -> most negative finite number - return (1 << 15) | CL_HALF_MAX_FINITE_MAG; - } - else if (rounding_mode == CL_HALF_RTN && !sign) - { - // Round positive overflow towards negative infinity -> largest finite number - return CL_HALF_MAX_FINITE_MAG; - } - - // Overflow to infinity - return (sign << 15) | CL_HALF_EXP_MASK; -} - -/* - * Utility to deal with values that underflow when converting to half precision. - */ -static inline cl_half cl_half_handle_underflow(cl_half_rounding_mode rounding_mode, - uint16_t sign) -{ - if (rounding_mode == CL_HALF_RTP && !sign) - { - // Round underflow towards positive infinity -> smallest positive value - return (sign << 15) | 1; - } - else if (rounding_mode == CL_HALF_RTN && sign) - { - // Round underflow towards negative infinity -> largest negative value - return (sign << 15) | 1; - } - - // Flush to zero - return (sign << 15); -} - - -/** - * Convert a cl_float to a cl_half. - */ -static inline cl_half cl_half_from_float(cl_float f, cl_half_rounding_mode rounding_mode) -{ - // Type-punning to get direct access to underlying bits - union - { - cl_float f; - uint32_t i; - } f32; - f32.f = f; - - // Extract sign bit - uint16_t sign = f32.i >> 31; - - // Extract FP32 exponent and mantissa - uint32_t f_exp = (f32.i >> (CL_FLT_MANT_DIG - 1)) & 0xFF; - uint32_t f_mant = f32.i & ((1 << (CL_FLT_MANT_DIG - 1)) - 1); - - // Remove FP32 exponent bias - int32_t exp = f_exp - CL_FLT_MAX_EXP + 1; - - // Add FP16 exponent bias - uint16_t h_exp = exp + CL_HALF_MAX_EXP - 1; - - // Position of the bit that will become the FP16 mantissa LSB - uint32_t lsb_pos = CL_FLT_MANT_DIG - CL_HALF_MANT_DIG; - - // Check for NaN / infinity - if (f_exp == 0xFF) - { - if (f_mant) - { - // NaN -> propagate mantissa and silence it - uint16_t h_mant = f_mant >> lsb_pos; - h_mant |= 0x200; - return (sign << 15) | CL_HALF_EXP_MASK | h_mant; - } - else - { - // Infinity -> zero mantissa - return (sign << 15) | CL_HALF_EXP_MASK; - } - } - - // Check for zero - if (!f_exp && !f_mant) - { - return (sign << 15); - } - - // Check for overflow - if (exp >= CL_HALF_MAX_EXP) - { - return cl_half_handle_overflow(rounding_mode, sign); - } - - // Check for underflow - if (exp < (CL_HALF_MIN_EXP - CL_HALF_MANT_DIG - 1)) - { - return cl_half_handle_underflow(rounding_mode, sign); - } - - // Check for value that will become denormal - if (exp < -14) - { - // Denormal -> include the implicit 1 from the FP32 mantissa - h_exp = 0; - f_mant |= 1 << (CL_FLT_MANT_DIG - 1); - - // Mantissa shift amount depends on exponent - lsb_pos = -exp + (CL_FLT_MANT_DIG - 25); - } - - // Generate FP16 mantissa by shifting FP32 mantissa - uint16_t h_mant = f_mant >> lsb_pos; - - // Check whether we need to round - uint32_t halfway = 1 << (lsb_pos - 1); - uint32_t mask = (halfway << 1) - 1; - switch (rounding_mode) - { - case CL_HALF_RTE: - if ((f_mant & mask) > halfway) - { - // More than halfway -> round up - h_mant += 1; - } - else if ((f_mant & mask) == halfway) - { - // Exactly halfway -> round to nearest even - if (h_mant & 0x1) - h_mant += 1; - } - break; - case CL_HALF_RTZ: - // Mantissa has already been truncated -> do nothing - break; - case CL_HALF_RTP: - if ((f_mant & mask) && !sign) - { - // Round positive numbers up - h_mant += 1; - } - break; - case CL_HALF_RTN: - if ((f_mant & mask) && sign) - { - // Round negative numbers down - h_mant += 1; - } - break; - } - - // Check for mantissa overflow - if (h_mant & 0x400) - { - h_exp += 1; - h_mant = 0; - } - - return (sign << 15) | (h_exp << 10) | h_mant; -} - - -/** - * Convert a cl_double to a cl_half. - */ -static inline cl_half cl_half_from_double(cl_double d, cl_half_rounding_mode rounding_mode) -{ - // Type-punning to get direct access to underlying bits - union - { - cl_double d; - uint64_t i; - } f64; - f64.d = d; - - // Extract sign bit - uint16_t sign = f64.i >> 63; - - // Extract FP64 exponent and mantissa - uint64_t d_exp = (f64.i >> (CL_DBL_MANT_DIG - 1)) & 0x7FF; - uint64_t d_mant = f64.i & (((uint64_t)1 << (CL_DBL_MANT_DIG - 1)) - 1); - - // Remove FP64 exponent bias - int64_t exp = d_exp - CL_DBL_MAX_EXP + 1; - - // Add FP16 exponent bias - uint16_t h_exp = (uint16_t)(exp + CL_HALF_MAX_EXP - 1); - - // Position of the bit that will become the FP16 mantissa LSB - uint32_t lsb_pos = CL_DBL_MANT_DIG - CL_HALF_MANT_DIG; - - // Check for NaN / infinity - if (d_exp == 0x7FF) - { - if (d_mant) - { - // NaN -> propagate mantissa and silence it - uint16_t h_mant = (uint16_t)(d_mant >> lsb_pos); - h_mant |= 0x200; - return (sign << 15) | CL_HALF_EXP_MASK | h_mant; - } - else - { - // Infinity -> zero mantissa - return (sign << 15) | CL_HALF_EXP_MASK; - } - } - - // Check for zero - if (!d_exp && !d_mant) - { - return (sign << 15); - } - - // Check for overflow - if (exp >= CL_HALF_MAX_EXP) - { - return cl_half_handle_overflow(rounding_mode, sign); - } - - // Check for underflow - if (exp < (CL_HALF_MIN_EXP - CL_HALF_MANT_DIG - 1)) - { - return cl_half_handle_underflow(rounding_mode, sign); - } - - // Check for value that will become denormal - if (exp < -14) - { - // Include the implicit 1 from the FP64 mantissa - h_exp = 0; - d_mant |= (uint64_t)1 << (CL_DBL_MANT_DIG - 1); - - // Mantissa shift amount depends on exponent - lsb_pos = (uint32_t)(-exp + (CL_DBL_MANT_DIG - 25)); - } - - // Generate FP16 mantissa by shifting FP64 mantissa - uint16_t h_mant = (uint16_t)(d_mant >> lsb_pos); - - // Check whether we need to round - uint64_t halfway = (uint64_t)1 << (lsb_pos - 1); - uint64_t mask = (halfway << 1) - 1; - switch (rounding_mode) - { - case CL_HALF_RTE: - if ((d_mant & mask) > halfway) - { - // More than halfway -> round up - h_mant += 1; - } - else if ((d_mant & mask) == halfway) - { - // Exactly halfway -> round to nearest even - if (h_mant & 0x1) - h_mant += 1; - } - break; - case CL_HALF_RTZ: - // Mantissa has already been truncated -> do nothing - break; - case CL_HALF_RTP: - if ((d_mant & mask) && !sign) - { - // Round positive numbers up - h_mant += 1; - } - break; - case CL_HALF_RTN: - if ((d_mant & mask) && sign) - { - // Round negative numbers down - h_mant += 1; - } - break; - } - - // Check for mantissa overflow - if (h_mant & 0x400) - { - h_exp += 1; - h_mant = 0; - } - - return (sign << 15) | (h_exp << 10) | h_mant; -} - - -/** - * Convert a cl_half to a cl_float. - */ -static inline cl_float cl_half_to_float(cl_half h) -{ - // Type-punning to get direct access to underlying bits - union - { - cl_float f; - uint32_t i; - } f32; - - // Extract sign bit - uint16_t sign = h >> 15; - - // Extract FP16 exponent and mantissa - uint16_t h_exp = (h >> (CL_HALF_MANT_DIG - 1)) & 0x1F; - uint16_t h_mant = h & 0x3FF; - - // Remove FP16 exponent bias - int32_t exp = h_exp - CL_HALF_MAX_EXP + 1; - - // Add FP32 exponent bias - uint32_t f_exp = exp + CL_FLT_MAX_EXP - 1; - - // Check for NaN / infinity - if (h_exp == 0x1F) - { - if (h_mant) - { - // NaN -> propagate mantissa and silence it - uint32_t f_mant = h_mant << (CL_FLT_MANT_DIG - CL_HALF_MANT_DIG); - f_mant |= 0x400000; - f32.i = (sign << 31) | 0x7F800000 | f_mant; - return f32.f; - } - else - { - // Infinity -> zero mantissa - f32.i = (sign << 31) | 0x7F800000; - return f32.f; - } - } - - // Check for zero / denormal - if (h_exp == 0) - { - if (h_mant == 0) - { - // Zero -> zero exponent - f_exp = 0; - } - else - { - // Denormal -> normalize it - // - Shift mantissa to make most-significant 1 implicit - // - Adjust exponent accordingly - uint32_t shift = 0; - while ((h_mant & 0x400) == 0) - { - h_mant <<= 1; - shift++; - } - h_mant &= 0x3FF; - f_exp -= shift - 1; - } - } - - f32.i = (sign << 31) | (f_exp << 23) | (h_mant << 13); - return f32.f; -} - - -#undef CL_HALF_EXP_MASK -#undef CL_HALF_MAX_FINITE_MAG - - -#ifdef __cplusplus -} -#endif - - -#endif /* OPENCL_CL_HALF_H */ diff --git a/src/CL/cl_icd.h b/src/CL/cl_icd.h deleted file mode 100644 index 8c74724..0000000 --- a/src/CL/cl_icd.h +++ /dev/null @@ -1,1287 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019-2020 The Khronos Group Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -#ifndef OPENCL_CL_ICD_H -#define OPENCL_CL_ICD_H - -#include -#include -#include -#include - -#if defined(_WIN32) -#include -#include -#include -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * This file contains pointer type definitions for each of the CL API calls as - * well as a type definition for the dispatch table used by the Khronos ICD - * loader (see cl_khr_icd extension specification for background). - */ - -/* API function pointer definitions */ - -// Platform APIs -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetPlatformIDs)( - cl_uint num_entries, cl_platform_id *platforms, - cl_uint *num_platforms) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetPlatformInfo)( - cl_platform_id platform, cl_platform_info param_name, - size_t param_value_size, void *param_value, - size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -// Device APIs -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetDeviceIDs)( - cl_platform_id platform, cl_device_type device_type, cl_uint num_entries, - cl_device_id *devices, cl_uint *num_devices) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetDeviceInfo)( - cl_device_id device, cl_device_info param_name, size_t param_value_size, - void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_2 - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clCreateSubDevices)( - cl_device_id in_device, - const cl_device_partition_property *partition_properties, - cl_uint num_entries, cl_device_id *out_devices, cl_uint *num_devices); - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clRetainDevice)( - cl_device_id device) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clReleaseDevice)( - cl_device_id device) CL_API_SUFFIX__VERSION_1_2; - -#else - -typedef void *cl_api_clCreateSubDevices; -typedef void *cl_api_clRetainDevice; -typedef void *cl_api_clReleaseDevice; - -#endif - -// Context APIs -typedef CL_API_ENTRY cl_context(CL_API_CALL *cl_api_clCreateContext)( - const cl_context_properties *properties, cl_uint num_devices, - const cl_device_id *devices, - void(CL_CALLBACK *pfn_notify)(const char *, const void *, size_t, void *), - void *user_data, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_context(CL_API_CALL *cl_api_clCreateContextFromType)( - const cl_context_properties *properties, cl_device_type device_type, - void(CL_CALLBACK *pfn_notify)(const char *, const void *, size_t, void *), - void *user_data, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clRetainContext)( - cl_context context) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clReleaseContext)( - cl_context context) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetContextInfo)( - cl_context context, cl_context_info param_name, size_t param_value_size, - void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -// Command Queue APIs -typedef CL_API_ENTRY cl_command_queue(CL_API_CALL *cl_api_clCreateCommandQueue)( - cl_context context, cl_device_id device, - cl_command_queue_properties properties, - cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_2_0 - -typedef CL_API_ENTRY -cl_command_queue(CL_API_CALL *cl_api_clCreateCommandQueueWithProperties)( - cl_context /* context */, cl_device_id /* device */, - const cl_queue_properties * /* properties */, - cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_2_0; - -#else - -typedef void *cl_api_clCreateCommandQueueWithProperties; - -#endif - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clRetainCommandQueue)( - cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clReleaseCommandQueue)( - cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetCommandQueueInfo)( - cl_command_queue command_queue, cl_command_queue_info param_name, - size_t param_value_size, void *param_value, - size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -// Memory Object APIs -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateBuffer)( - cl_context context, cl_mem_flags flags, size_t size, void *host_ptr, - cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_2 - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateImage)( - cl_context context, cl_mem_flags flags, const cl_image_format *image_format, - const cl_image_desc *image_desc, void *host_ptr, - cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_2; - -#else - -typedef void *cl_api_clCreateImage; - -#endif - -#ifdef CL_VERSION_3_0 - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateBufferWithProperties)( - cl_context context, const cl_mem_properties *properties, cl_mem_flags flags, - size_t size, void *host_ptr, - cl_int *errcode_ret) CL_API_SUFFIX__VERSION_3_0; - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateImageWithProperties)( - cl_context context, const cl_mem_properties *properties, cl_mem_flags flags, - const cl_image_format *image_format, const cl_image_desc *image_desc, - void *host_ptr, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_3_0; - -#else - -typedef void *cl_api_clCreateBufferWithProperties; -typedef void *cl_api_clCreateImageWithProperties; - -#endif - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clRetainMemObject)( - cl_mem memobj) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clReleaseMemObject)( - cl_mem memobj) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetSupportedImageFormats)( - cl_context context, cl_mem_flags flags, cl_mem_object_type image_type, - cl_uint num_entries, cl_image_format *image_formats, - cl_uint *num_image_formats) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetMemObjectInfo)( - cl_mem memobj, cl_mem_info param_name, size_t param_value_size, - void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetImageInfo)( - cl_mem image, cl_image_info param_name, size_t param_value_size, - void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_2_0 - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreatePipe)( - cl_context /* context */, cl_mem_flags /* flags */, - cl_uint /* pipe_packet_size */, cl_uint /* pipe_max_packets */, - const cl_pipe_properties * /* properties */, - cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_2_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetPipeInfo)( - cl_mem /* pipe */, cl_pipe_info /* param_name */, - size_t /* param_value_size */, void * /* param_value */, - size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_2_0; - -typedef CL_API_ENTRY void *(CL_API_CALL *cl_api_clSVMAlloc)( - cl_context /* context */, cl_svm_mem_flags /* flags */, size_t /* size */, - unsigned int /* alignment */)CL_API_SUFFIX__VERSION_2_0; - -typedef CL_API_ENTRY void(CL_API_CALL *cl_api_clSVMFree)( - cl_context /* context */, - void * /* svm_pointer */) CL_API_SUFFIX__VERSION_2_0; - -#else - -typedef void *cl_api_clCreatePipe; -typedef void *cl_api_clGetPipeInfo; -typedef void *cl_api_clSVMAlloc; -typedef void *cl_api_clSVMFree; - -#endif - -// Sampler APIs -typedef CL_API_ENTRY cl_sampler(CL_API_CALL *cl_api_clCreateSampler)( - cl_context context, cl_bool normalized_coords, - cl_addressing_mode addressing_mode, cl_filter_mode filter_mode, - cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clRetainSampler)( - cl_sampler sampler) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clReleaseSampler)( - cl_sampler sampler) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetSamplerInfo)( - cl_sampler sampler, cl_sampler_info param_name, size_t param_value_size, - void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_2_0 - -typedef CL_API_ENTRY -cl_sampler(CL_API_CALL *cl_api_clCreateSamplerWithProperties)( - cl_context /* context */, - const cl_sampler_properties * /* sampler_properties */, - cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_2_0; - -#else - -typedef void *cl_api_clCreateSamplerWithProperties; - -#endif - -// Program Object APIs -typedef CL_API_ENTRY cl_program(CL_API_CALL *cl_api_clCreateProgramWithSource)( - cl_context context, cl_uint count, const char **strings, - const size_t *lengths, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_program(CL_API_CALL *cl_api_clCreateProgramWithBinary)( - cl_context context, cl_uint num_devices, const cl_device_id *device_list, - const size_t *lengths, const unsigned char **binaries, - cl_int *binary_status, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_2 - -typedef CL_API_ENTRY -cl_program(CL_API_CALL *cl_api_clCreateProgramWithBuiltInKernels)( - cl_context context, cl_uint num_devices, const cl_device_id *device_list, - const char *kernel_names, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_2; - -#else - -typedef void *cl_api_clCreateProgramWithBuiltInKernels; - -#endif - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clRetainProgram)( - cl_program program) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clReleaseProgram)( - cl_program program) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clBuildProgram)( - cl_program program, cl_uint num_devices, const cl_device_id *device_list, - const char *options, - void(CL_CALLBACK *pfn_notify)(cl_program program, void *user_data), - void *user_data) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_2 - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clCompileProgram)( - cl_program program, cl_uint num_devices, const cl_device_id *device_list, - const char *options, cl_uint num_input_headers, - const cl_program *input_headers, const char **header_include_names, - void(CL_CALLBACK *pfn_notify)(cl_program program, void *user_data), - void *user_data) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_program(CL_API_CALL *cl_api_clLinkProgram)( - cl_context context, cl_uint num_devices, const cl_device_id *device_list, - const char *options, cl_uint num_input_programs, - const cl_program *input_programs, - void(CL_CALLBACK *pfn_notify)(cl_program program, void *user_data), - void *user_data, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_2; - -#else - -typedef void *cl_api_clCompileProgram; -typedef void *cl_api_clLinkProgram; - -#endif - -#ifdef CL_VERSION_2_2 - -typedef CL_API_ENTRY -cl_int(CL_API_CALL *cl_api_clSetProgramSpecializationConstant)( - cl_program program, cl_uint spec_id, size_t spec_size, - const void *spec_value) CL_API_SUFFIX__VERSION_2_2; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clSetProgramReleaseCallback)( - cl_program program, - void(CL_CALLBACK *pfn_notify)(cl_program program, void *user_data), - void *user_data) CL_API_SUFFIX__VERSION_2_2; - -#else - -typedef void *cl_api_clSetProgramSpecializationConstant; -typedef void *cl_api_clSetProgramReleaseCallback; - -#endif - -#ifdef CL_VERSION_1_2 - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clUnloadPlatformCompiler)( - cl_platform_id platform) CL_API_SUFFIX__VERSION_1_2; - -#else - -typedef void *cl_api_clUnloadPlatformCompiler; - -#endif - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetProgramInfo)( - cl_program program, cl_program_info param_name, size_t param_value_size, - void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetProgramBuildInfo)( - cl_program program, cl_device_id device, cl_program_build_info param_name, - size_t param_value_size, void *param_value, - size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -// Kernel Object APIs -typedef CL_API_ENTRY cl_kernel(CL_API_CALL *cl_api_clCreateKernel)( - cl_program program, const char *kernel_name, - cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clCreateKernelsInProgram)( - cl_program program, cl_uint num_kernels, cl_kernel *kernels, - cl_uint *num_kernels_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clRetainKernel)( - cl_kernel kernel) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clReleaseKernel)( - cl_kernel kernel) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clSetKernelArg)( - cl_kernel kernel, cl_uint arg_index, size_t arg_size, - const void *arg_value) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetKernelInfo)( - cl_kernel kernel, cl_kernel_info param_name, size_t param_value_size, - void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_2 - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetKernelArgInfo)( - cl_kernel kernel, cl_uint arg_indx, cl_kernel_arg_info param_name, - size_t param_value_size, void *param_value, - size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_2; - -#else - -typedef void *cl_api_clGetKernelArgInfo; - -#endif - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetKernelWorkGroupInfo)( - cl_kernel kernel, cl_device_id device, cl_kernel_work_group_info param_name, - size_t param_value_size, void *param_value, - size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_2_0 - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clSetKernelArgSVMPointer)( - cl_kernel /* kernel */, cl_uint /* arg_index */, - const void * /* arg_value */) CL_API_SUFFIX__VERSION_2_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clSetKernelExecInfo)( - cl_kernel /* kernel */, cl_kernel_exec_info /* param_name */, - size_t /* param_value_size */, - const void * /* param_value */) CL_API_SUFFIX__VERSION_2_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetKernelSubGroupInfoKHR)( - cl_kernel /* in_kernel */, cl_device_id /*in_device*/, - cl_kernel_sub_group_info /* param_name */, size_t /*input_value_size*/, - const void * /*input_value*/, size_t /*param_value_size*/, - void * /*param_value*/, - size_t * /*param_value_size_ret*/) CL_EXT_SUFFIX__VERSION_2_0; - -#else - -typedef void *cl_api_clSetKernelArgSVMPointer; -typedef void *cl_api_clSetKernelExecInfo; -typedef void *cl_api_clGetKernelSubGroupInfoKHR; - -#endif - -// Event Object APIs -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clWaitForEvents)( - cl_uint num_events, const cl_event *event_list) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetEventInfo)( - cl_event event, cl_event_info param_name, size_t param_value_size, - void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clRetainEvent)(cl_event event) - CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clReleaseEvent)(cl_event event) - CL_API_SUFFIX__VERSION_1_0; - -// Profiling APIs -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetEventProfilingInfo)( - cl_event event, cl_profiling_info param_name, size_t param_value_size, - void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -// Flush and Finish APIs -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clFlush)( - cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clFinish)( - cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0; - -// Enqueued Commands APIs -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueReadBuffer)( - cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_read, - size_t offset, size_t cb, void *ptr, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_1 - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueReadBufferRect)( - cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_read, - const size_t *buffer_origin, const size_t *host_origin, - const size_t *region, size_t buffer_row_pitch, size_t buffer_slice_pitch, - size_t host_row_pitch, size_t host_slice_pitch, void *ptr, - cl_uint num_events_in_wait_list, const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_1; - -#else - -typedef void *cl_api_clEnqueueReadBufferRect; - -#endif - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueWriteBuffer)( - cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_write, - size_t offset, size_t cb, const void *ptr, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_1 - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueWriteBufferRect)( - cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_read, - const size_t *buffer_origin, const size_t *host_origin, - const size_t *region, size_t buffer_row_pitch, size_t buffer_slice_pitch, - size_t host_row_pitch, size_t host_slice_pitch, const void *ptr, - cl_uint num_events_in_wait_list, const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_1; - -#else - -typedef void *cl_api_clEnqueueWriteBufferRect; - -#endif - -#ifdef CL_VERSION_1_2 - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueFillBuffer)( - cl_command_queue command_queue, cl_mem buffer, const void *pattern, - size_t pattern_size, size_t offset, size_t cb, - cl_uint num_events_in_wait_list, const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_2; - -#else - -typedef void *cl_api_clEnqueueFillBuffer; - -#endif - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueCopyBuffer)( - cl_command_queue command_queue, cl_mem src_buffer, cl_mem dst_buffer, - size_t src_offset, size_t dst_offset, size_t cb, - cl_uint num_events_in_wait_list, const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_1 - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueCopyBufferRect)( - cl_command_queue command_queue, cl_mem src_buffer, cl_mem dst_buffer, - const size_t *src_origin, const size_t *dst_origin, const size_t *region, - size_t src_row_pitch, size_t src_slice_pitch, size_t dst_row_pitch, - size_t dst_slice_pitch, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_1; - -#else - -typedef void *cl_api_clEnqueueCopyBufferRect; - -#endif - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueReadImage)( - cl_command_queue command_queue, cl_mem image, cl_bool blocking_read, - const size_t *origin, const size_t *region, size_t row_pitch, - size_t slice_pitch, void *ptr, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueWriteImage)( - cl_command_queue command_queue, cl_mem image, cl_bool blocking_write, - const size_t *origin, const size_t *region, size_t input_row_pitch, - size_t input_slice_pitch, const void *ptr, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_2 - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueFillImage)( - cl_command_queue command_queue, cl_mem image, const void *fill_color, - const size_t origin[3], const size_t region[3], - cl_uint num_events_in_wait_list, const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_2; - -#else - -typedef void *cl_api_clEnqueueFillImage; - -#endif - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueCopyImage)( - cl_command_queue command_queue, cl_mem src_image, cl_mem dst_image, - const size_t *src_origin, const size_t *dst_origin, const size_t *region, - cl_uint num_events_in_wait_list, const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueCopyImageToBuffer)( - cl_command_queue command_queue, cl_mem src_image, cl_mem dst_buffer, - const size_t *src_origin, const size_t *region, size_t dst_offset, - cl_uint num_events_in_wait_list, const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueCopyBufferToImage)( - cl_command_queue command_queue, cl_mem src_buffer, cl_mem dst_image, - size_t src_offset, const size_t *dst_origin, const size_t *region, - cl_uint num_events_in_wait_list, const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY void *(CL_API_CALL *cl_api_clEnqueueMapBuffer)( - cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_map, - cl_map_flags map_flags, size_t offset, size_t cb, - cl_uint num_events_in_wait_list, const cl_event *event_wait_list, - cl_event *event, cl_int *errcode_ret)CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY void *(CL_API_CALL *cl_api_clEnqueueMapImage)( - cl_command_queue command_queue, cl_mem image, cl_bool blocking_map, - cl_map_flags map_flags, const size_t *origin, const size_t *region, - size_t *image_row_pitch, size_t *image_slice_pitch, - cl_uint num_events_in_wait_list, const cl_event *event_wait_list, - cl_event *event, cl_int *errcode_ret)CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueUnmapMemObject)( - cl_command_queue command_queue, cl_mem memobj, void *mapped_ptr, - cl_uint num_events_in_wait_list, const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_2 - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueMigrateMemObjects)( - cl_command_queue command_queue, cl_uint num_mem_objects, - const cl_mem *mem_objects, cl_mem_migration_flags flags, - cl_uint num_events_in_wait_list, const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_2; - -#else - -typedef void *cl_api_clEnqueueMigrateMemObjects; - -#endif - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueNDRangeKernel)( - cl_command_queue command_queue, cl_kernel kernel, cl_uint work_dim, - const size_t *global_work_offset, const size_t *global_work_size, - const size_t *local_work_size, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueTask)( - cl_command_queue command_queue, cl_kernel kernel, - cl_uint num_events_in_wait_list, const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueNativeKernel)( - cl_command_queue command_queue, void(CL_CALLBACK *user_func)(void *), - void *args, size_t cb_args, cl_uint num_mem_objects, const cl_mem *mem_list, - const void **args_mem_loc, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_0; - -#ifdef CL_VERSION_1_2 - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueMarkerWithWaitList)( - cl_command_queue command_queue, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueBarrierWithWaitList)( - cl_command_queue command_queue, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY void *( - CL_API_CALL *cl_api_clGetExtensionFunctionAddressForPlatform)( - cl_platform_id platform, - const char *function_name)CL_API_SUFFIX__VERSION_1_2; - -#else - -typedef void *cl_api_clEnqueueMarkerWithWaitList; -typedef void *cl_api_clEnqueueBarrierWithWaitList; -typedef void *cl_api_clGetExtensionFunctionAddressForPlatform; - -#endif - -// Shared Virtual Memory APIs - -#ifdef CL_VERSION_2_0 - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueSVMFree)( - cl_command_queue /* command_queue */, cl_uint /* num_svm_pointers */, - void ** /* svm_pointers */, - void(CL_CALLBACK *pfn_free_func)(cl_command_queue /* queue */, - cl_uint /* num_svm_pointers */, - void ** /* svm_pointers[] */, - void * /* user_data */), - void * /* user_data */, cl_uint /* num_events_in_wait_list */, - const cl_event * /* event_wait_list */, - cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueSVMMemcpy)( - cl_command_queue /* command_queue */, cl_bool /* blocking_copy */, - void * /* dst_ptr */, const void * /* src_ptr */, size_t /* size */, - cl_uint /* num_events_in_wait_list */, - const cl_event * /* event_wait_list */, - cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueSVMMemFill)( - cl_command_queue /* command_queue */, void * /* svm_ptr */, - const void * /* pattern */, size_t /* pattern_size */, size_t /* size */, - cl_uint /* num_events_in_wait_list */, - const cl_event * /* event_wait_list */, - cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueSVMMap)( - cl_command_queue /* command_queue */, cl_bool /* blocking_map */, - cl_map_flags /* map_flags */, void * /* svm_ptr */, size_t /* size */, - cl_uint /* num_events_in_wait_list */, - const cl_event * /* event_wait_list */, - cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueSVMUnmap)( - cl_command_queue /* command_queue */, void * /* svm_ptr */, - cl_uint /* num_events_in_wait_list */, - const cl_event * /* event_wait_list */, - cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0; - -#else - -typedef void *cl_api_clEnqueueSVMFree; -typedef void *cl_api_clEnqueueSVMMemcpy; -typedef void *cl_api_clEnqueueSVMMemFill; -typedef void *cl_api_clEnqueueSVMMap; -typedef void *cl_api_clEnqueueSVMUnmap; - -#endif - -// Deprecated APIs -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clSetCommandQueueProperty)( - cl_command_queue command_queue, cl_command_queue_properties properties, - cl_bool enable, cl_command_queue_properties *old_properties) - CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED; - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateImage2D)( - cl_context context, cl_mem_flags flags, const cl_image_format *image_format, - size_t image_width, size_t image_height, size_t image_row_pitch, - void *host_ptr, cl_int *errcode_ret) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateImage3D)( - cl_context context, cl_mem_flags flags, const cl_image_format *image_format, - size_t image_width, size_t image_height, size_t image_depth, - size_t image_row_pitch, size_t image_slice_pitch, void *host_ptr, - cl_int *errcode_ret) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clUnloadCompiler)(void) - CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueMarker)( - cl_command_queue command_queue, - cl_event *event) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueWaitForEvents)( - cl_command_queue command_queue, cl_uint num_events, - const cl_event *event_list) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueBarrier)( - cl_command_queue command_queue) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; - -typedef CL_API_ENTRY void *(CL_API_CALL *cl_api_clGetExtensionFunctionAddress)( - const char *function_name)CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; - -// GL and other APIs -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateFromGLBuffer)( - cl_context context, cl_mem_flags flags, cl_GLuint bufobj, - int *errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateFromGLTexture)( - cl_context context, cl_mem_flags flags, cl_GLenum target, cl_GLint miplevel, - cl_GLuint texture, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateFromGLTexture2D)( - cl_context context, cl_mem_flags flags, cl_GLenum target, cl_GLint miplevel, - cl_GLuint texture, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateFromGLTexture3D)( - cl_context context, cl_mem_flags flags, cl_GLenum target, cl_GLint miplevel, - cl_GLuint texture, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateFromGLRenderbuffer)( - cl_context context, cl_mem_flags flags, cl_GLuint renderbuffer, - cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetGLObjectInfo)( - cl_mem memobj, cl_gl_object_type *gl_object_type, - cl_GLuint *gl_object_name) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetGLTextureInfo)( - cl_mem memobj, cl_gl_texture_info param_name, size_t param_value_size, - void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueAcquireGLObjects)( - cl_command_queue command_queue, cl_uint num_objects, - const cl_mem *mem_objects, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueReleaseGLObjects)( - cl_command_queue command_queue, cl_uint num_objects, - const cl_mem *mem_objects, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_0; - -/* cl_khr_gl_sharing */ -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetGLContextInfoKHR)( - const cl_context_properties *properties, cl_gl_context_info param_name, - size_t param_value_size, void *param_value, size_t *param_value_size_ret); - -/* cl_khr_gl_event */ -typedef CL_API_ENTRY cl_event(CL_API_CALL *cl_api_clCreateEventFromGLsyncKHR)( - cl_context context, cl_GLsync sync, cl_int *errcode_ret); - -#if defined(_WIN32) - -/* cl_khr_d3d10_sharing */ - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetDeviceIDsFromD3D10KHR)( - cl_platform_id platform, cl_d3d10_device_source_khr d3d_device_source, - void *d3d_object, cl_d3d10_device_set_khr d3d_device_set, - cl_uint num_entries, cl_device_id *devices, - cl_uint *num_devices) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateFromD3D10BufferKHR)( - cl_context context, cl_mem_flags flags, ID3D10Buffer *resource, - cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateFromD3D10Texture2DKHR)( - cl_context context, cl_mem_flags flags, ID3D10Texture2D *resource, - UINT subresource, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateFromD3D10Texture3DKHR)( - cl_context context, cl_mem_flags flags, ID3D10Texture3D *resource, - UINT subresource, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY -cl_int(CL_API_CALL *cl_api_clEnqueueAcquireD3D10ObjectsKHR)( - cl_command_queue command_queue, cl_uint num_objects, - const cl_mem *mem_objects, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY -cl_int(CL_API_CALL *cl_api_clEnqueueReleaseD3D10ObjectsKHR)( - cl_command_queue command_queue, cl_uint num_objects, - const cl_mem *mem_objects, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_0; - -extern CL_API_ENTRY cl_int CL_API_CALL clGetDeviceIDsFromD3D10KHR( - cl_platform_id platform, cl_d3d10_device_source_khr d3d_device_source, - void *d3d_object, cl_d3d10_device_set_khr d3d_device_set, - cl_uint num_entries, cl_device_id *devices, cl_uint *num_devices); - -extern CL_API_ENTRY cl_mem CL_API_CALL -clCreateFromD3D10BufferKHR(cl_context context, cl_mem_flags flags, - ID3D10Buffer *resource, cl_int *errcode_ret); - -extern CL_API_ENTRY cl_mem CL_API_CALL clCreateFromD3D10Texture2DKHR( - cl_context context, cl_mem_flags flags, ID3D10Texture2D *resource, - UINT subresource, cl_int *errcode_ret); - -extern CL_API_ENTRY cl_mem CL_API_CALL clCreateFromD3D10Texture3DKHR( - cl_context context, cl_mem_flags flags, ID3D10Texture3D *resource, - UINT subresource, cl_int *errcode_ret); - -extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueAcquireD3D10ObjectsKHR( - cl_command_queue command_queue, cl_uint num_objects, - const cl_mem *mem_objects, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, cl_event *event); - -extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueReleaseD3D10ObjectsKHR( - cl_command_queue command_queue, cl_uint num_objects, - const cl_mem *mem_objects, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, cl_event *event); - -/* cl_khr_d3d11_sharing */ -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetDeviceIDsFromD3D11KHR)( - cl_platform_id platform, cl_d3d11_device_source_khr d3d_device_source, - void *d3d_object, cl_d3d11_device_set_khr d3d_device_set, - cl_uint num_entries, cl_device_id *devices, - cl_uint *num_devices) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateFromD3D11BufferKHR)( - cl_context context, cl_mem_flags flags, ID3D11Buffer *resource, - cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateFromD3D11Texture2DKHR)( - cl_context context, cl_mem_flags flags, ID3D11Texture2D *resource, - UINT subresource, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateFromD3D11Texture3DKHR)( - cl_context context, cl_mem_flags flags, ID3D11Texture3D *resource, - UINT subresource, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY -cl_int(CL_API_CALL *cl_api_clEnqueueAcquireD3D11ObjectsKHR)( - cl_command_queue command_queue, cl_uint num_objects, - const cl_mem *mem_objects, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY -cl_int(CL_API_CALL *cl_api_clEnqueueReleaseD3D11ObjectsKHR)( - cl_command_queue command_queue, cl_uint num_objects, - const cl_mem *mem_objects, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_2; - -/* cl_khr_dx9_media_sharing */ -typedef CL_API_ENTRY -cl_int(CL_API_CALL *cl_api_clGetDeviceIDsFromDX9MediaAdapterKHR)( - cl_platform_id platform, cl_uint num_media_adapters, - cl_dx9_media_adapter_type_khr *media_adapters_type, void *media_adapters, - cl_dx9_media_adapter_set_khr media_adapter_set, cl_uint num_entries, - cl_device_id *devices, cl_uint *num_devices) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateFromDX9MediaSurfaceKHR)( - cl_context context, cl_mem_flags flags, - cl_dx9_media_adapter_type_khr adapter_type, void *surface_info, - cl_uint plane, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY -cl_int(CL_API_CALL *cl_api_clEnqueueAcquireDX9MediaSurfacesKHR)( - cl_command_queue command_queue, cl_uint num_objects, - const cl_mem *mem_objects, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY -cl_int(CL_API_CALL *cl_api_clEnqueueReleaseDX9MediaSurfacesKHR)( - cl_command_queue command_queue, cl_uint num_objects, - const cl_mem *mem_objects, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_1_2; - -/* cl_khr_d3d11_sharing */ -extern CL_API_ENTRY cl_int CL_API_CALL clGetDeviceIDsFromD3D11KHR( - cl_platform_id platform, cl_d3d11_device_source_khr d3d_device_source, - void *d3d_object, cl_d3d11_device_set_khr d3d_device_set, - cl_uint num_entries, cl_device_id *devices, cl_uint *num_devices); - -extern CL_API_ENTRY cl_mem CL_API_CALL -clCreateFromD3D11BufferKHR(cl_context context, cl_mem_flags flags, - ID3D11Buffer *resource, cl_int *errcode_ret); - -extern CL_API_ENTRY cl_mem CL_API_CALL clCreateFromD3D11Texture2DKHR( - cl_context context, cl_mem_flags flags, ID3D11Texture2D *resource, - UINT subresource, cl_int *errcode_ret); - -extern CL_API_ENTRY cl_mem CL_API_CALL clCreateFromD3D11Texture3DKHR( - cl_context context, cl_mem_flags flags, ID3D11Texture3D *resource, - UINT subresource, cl_int *errcode_ret); - -extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueAcquireD3D11ObjectsKHR( - cl_command_queue command_queue, cl_uint num_objects, - const cl_mem *mem_objects, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, cl_event *event); - -extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueReleaseD3D11ObjectsKHR( - cl_command_queue command_queue, cl_uint num_objects, - const cl_mem *mem_objects, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, cl_event *event); - -/* cl_khr_dx9_media_sharing */ -extern CL_API_ENTRY cl_int CL_API_CALL clGetDeviceIDsFromDX9MediaAdapterKHR( - cl_platform_id platform, cl_uint num_media_adapters, - cl_dx9_media_adapter_type_khr *media_adapter_type, void *media_adapters, - cl_dx9_media_adapter_set_khr media_adapter_set, cl_uint num_entries, - cl_device_id *devices, cl_uint *num_devices); - -extern CL_API_ENTRY cl_mem CL_API_CALL clCreateFromDX9MediaSurfaceKHR( - cl_context context, cl_mem_flags flags, - cl_dx9_media_adapter_type_khr adapter_type, void *surface_info, - cl_uint plane, cl_int *errcode_ret); - -extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueAcquireDX9MediaSurfacesKHR( - cl_command_queue command_queue, cl_uint num_objects, - const cl_mem *mem_objects, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, cl_event *event); - -extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueReleaseDX9MediaSurfacesKHR( - cl_command_queue command_queue, cl_uint num_objects, - const cl_mem *mem_objects, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, cl_event *event); - -#else - -/* cl_khr_d3d10_sharing */ -typedef void *cl_api_clGetDeviceIDsFromD3D10KHR; -typedef void *cl_api_clCreateFromD3D10BufferKHR; -typedef void *cl_api_clCreateFromD3D10Texture2DKHR; -typedef void *cl_api_clCreateFromD3D10Texture3DKHR; -typedef void *cl_api_clEnqueueAcquireD3D10ObjectsKHR; -typedef void *cl_api_clEnqueueReleaseD3D10ObjectsKHR; - -/* cl_khr_d3d11_sharing */ -typedef void *cl_api_clGetDeviceIDsFromD3D11KHR; -typedef void *cl_api_clCreateFromD3D11BufferKHR; -typedef void *cl_api_clCreateFromD3D11Texture2DKHR; -typedef void *cl_api_clCreateFromD3D11Texture3DKHR; -typedef void *cl_api_clEnqueueAcquireD3D11ObjectsKHR; -typedef void *cl_api_clEnqueueReleaseD3D11ObjectsKHR; - -/* cl_khr_dx9_media_sharing */ -typedef void *cl_api_clCreateFromDX9MediaSurfaceKHR; -typedef void *cl_api_clEnqueueAcquireDX9MediaSurfacesKHR; -typedef void *cl_api_clEnqueueReleaseDX9MediaSurfacesKHR; -typedef void *cl_api_clGetDeviceIDsFromDX9MediaAdapterKHR; - -#endif - -/* OpenCL 1.1 */ - -#ifdef CL_VERSION_1_1 - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clSetEventCallback)( - cl_event /* event */, cl_int /* command_exec_callback_type */, - void(CL_CALLBACK * /* pfn_notify */)(cl_event, cl_int, void *), - void * /* user_data */) CL_API_SUFFIX__VERSION_1_1; - -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateSubBuffer)( - cl_mem /* buffer */, cl_mem_flags /* flags */, - cl_buffer_create_type /* buffer_create_type */, - const void * /* buffer_create_info */, - cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_1; - -typedef CL_API_ENTRY -cl_int(CL_API_CALL *cl_api_clSetMemObjectDestructorCallback)( - cl_mem /* memobj */, - void(CL_CALLBACK * /*pfn_notify*/)(cl_mem /* memobj */, - void * /*user_data*/), - void * /*user_data */) CL_API_SUFFIX__VERSION_1_1; - -typedef CL_API_ENTRY cl_event(CL_API_CALL *cl_api_clCreateUserEvent)( - cl_context /* context */, - cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_1; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clSetUserEventStatus)( - cl_event /* event */, - cl_int /* execution_status */) CL_API_SUFFIX__VERSION_1_1; - -#else - -typedef void *cl_api_clSetEventCallback; -typedef void *cl_api_clCreateSubBuffer; -typedef void *cl_api_clSetMemObjectDestructorCallback; -typedef void *cl_api_clCreateUserEvent; -typedef void *cl_api_clSetUserEventStatus; - -#endif - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clCreateSubDevicesEXT)( - cl_device_id in_device, - const cl_device_partition_property_ext *partition_properties, - cl_uint num_entries, cl_device_id *out_devices, cl_uint *num_devices); - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clRetainDeviceEXT)( - cl_device_id device) CL_API_SUFFIX__VERSION_1_0; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clReleaseDeviceEXT)( - cl_device_id device) CL_API_SUFFIX__VERSION_1_0; - -/* cl_khr_egl_image */ -typedef CL_API_ENTRY cl_mem(CL_API_CALL *cl_api_clCreateFromEGLImageKHR)( - cl_context context, CLeglDisplayKHR display, CLeglImageKHR image, - cl_mem_flags flags, const cl_egl_image_properties_khr *properties, - cl_int *errcode_ret); - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueAcquireEGLObjectsKHR)( - cl_command_queue command_queue, cl_uint num_objects, - const cl_mem *mem_objects, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, cl_event *event); - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueReleaseEGLObjectsKHR)( - cl_command_queue command_queue, cl_uint num_objects, - const cl_mem *mem_objects, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, cl_event *event); - -/* cl_khr_egl_event */ -typedef CL_API_ENTRY cl_event(CL_API_CALL *cl_api_clCreateEventFromEGLSyncKHR)( - cl_context context, CLeglSyncKHR sync, CLeglDisplayKHR display, - cl_int *errcode_ret); - -#ifdef CL_VERSION_2_1 - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clSetDefaultDeviceCommandQueue)( - cl_context context, cl_device_id device, - cl_command_queue command_queue) CL_API_SUFFIX__VERSION_2_1; - -typedef CL_API_ENTRY cl_program(CL_API_CALL *cl_api_clCreateProgramWithIL)( - cl_context context, const void *il, size_t length, - cl_int *errcode_ret) CL_API_SUFFIX__VERSION_2_1; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetKernelSubGroupInfo)( - cl_kernel kernel, cl_device_id device, cl_kernel_sub_group_info param_name, - size_t input_value_size, const void *input_value, size_t param_value_size, - void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_2_1; - -typedef CL_API_ENTRY cl_kernel(CL_API_CALL *cl_api_clCloneKernel)( - cl_kernel source_kernel, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_2_1; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clEnqueueSVMMigrateMem)( - cl_command_queue command_queue, cl_uint num_svm_pointers, - const void **svm_pointers, const size_t *sizes, - cl_mem_migration_flags flags, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) CL_API_SUFFIX__VERSION_2_1; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetDeviceAndHostTimer)( - cl_device_id device, cl_ulong *device_timestamp, - cl_ulong *host_timestamp) CL_API_SUFFIX__VERSION_2_1; - -typedef CL_API_ENTRY cl_int(CL_API_CALL *cl_api_clGetHostTimer)( - cl_device_id device, cl_ulong *host_timestamp) CL_API_SUFFIX__VERSION_2_1; - -#else - -typedef void *cl_api_clSetDefaultDeviceCommandQueue; -typedef void *cl_api_clCreateProgramWithIL; -typedef void *cl_api_clGetKernelSubGroupInfo; -typedef void *cl_api_clCloneKernel; -typedef void *cl_api_clEnqueueSVMMigrateMem; -typedef void *cl_api_clGetDeviceAndHostTimer; -typedef void *cl_api_clGetHostTimer; - -#endif - -/* Vendor dispatch table struture */ - -typedef struct _cl_icd_dispatch { - /* OpenCL 1.0 */ - cl_api_clGetPlatformIDs clGetPlatformIDs; - cl_api_clGetPlatformInfo clGetPlatformInfo; - cl_api_clGetDeviceIDs clGetDeviceIDs; - cl_api_clGetDeviceInfo clGetDeviceInfo; - cl_api_clCreateContext clCreateContext; - cl_api_clCreateContextFromType clCreateContextFromType; - cl_api_clRetainContext clRetainContext; - cl_api_clReleaseContext clReleaseContext; - cl_api_clGetContextInfo clGetContextInfo; - cl_api_clCreateCommandQueue clCreateCommandQueue; - cl_api_clRetainCommandQueue clRetainCommandQueue; - cl_api_clReleaseCommandQueue clReleaseCommandQueue; - cl_api_clGetCommandQueueInfo clGetCommandQueueInfo; - cl_api_clSetCommandQueueProperty clSetCommandQueueProperty; - cl_api_clCreateBuffer clCreateBuffer; - cl_api_clCreateImage2D clCreateImage2D; - cl_api_clCreateImage3D clCreateImage3D; - cl_api_clRetainMemObject clRetainMemObject; - cl_api_clReleaseMemObject clReleaseMemObject; - cl_api_clGetSupportedImageFormats clGetSupportedImageFormats; - cl_api_clGetMemObjectInfo clGetMemObjectInfo; - cl_api_clGetImageInfo clGetImageInfo; - cl_api_clCreateSampler clCreateSampler; - cl_api_clRetainSampler clRetainSampler; - cl_api_clReleaseSampler clReleaseSampler; - cl_api_clGetSamplerInfo clGetSamplerInfo; - cl_api_clCreateProgramWithSource clCreateProgramWithSource; - cl_api_clCreateProgramWithBinary clCreateProgramWithBinary; - cl_api_clRetainProgram clRetainProgram; - cl_api_clReleaseProgram clReleaseProgram; - cl_api_clBuildProgram clBuildProgram; - cl_api_clUnloadCompiler clUnloadCompiler; - cl_api_clGetProgramInfo clGetProgramInfo; - cl_api_clGetProgramBuildInfo clGetProgramBuildInfo; - cl_api_clCreateKernel clCreateKernel; - cl_api_clCreateKernelsInProgram clCreateKernelsInProgram; - cl_api_clRetainKernel clRetainKernel; - cl_api_clReleaseKernel clReleaseKernel; - cl_api_clSetKernelArg clSetKernelArg; - cl_api_clGetKernelInfo clGetKernelInfo; - cl_api_clGetKernelWorkGroupInfo clGetKernelWorkGroupInfo; - cl_api_clWaitForEvents clWaitForEvents; - cl_api_clGetEventInfo clGetEventInfo; - cl_api_clRetainEvent clRetainEvent; - cl_api_clReleaseEvent clReleaseEvent; - cl_api_clGetEventProfilingInfo clGetEventProfilingInfo; - cl_api_clFlush clFlush; - cl_api_clFinish clFinish; - cl_api_clEnqueueReadBuffer clEnqueueReadBuffer; - cl_api_clEnqueueWriteBuffer clEnqueueWriteBuffer; - cl_api_clEnqueueCopyBuffer clEnqueueCopyBuffer; - cl_api_clEnqueueReadImage clEnqueueReadImage; - cl_api_clEnqueueWriteImage clEnqueueWriteImage; - cl_api_clEnqueueCopyImage clEnqueueCopyImage; - cl_api_clEnqueueCopyImageToBuffer clEnqueueCopyImageToBuffer; - cl_api_clEnqueueCopyBufferToImage clEnqueueCopyBufferToImage; - cl_api_clEnqueueMapBuffer clEnqueueMapBuffer; - cl_api_clEnqueueMapImage clEnqueueMapImage; - cl_api_clEnqueueUnmapMemObject clEnqueueUnmapMemObject; - cl_api_clEnqueueNDRangeKernel clEnqueueNDRangeKernel; - cl_api_clEnqueueTask clEnqueueTask; - cl_api_clEnqueueNativeKernel clEnqueueNativeKernel; - cl_api_clEnqueueMarker clEnqueueMarker; - cl_api_clEnqueueWaitForEvents clEnqueueWaitForEvents; - cl_api_clEnqueueBarrier clEnqueueBarrier; - cl_api_clGetExtensionFunctionAddress clGetExtensionFunctionAddress; - cl_api_clCreateFromGLBuffer clCreateFromGLBuffer; - cl_api_clCreateFromGLTexture2D clCreateFromGLTexture2D; - cl_api_clCreateFromGLTexture3D clCreateFromGLTexture3D; - cl_api_clCreateFromGLRenderbuffer clCreateFromGLRenderbuffer; - cl_api_clGetGLObjectInfo clGetGLObjectInfo; - cl_api_clGetGLTextureInfo clGetGLTextureInfo; - cl_api_clEnqueueAcquireGLObjects clEnqueueAcquireGLObjects; - cl_api_clEnqueueReleaseGLObjects clEnqueueReleaseGLObjects; - cl_api_clGetGLContextInfoKHR clGetGLContextInfoKHR; - - /* cl_khr_d3d10_sharing */ - cl_api_clGetDeviceIDsFromD3D10KHR clGetDeviceIDsFromD3D10KHR; - cl_api_clCreateFromD3D10BufferKHR clCreateFromD3D10BufferKHR; - cl_api_clCreateFromD3D10Texture2DKHR clCreateFromD3D10Texture2DKHR; - cl_api_clCreateFromD3D10Texture3DKHR clCreateFromD3D10Texture3DKHR; - cl_api_clEnqueueAcquireD3D10ObjectsKHR clEnqueueAcquireD3D10ObjectsKHR; - cl_api_clEnqueueReleaseD3D10ObjectsKHR clEnqueueReleaseD3D10ObjectsKHR; - - /* OpenCL 1.1 */ - cl_api_clSetEventCallback clSetEventCallback; - cl_api_clCreateSubBuffer clCreateSubBuffer; - cl_api_clSetMemObjectDestructorCallback clSetMemObjectDestructorCallback; - cl_api_clCreateUserEvent clCreateUserEvent; - cl_api_clSetUserEventStatus clSetUserEventStatus; - cl_api_clEnqueueReadBufferRect clEnqueueReadBufferRect; - cl_api_clEnqueueWriteBufferRect clEnqueueWriteBufferRect; - cl_api_clEnqueueCopyBufferRect clEnqueueCopyBufferRect; - - /* cl_ext_device_fission */ - cl_api_clCreateSubDevicesEXT clCreateSubDevicesEXT; - cl_api_clRetainDeviceEXT clRetainDeviceEXT; - cl_api_clReleaseDeviceEXT clReleaseDeviceEXT; - - /* cl_khr_gl_event */ - cl_api_clCreateEventFromGLsyncKHR clCreateEventFromGLsyncKHR; - - /* OpenCL 1.2 */ - cl_api_clCreateSubDevices clCreateSubDevices; - cl_api_clRetainDevice clRetainDevice; - cl_api_clReleaseDevice clReleaseDevice; - cl_api_clCreateImage clCreateImage; - cl_api_clCreateProgramWithBuiltInKernels clCreateProgramWithBuiltInKernels; - cl_api_clCompileProgram clCompileProgram; - cl_api_clLinkProgram clLinkProgram; - cl_api_clUnloadPlatformCompiler clUnloadPlatformCompiler; - cl_api_clGetKernelArgInfo clGetKernelArgInfo; - cl_api_clEnqueueFillBuffer clEnqueueFillBuffer; - cl_api_clEnqueueFillImage clEnqueueFillImage; - cl_api_clEnqueueMigrateMemObjects clEnqueueMigrateMemObjects; - cl_api_clEnqueueMarkerWithWaitList clEnqueueMarkerWithWaitList; - cl_api_clEnqueueBarrierWithWaitList clEnqueueBarrierWithWaitList; - cl_api_clGetExtensionFunctionAddressForPlatform - clGetExtensionFunctionAddressForPlatform; - cl_api_clCreateFromGLTexture clCreateFromGLTexture; - - /* cl_khr_d3d11_sharing */ - cl_api_clGetDeviceIDsFromD3D11KHR clGetDeviceIDsFromD3D11KHR; - cl_api_clCreateFromD3D11BufferKHR clCreateFromD3D11BufferKHR; - cl_api_clCreateFromD3D11Texture2DKHR clCreateFromD3D11Texture2DKHR; - cl_api_clCreateFromD3D11Texture3DKHR clCreateFromD3D11Texture3DKHR; - cl_api_clCreateFromDX9MediaSurfaceKHR clCreateFromDX9MediaSurfaceKHR; - cl_api_clEnqueueAcquireD3D11ObjectsKHR clEnqueueAcquireD3D11ObjectsKHR; - cl_api_clEnqueueReleaseD3D11ObjectsKHR clEnqueueReleaseD3D11ObjectsKHR; - - /* cl_khr_dx9_media_sharing */ - cl_api_clGetDeviceIDsFromDX9MediaAdapterKHR - clGetDeviceIDsFromDX9MediaAdapterKHR; - cl_api_clEnqueueAcquireDX9MediaSurfacesKHR - clEnqueueAcquireDX9MediaSurfacesKHR; - cl_api_clEnqueueReleaseDX9MediaSurfacesKHR - clEnqueueReleaseDX9MediaSurfacesKHR; - - /* cl_khr_egl_image */ - cl_api_clCreateFromEGLImageKHR clCreateFromEGLImageKHR; - cl_api_clEnqueueAcquireEGLObjectsKHR clEnqueueAcquireEGLObjectsKHR; - cl_api_clEnqueueReleaseEGLObjectsKHR clEnqueueReleaseEGLObjectsKHR; - - /* cl_khr_egl_event */ - cl_api_clCreateEventFromEGLSyncKHR clCreateEventFromEGLSyncKHR; - - /* OpenCL 2.0 */ - cl_api_clCreateCommandQueueWithProperties clCreateCommandQueueWithProperties; - cl_api_clCreatePipe clCreatePipe; - cl_api_clGetPipeInfo clGetPipeInfo; - cl_api_clSVMAlloc clSVMAlloc; - cl_api_clSVMFree clSVMFree; - cl_api_clEnqueueSVMFree clEnqueueSVMFree; - cl_api_clEnqueueSVMMemcpy clEnqueueSVMMemcpy; - cl_api_clEnqueueSVMMemFill clEnqueueSVMMemFill; - cl_api_clEnqueueSVMMap clEnqueueSVMMap; - cl_api_clEnqueueSVMUnmap clEnqueueSVMUnmap; - cl_api_clCreateSamplerWithProperties clCreateSamplerWithProperties; - cl_api_clSetKernelArgSVMPointer clSetKernelArgSVMPointer; - cl_api_clSetKernelExecInfo clSetKernelExecInfo; - - /* cl_khr_sub_groups */ - cl_api_clGetKernelSubGroupInfoKHR clGetKernelSubGroupInfoKHR; - - /* OpenCL 2.1 */ - cl_api_clCloneKernel clCloneKernel; - cl_api_clCreateProgramWithIL clCreateProgramWithIL; - cl_api_clEnqueueSVMMigrateMem clEnqueueSVMMigrateMem; - cl_api_clGetDeviceAndHostTimer clGetDeviceAndHostTimer; - cl_api_clGetHostTimer clGetHostTimer; - cl_api_clGetKernelSubGroupInfo clGetKernelSubGroupInfo; - cl_api_clSetDefaultDeviceCommandQueue clSetDefaultDeviceCommandQueue; - - /* OpenCL 2.2 */ - cl_api_clSetProgramReleaseCallback clSetProgramReleaseCallback; - cl_api_clSetProgramSpecializationConstant clSetProgramSpecializationConstant; - - /* OpenCL 3.0 */ - cl_api_clCreateBufferWithProperties clCreateBufferWithProperties; - cl_api_clCreateImageWithProperties clCreateImageWithProperties; - -} cl_icd_dispatch; - -#ifdef __cplusplus -} -#endif - -#endif /* #ifndef OPENCL_CL_ICD_H */ diff --git a/src/CL/cl_platform.h b/src/CL/cl_platform.h deleted file mode 100644 index 1bd7d4b..0000000 --- a/src/CL/cl_platform.h +++ /dev/null @@ -1,1384 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008-2020 The Khronos Group Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -#ifndef __CL_PLATFORM_H -#define __CL_PLATFORM_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(_WIN32) - #define CL_API_ENTRY - #define CL_API_CALL __stdcall - #define CL_CALLBACK __stdcall -#else - #define CL_API_ENTRY - #define CL_API_CALL - #define CL_CALLBACK -#endif - -/* - * Deprecation flags refer to the last version of the header in which the - * feature was not deprecated. - * - * E.g. VERSION_1_1_DEPRECATED means the feature is present in 1.1 without - * deprecation but is deprecated in versions later than 1.1. - */ - -#define CL_EXTENSION_WEAK_LINK -#define CL_API_SUFFIX__VERSION_1_0 -#define CL_EXT_SUFFIX__VERSION_1_0 -#define CL_API_SUFFIX__VERSION_1_1 -#define CL_EXT_SUFFIX__VERSION_1_1 -#define CL_API_SUFFIX__VERSION_1_2 -#define CL_EXT_SUFFIX__VERSION_1_2 -#define CL_API_SUFFIX__VERSION_2_0 -#define CL_EXT_SUFFIX__VERSION_2_0 -#define CL_API_SUFFIX__VERSION_2_1 -#define CL_EXT_SUFFIX__VERSION_2_1 -#define CL_API_SUFFIX__VERSION_2_2 -#define CL_EXT_SUFFIX__VERSION_2_2 -#define CL_API_SUFFIX__VERSION_3_0 -#define CL_EXT_SUFFIX__VERSION_3_0 -#define CL_API_SUFFIX__EXPERIMENTAL -#define CL_EXT_SUFFIX__EXPERIMENTAL - - -#ifdef __GNUC__ - #define CL_EXT_SUFFIX_DEPRECATED __attribute__((deprecated)) - #define CL_EXT_PREFIX_DEPRECATED -#elif defined(_WIN32) - #define CL_EXT_SUFFIX_DEPRECATED - #define CL_EXT_PREFIX_DEPRECATED __declspec(deprecated) -#else - #define CL_EXT_SUFFIX_DEPRECATED - #define CL_EXT_PREFIX_DEPRECATED -#endif - -#ifdef CL_USE_DEPRECATED_OPENCL_1_0_APIS - #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED - #define CL_EXT_PREFIX__VERSION_1_0_DEPRECATED -#else - #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED CL_EXT_SUFFIX_DEPRECATED - #define CL_EXT_PREFIX__VERSION_1_0_DEPRECATED CL_EXT_PREFIX_DEPRECATED -#endif - -#ifdef CL_USE_DEPRECATED_OPENCL_1_1_APIS - #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED - #define CL_EXT_PREFIX__VERSION_1_1_DEPRECATED -#else - #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED CL_EXT_SUFFIX_DEPRECATED - #define CL_EXT_PREFIX__VERSION_1_1_DEPRECATED CL_EXT_PREFIX_DEPRECATED -#endif - -#ifdef CL_USE_DEPRECATED_OPENCL_1_2_APIS - #define CL_EXT_SUFFIX__VERSION_1_2_DEPRECATED - #define CL_EXT_PREFIX__VERSION_1_2_DEPRECATED -#else - #define CL_EXT_SUFFIX__VERSION_1_2_DEPRECATED CL_EXT_SUFFIX_DEPRECATED - #define CL_EXT_PREFIX__VERSION_1_2_DEPRECATED CL_EXT_PREFIX_DEPRECATED - #endif - -#ifdef CL_USE_DEPRECATED_OPENCL_2_0_APIS - #define CL_EXT_SUFFIX__VERSION_2_0_DEPRECATED - #define CL_EXT_PREFIX__VERSION_2_0_DEPRECATED -#else - #define CL_EXT_SUFFIX__VERSION_2_0_DEPRECATED CL_EXT_SUFFIX_DEPRECATED - #define CL_EXT_PREFIX__VERSION_2_0_DEPRECATED CL_EXT_PREFIX_DEPRECATED -#endif - -#ifdef CL_USE_DEPRECATED_OPENCL_2_1_APIS - #define CL_EXT_SUFFIX__VERSION_2_1_DEPRECATED - #define CL_EXT_PREFIX__VERSION_2_1_DEPRECATED -#else - #define CL_EXT_SUFFIX__VERSION_2_1_DEPRECATED CL_EXT_SUFFIX_DEPRECATED - #define CL_EXT_PREFIX__VERSION_2_1_DEPRECATED CL_EXT_PREFIX_DEPRECATED -#endif - -#ifdef CL_USE_DEPRECATED_OPENCL_2_2_APIS - #define CL_EXT_SUFFIX__VERSION_2_2_DEPRECATED - #define CL_EXT_PREFIX__VERSION_2_2_DEPRECATED -#else - #define CL_EXT_SUFFIX__VERSION_2_2_DEPRECATED CL_EXT_SUFFIX_DEPRECATED - #define CL_EXT_PREFIX__VERSION_2_2_DEPRECATED CL_EXT_PREFIX_DEPRECATED -#endif - -#if (defined (_WIN32) && defined(_MSC_VER)) - -/* scalar types */ -typedef signed __int8 cl_char; -typedef unsigned __int8 cl_uchar; -typedef signed __int16 cl_short; -typedef unsigned __int16 cl_ushort; -typedef signed __int32 cl_int; -typedef unsigned __int32 cl_uint; -typedef signed __int64 cl_long; -typedef unsigned __int64 cl_ulong; - -typedef unsigned __int16 cl_half; -typedef float cl_float; -typedef double cl_double; - -/* Macro names and corresponding values defined by OpenCL */ -#define CL_CHAR_BIT 8 -#define CL_SCHAR_MAX 127 -#define CL_SCHAR_MIN (-127-1) -#define CL_CHAR_MAX CL_SCHAR_MAX -#define CL_CHAR_MIN CL_SCHAR_MIN -#define CL_UCHAR_MAX 255 -#define CL_SHRT_MAX 32767 -#define CL_SHRT_MIN (-32767-1) -#define CL_USHRT_MAX 65535 -#define CL_INT_MAX 2147483647 -#define CL_INT_MIN (-2147483647-1) -#define CL_UINT_MAX 0xffffffffU -#define CL_LONG_MAX ((cl_long) 0x7FFFFFFFFFFFFFFFLL) -#define CL_LONG_MIN ((cl_long) -0x7FFFFFFFFFFFFFFFLL - 1LL) -#define CL_ULONG_MAX ((cl_ulong) 0xFFFFFFFFFFFFFFFFULL) - -#define CL_FLT_DIG 6 -#define CL_FLT_MANT_DIG 24 -#define CL_FLT_MAX_10_EXP +38 -#define CL_FLT_MAX_EXP +128 -#define CL_FLT_MIN_10_EXP -37 -#define CL_FLT_MIN_EXP -125 -#define CL_FLT_RADIX 2 -#define CL_FLT_MAX 340282346638528859811704183484516925440.0f -#define CL_FLT_MIN 1.175494350822287507969e-38f -#define CL_FLT_EPSILON 1.1920928955078125e-7f - -#define CL_HALF_DIG 3 -#define CL_HALF_MANT_DIG 11 -#define CL_HALF_MAX_10_EXP +4 -#define CL_HALF_MAX_EXP +16 -#define CL_HALF_MIN_10_EXP -4 -#define CL_HALF_MIN_EXP -13 -#define CL_HALF_RADIX 2 -#define CL_HALF_MAX 65504.0f -#define CL_HALF_MIN 6.103515625e-05f -#define CL_HALF_EPSILON 9.765625e-04f - -#define CL_DBL_DIG 15 -#define CL_DBL_MANT_DIG 53 -#define CL_DBL_MAX_10_EXP +308 -#define CL_DBL_MAX_EXP +1024 -#define CL_DBL_MIN_10_EXP -307 -#define CL_DBL_MIN_EXP -1021 -#define CL_DBL_RADIX 2 -#define CL_DBL_MAX 1.7976931348623158e+308 -#define CL_DBL_MIN 2.225073858507201383090e-308 -#define CL_DBL_EPSILON 2.220446049250313080847e-16 - -#define CL_M_E 2.7182818284590452354 -#define CL_M_LOG2E 1.4426950408889634074 -#define CL_M_LOG10E 0.43429448190325182765 -#define CL_M_LN2 0.69314718055994530942 -#define CL_M_LN10 2.30258509299404568402 -#define CL_M_PI 3.14159265358979323846 -#define CL_M_PI_2 1.57079632679489661923 -#define CL_M_PI_4 0.78539816339744830962 -#define CL_M_1_PI 0.31830988618379067154 -#define CL_M_2_PI 0.63661977236758134308 -#define CL_M_2_SQRTPI 1.12837916709551257390 -#define CL_M_SQRT2 1.41421356237309504880 -#define CL_M_SQRT1_2 0.70710678118654752440 - -#define CL_M_E_F 2.718281828f -#define CL_M_LOG2E_F 1.442695041f -#define CL_M_LOG10E_F 0.434294482f -#define CL_M_LN2_F 0.693147181f -#define CL_M_LN10_F 2.302585093f -#define CL_M_PI_F 3.141592654f -#define CL_M_PI_2_F 1.570796327f -#define CL_M_PI_4_F 0.785398163f -#define CL_M_1_PI_F 0.318309886f -#define CL_M_2_PI_F 0.636619772f -#define CL_M_2_SQRTPI_F 1.128379167f -#define CL_M_SQRT2_F 1.414213562f -#define CL_M_SQRT1_2_F 0.707106781f - -#define CL_NAN (CL_INFINITY - CL_INFINITY) -#define CL_HUGE_VALF ((cl_float) 1e50) -#define CL_HUGE_VAL ((cl_double) 1e500) -#define CL_MAXFLOAT CL_FLT_MAX -#define CL_INFINITY CL_HUGE_VALF - -#else - -#include - -/* scalar types */ -typedef int8_t cl_char; -typedef uint8_t cl_uchar; -typedef int16_t cl_short; -typedef uint16_t cl_ushort; -typedef int32_t cl_int; -typedef uint32_t cl_uint; -typedef int64_t cl_long; -typedef uint64_t cl_ulong; - -typedef uint16_t cl_half; -typedef float cl_float; -typedef double cl_double; - -/* Macro names and corresponding values defined by OpenCL */ -#define CL_CHAR_BIT 8 -#define CL_SCHAR_MAX 127 -#define CL_SCHAR_MIN (-127-1) -#define CL_CHAR_MAX CL_SCHAR_MAX -#define CL_CHAR_MIN CL_SCHAR_MIN -#define CL_UCHAR_MAX 255 -#define CL_SHRT_MAX 32767 -#define CL_SHRT_MIN (-32767-1) -#define CL_USHRT_MAX 65535 -#define CL_INT_MAX 2147483647 -#define CL_INT_MIN (-2147483647-1) -#define CL_UINT_MAX 0xffffffffU -#define CL_LONG_MAX ((cl_long) 0x7FFFFFFFFFFFFFFFLL) -#define CL_LONG_MIN ((cl_long) -0x7FFFFFFFFFFFFFFFLL - 1LL) -#define CL_ULONG_MAX ((cl_ulong) 0xFFFFFFFFFFFFFFFFULL) - -#define CL_FLT_DIG 6 -#define CL_FLT_MANT_DIG 24 -#define CL_FLT_MAX_10_EXP +38 -#define CL_FLT_MAX_EXP +128 -#define CL_FLT_MIN_10_EXP -37 -#define CL_FLT_MIN_EXP -125 -#define CL_FLT_RADIX 2 -#define CL_FLT_MAX 340282346638528859811704183484516925440.0f -#define CL_FLT_MIN 1.175494350822287507969e-38f -#define CL_FLT_EPSILON 1.1920928955078125e-7f - -#define CL_HALF_DIG 3 -#define CL_HALF_MANT_DIG 11 -#define CL_HALF_MAX_10_EXP +4 -#define CL_HALF_MAX_EXP +16 -#define CL_HALF_MIN_10_EXP -4 -#define CL_HALF_MIN_EXP -13 -#define CL_HALF_RADIX 2 -#define CL_HALF_MAX 65504.0f -#define CL_HALF_MIN 6.103515625e-05f -#define CL_HALF_EPSILON 9.765625e-04f - -#define CL_DBL_DIG 15 -#define CL_DBL_MANT_DIG 53 -#define CL_DBL_MAX_10_EXP +308 -#define CL_DBL_MAX_EXP +1024 -#define CL_DBL_MIN_10_EXP -307 -#define CL_DBL_MIN_EXP -1021 -#define CL_DBL_RADIX 2 -#define CL_DBL_MAX 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0 -#define CL_DBL_MIN 2.225073858507201383090e-308 -#define CL_DBL_EPSILON 2.220446049250313080847e-16 - -#define CL_M_E 2.7182818284590452354 -#define CL_M_LOG2E 1.4426950408889634074 -#define CL_M_LOG10E 0.43429448190325182765 -#define CL_M_LN2 0.69314718055994530942 -#define CL_M_LN10 2.30258509299404568402 -#define CL_M_PI 3.14159265358979323846 -#define CL_M_PI_2 1.57079632679489661923 -#define CL_M_PI_4 0.78539816339744830962 -#define CL_M_1_PI 0.31830988618379067154 -#define CL_M_2_PI 0.63661977236758134308 -#define CL_M_2_SQRTPI 1.12837916709551257390 -#define CL_M_SQRT2 1.41421356237309504880 -#define CL_M_SQRT1_2 0.70710678118654752440 - -#define CL_M_E_F 2.718281828f -#define CL_M_LOG2E_F 1.442695041f -#define CL_M_LOG10E_F 0.434294482f -#define CL_M_LN2_F 0.693147181f -#define CL_M_LN10_F 2.302585093f -#define CL_M_PI_F 3.141592654f -#define CL_M_PI_2_F 1.570796327f -#define CL_M_PI_4_F 0.785398163f -#define CL_M_1_PI_F 0.318309886f -#define CL_M_2_PI_F 0.636619772f -#define CL_M_2_SQRTPI_F 1.128379167f -#define CL_M_SQRT2_F 1.414213562f -#define CL_M_SQRT1_2_F 0.707106781f - -#if defined( __GNUC__ ) - #define CL_HUGE_VALF __builtin_huge_valf() - #define CL_HUGE_VAL __builtin_huge_val() - #define CL_NAN __builtin_nanf( "" ) -#else - #define CL_HUGE_VALF ((cl_float) 1e50) - #define CL_HUGE_VAL ((cl_double) 1e500) - float nanf( const char * ); - #define CL_NAN nanf( "" ) -#endif -#define CL_MAXFLOAT CL_FLT_MAX -#define CL_INFINITY CL_HUGE_VALF - -#endif - -#include - -/* Mirror types to GL types. Mirror types allow us to avoid deciding which 87s to load based on whether we are using GL or GLES here. */ -typedef unsigned int cl_GLuint; -typedef int cl_GLint; -typedef unsigned int cl_GLenum; - -/* - * Vector types - * - * Note: OpenCL requires that all types be naturally aligned. - * This means that vector types must be naturally aligned. - * For example, a vector of four floats must be aligned to - * a 16 byte boundary (calculated as 4 * the natural 4-byte - * alignment of the float). The alignment qualifiers here - * will only function properly if your compiler supports them - * and if you don't actively work to defeat them. For example, - * in order for a cl_float4 to be 16 byte aligned in a struct, - * the start of the struct must itself be 16-byte aligned. - * - * Maintaining proper alignment is the user's responsibility. - */ - -/* Define basic vector types */ -#if defined( __VEC__ ) - #include /* may be omitted depending on compiler. AltiVec spec provides no way to detect whether the header is required. */ - typedef __vector unsigned char __cl_uchar16; - typedef __vector signed char __cl_char16; - typedef __vector unsigned short __cl_ushort8; - typedef __vector signed short __cl_short8; - typedef __vector unsigned int __cl_uint4; - typedef __vector signed int __cl_int4; - typedef __vector float __cl_float4; - #define __CL_UCHAR16__ 1 - #define __CL_CHAR16__ 1 - #define __CL_USHORT8__ 1 - #define __CL_SHORT8__ 1 - #define __CL_UINT4__ 1 - #define __CL_INT4__ 1 - #define __CL_FLOAT4__ 1 -#endif - -#if defined( __SSE__ ) - #if defined( __MINGW64__ ) - #include - #else - #include - #endif - #if defined( __GNUC__ ) - typedef float __cl_float4 __attribute__((vector_size(16))); - #else - typedef __m128 __cl_float4; - #endif - #define __CL_FLOAT4__ 1 -#endif - -#if defined( __SSE2__ ) - #if defined( __MINGW64__ ) - #include - #else - #include - #endif - #if defined( __GNUC__ ) - typedef cl_uchar __cl_uchar16 __attribute__((vector_size(16))); - typedef cl_char __cl_char16 __attribute__((vector_size(16))); - typedef cl_ushort __cl_ushort8 __attribute__((vector_size(16))); - typedef cl_short __cl_short8 __attribute__((vector_size(16))); - typedef cl_uint __cl_uint4 __attribute__((vector_size(16))); - typedef cl_int __cl_int4 __attribute__((vector_size(16))); - typedef cl_ulong __cl_ulong2 __attribute__((vector_size(16))); - typedef cl_long __cl_long2 __attribute__((vector_size(16))); - typedef cl_double __cl_double2 __attribute__((vector_size(16))); - #else - typedef __m128i __cl_uchar16; - typedef __m128i __cl_char16; - typedef __m128i __cl_ushort8; - typedef __m128i __cl_short8; - typedef __m128i __cl_uint4; - typedef __m128i __cl_int4; - typedef __m128i __cl_ulong2; - typedef __m128i __cl_long2; - typedef __m128d __cl_double2; - #endif - #define __CL_UCHAR16__ 1 - #define __CL_CHAR16__ 1 - #define __CL_USHORT8__ 1 - #define __CL_SHORT8__ 1 - #define __CL_INT4__ 1 - #define __CL_UINT4__ 1 - #define __CL_ULONG2__ 1 - #define __CL_LONG2__ 1 - #define __CL_DOUBLE2__ 1 -#endif - -#if defined( __MMX__ ) - #include - #if defined( __GNUC__ ) - typedef cl_uchar __cl_uchar8 __attribute__((vector_size(8))); - typedef cl_char __cl_char8 __attribute__((vector_size(8))); - typedef cl_ushort __cl_ushort4 __attribute__((vector_size(8))); - typedef cl_short __cl_short4 __attribute__((vector_size(8))); - typedef cl_uint __cl_uint2 __attribute__((vector_size(8))); - typedef cl_int __cl_int2 __attribute__((vector_size(8))); - typedef cl_ulong __cl_ulong1 __attribute__((vector_size(8))); - typedef cl_long __cl_long1 __attribute__((vector_size(8))); - typedef cl_float __cl_float2 __attribute__((vector_size(8))); - #else - typedef __m64 __cl_uchar8; - typedef __m64 __cl_char8; - typedef __m64 __cl_ushort4; - typedef __m64 __cl_short4; - typedef __m64 __cl_uint2; - typedef __m64 __cl_int2; - typedef __m64 __cl_ulong1; - typedef __m64 __cl_long1; - typedef __m64 __cl_float2; - #endif - #define __CL_UCHAR8__ 1 - #define __CL_CHAR8__ 1 - #define __CL_USHORT4__ 1 - #define __CL_SHORT4__ 1 - #define __CL_INT2__ 1 - #define __CL_UINT2__ 1 - #define __CL_ULONG1__ 1 - #define __CL_LONG1__ 1 - #define __CL_FLOAT2__ 1 -#endif - -#if defined( __AVX__ ) - #if defined( __MINGW64__ ) - #include - #else - #include - #endif - #if defined( __GNUC__ ) - typedef cl_float __cl_float8 __attribute__((vector_size(32))); - typedef cl_double __cl_double4 __attribute__((vector_size(32))); - #else - typedef __m256 __cl_float8; - typedef __m256d __cl_double4; - #endif - #define __CL_FLOAT8__ 1 - #define __CL_DOUBLE4__ 1 -#endif - -/* Define capabilities for anonymous struct members. */ -#if !defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L -#define __CL_HAS_ANON_STRUCT__ 1 -#define __CL_ANON_STRUCT__ -#elif defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) -#define __CL_HAS_ANON_STRUCT__ 1 -#define __CL_ANON_STRUCT__ __extension__ -#elif defined( _WIN32) && defined(_MSC_VER) - #if _MSC_VER >= 1500 - /* Microsoft Developer Studio 2008 supports anonymous structs, but - * complains by default. */ - #define __CL_HAS_ANON_STRUCT__ 1 - #define __CL_ANON_STRUCT__ - /* Disable warning C4201: nonstandard extension used : nameless - * struct/union */ - #pragma warning( push ) - #pragma warning( disable : 4201 ) - #endif -#else -#define __CL_HAS_ANON_STRUCT__ 0 -#define __CL_ANON_STRUCT__ -#endif - -/* Define alignment keys */ -#if defined( __GNUC__ ) - #define CL_ALIGNED(_x) __attribute__ ((aligned(_x))) -#elif defined( _WIN32) && (_MSC_VER) - /* Alignment keys neutered on windows because MSVC can't swallow function arguments with alignment requirements */ - /* http://msdn.microsoft.com/en-us/library/373ak2y1%28VS.71%29.aspx */ - /* #include */ - /* #define CL_ALIGNED(_x) _CRT_ALIGN(_x) */ - #define CL_ALIGNED(_x) -#else - #warning Need to implement some method to align data here - #define CL_ALIGNED(_x) -#endif - -/* Indicate whether .xyzw, .s0123 and .hi.lo are supported */ -#if __CL_HAS_ANON_STRUCT__ - /* .xyzw and .s0123...{f|F} are supported */ - #define CL_HAS_NAMED_VECTOR_FIELDS 1 - /* .hi and .lo are supported */ - #define CL_HAS_HI_LO_VECTOR_FIELDS 1 -#endif - -/* Define cl_vector types */ - -/* ---- cl_charn ---- */ -typedef union -{ - cl_char CL_ALIGNED(2) s[2]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_char x, y; }; - __CL_ANON_STRUCT__ struct{ cl_char s0, s1; }; - __CL_ANON_STRUCT__ struct{ cl_char lo, hi; }; -#endif -#if defined( __CL_CHAR2__) - __cl_char2 v2; -#endif -}cl_char2; - -typedef union -{ - cl_char CL_ALIGNED(4) s[4]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_char x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_char s0, s1, s2, s3; }; - __CL_ANON_STRUCT__ struct{ cl_char2 lo, hi; }; -#endif -#if defined( __CL_CHAR2__) - __cl_char2 v2[2]; -#endif -#if defined( __CL_CHAR4__) - __cl_char4 v4; -#endif -}cl_char4; - -/* cl_char3 is identical in size, alignment and behavior to cl_char4. See section 6.1.5. */ -typedef cl_char4 cl_char3; - -typedef union -{ - cl_char CL_ALIGNED(8) s[8]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_char x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_char s0, s1, s2, s3, s4, s5, s6, s7; }; - __CL_ANON_STRUCT__ struct{ cl_char4 lo, hi; }; -#endif -#if defined( __CL_CHAR2__) - __cl_char2 v2[4]; -#endif -#if defined( __CL_CHAR4__) - __cl_char4 v4[2]; -#endif -#if defined( __CL_CHAR8__ ) - __cl_char8 v8; -#endif -}cl_char8; - -typedef union -{ - cl_char CL_ALIGNED(16) s[16]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_char x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; - __CL_ANON_STRUCT__ struct{ cl_char s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; - __CL_ANON_STRUCT__ struct{ cl_char8 lo, hi; }; -#endif -#if defined( __CL_CHAR2__) - __cl_char2 v2[8]; -#endif -#if defined( __CL_CHAR4__) - __cl_char4 v4[4]; -#endif -#if defined( __CL_CHAR8__ ) - __cl_char8 v8[2]; -#endif -#if defined( __CL_CHAR16__ ) - __cl_char16 v16; -#endif -}cl_char16; - - -/* ---- cl_ucharn ---- */ -typedef union -{ - cl_uchar CL_ALIGNED(2) s[2]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_uchar x, y; }; - __CL_ANON_STRUCT__ struct{ cl_uchar s0, s1; }; - __CL_ANON_STRUCT__ struct{ cl_uchar lo, hi; }; -#endif -#if defined( __cl_uchar2__) - __cl_uchar2 v2; -#endif -}cl_uchar2; - -typedef union -{ - cl_uchar CL_ALIGNED(4) s[4]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_uchar x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_uchar s0, s1, s2, s3; }; - __CL_ANON_STRUCT__ struct{ cl_uchar2 lo, hi; }; -#endif -#if defined( __CL_UCHAR2__) - __cl_uchar2 v2[2]; -#endif -#if defined( __CL_UCHAR4__) - __cl_uchar4 v4; -#endif -}cl_uchar4; - -/* cl_uchar3 is identical in size, alignment and behavior to cl_uchar4. See section 6.1.5. */ -typedef cl_uchar4 cl_uchar3; - -typedef union -{ - cl_uchar CL_ALIGNED(8) s[8]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_uchar x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_uchar s0, s1, s2, s3, s4, s5, s6, s7; }; - __CL_ANON_STRUCT__ struct{ cl_uchar4 lo, hi; }; -#endif -#if defined( __CL_UCHAR2__) - __cl_uchar2 v2[4]; -#endif -#if defined( __CL_UCHAR4__) - __cl_uchar4 v4[2]; -#endif -#if defined( __CL_UCHAR8__ ) - __cl_uchar8 v8; -#endif -}cl_uchar8; - -typedef union -{ - cl_uchar CL_ALIGNED(16) s[16]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_uchar x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; - __CL_ANON_STRUCT__ struct{ cl_uchar s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; - __CL_ANON_STRUCT__ struct{ cl_uchar8 lo, hi; }; -#endif -#if defined( __CL_UCHAR2__) - __cl_uchar2 v2[8]; -#endif -#if defined( __CL_UCHAR4__) - __cl_uchar4 v4[4]; -#endif -#if defined( __CL_UCHAR8__ ) - __cl_uchar8 v8[2]; -#endif -#if defined( __CL_UCHAR16__ ) - __cl_uchar16 v16; -#endif -}cl_uchar16; - - -/* ---- cl_shortn ---- */ -typedef union -{ - cl_short CL_ALIGNED(4) s[2]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_short x, y; }; - __CL_ANON_STRUCT__ struct{ cl_short s0, s1; }; - __CL_ANON_STRUCT__ struct{ cl_short lo, hi; }; -#endif -#if defined( __CL_SHORT2__) - __cl_short2 v2; -#endif -}cl_short2; - -typedef union -{ - cl_short CL_ALIGNED(8) s[4]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_short x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_short s0, s1, s2, s3; }; - __CL_ANON_STRUCT__ struct{ cl_short2 lo, hi; }; -#endif -#if defined( __CL_SHORT2__) - __cl_short2 v2[2]; -#endif -#if defined( __CL_SHORT4__) - __cl_short4 v4; -#endif -}cl_short4; - -/* cl_short3 is identical in size, alignment and behavior to cl_short4. See section 6.1.5. */ -typedef cl_short4 cl_short3; - -typedef union -{ - cl_short CL_ALIGNED(16) s[8]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_short x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_short s0, s1, s2, s3, s4, s5, s6, s7; }; - __CL_ANON_STRUCT__ struct{ cl_short4 lo, hi; }; -#endif -#if defined( __CL_SHORT2__) - __cl_short2 v2[4]; -#endif -#if defined( __CL_SHORT4__) - __cl_short4 v4[2]; -#endif -#if defined( __CL_SHORT8__ ) - __cl_short8 v8; -#endif -}cl_short8; - -typedef union -{ - cl_short CL_ALIGNED(32) s[16]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_short x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; - __CL_ANON_STRUCT__ struct{ cl_short s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; - __CL_ANON_STRUCT__ struct{ cl_short8 lo, hi; }; -#endif -#if defined( __CL_SHORT2__) - __cl_short2 v2[8]; -#endif -#if defined( __CL_SHORT4__) - __cl_short4 v4[4]; -#endif -#if defined( __CL_SHORT8__ ) - __cl_short8 v8[2]; -#endif -#if defined( __CL_SHORT16__ ) - __cl_short16 v16; -#endif -}cl_short16; - - -/* ---- cl_ushortn ---- */ -typedef union -{ - cl_ushort CL_ALIGNED(4) s[2]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_ushort x, y; }; - __CL_ANON_STRUCT__ struct{ cl_ushort s0, s1; }; - __CL_ANON_STRUCT__ struct{ cl_ushort lo, hi; }; -#endif -#if defined( __CL_USHORT2__) - __cl_ushort2 v2; -#endif -}cl_ushort2; - -typedef union -{ - cl_ushort CL_ALIGNED(8) s[4]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_ushort x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_ushort s0, s1, s2, s3; }; - __CL_ANON_STRUCT__ struct{ cl_ushort2 lo, hi; }; -#endif -#if defined( __CL_USHORT2__) - __cl_ushort2 v2[2]; -#endif -#if defined( __CL_USHORT4__) - __cl_ushort4 v4; -#endif -}cl_ushort4; - -/* cl_ushort3 is identical in size, alignment and behavior to cl_ushort4. See section 6.1.5. */ -typedef cl_ushort4 cl_ushort3; - -typedef union -{ - cl_ushort CL_ALIGNED(16) s[8]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_ushort x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_ushort s0, s1, s2, s3, s4, s5, s6, s7; }; - __CL_ANON_STRUCT__ struct{ cl_ushort4 lo, hi; }; -#endif -#if defined( __CL_USHORT2__) - __cl_ushort2 v2[4]; -#endif -#if defined( __CL_USHORT4__) - __cl_ushort4 v4[2]; -#endif -#if defined( __CL_USHORT8__ ) - __cl_ushort8 v8; -#endif -}cl_ushort8; - -typedef union -{ - cl_ushort CL_ALIGNED(32) s[16]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_ushort x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; - __CL_ANON_STRUCT__ struct{ cl_ushort s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; - __CL_ANON_STRUCT__ struct{ cl_ushort8 lo, hi; }; -#endif -#if defined( __CL_USHORT2__) - __cl_ushort2 v2[8]; -#endif -#if defined( __CL_USHORT4__) - __cl_ushort4 v4[4]; -#endif -#if defined( __CL_USHORT8__ ) - __cl_ushort8 v8[2]; -#endif -#if defined( __CL_USHORT16__ ) - __cl_ushort16 v16; -#endif -}cl_ushort16; - - -/* ---- cl_halfn ---- */ -typedef union -{ - cl_half CL_ALIGNED(4) s[2]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_half x, y; }; - __CL_ANON_STRUCT__ struct{ cl_half s0, s1; }; - __CL_ANON_STRUCT__ struct{ cl_half lo, hi; }; -#endif -#if defined( __CL_HALF2__) - __cl_half2 v2; -#endif -}cl_half2; - -typedef union -{ - cl_half CL_ALIGNED(8) s[4]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_half x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_half s0, s1, s2, s3; }; - __CL_ANON_STRUCT__ struct{ cl_half2 lo, hi; }; -#endif -#if defined( __CL_HALF2__) - __cl_half2 v2[2]; -#endif -#if defined( __CL_HALF4__) - __cl_half4 v4; -#endif -}cl_half4; - -/* cl_half3 is identical in size, alignment and behavior to cl_half4. See section 6.1.5. */ -typedef cl_half4 cl_half3; - -typedef union -{ - cl_half CL_ALIGNED(16) s[8]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_half x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_half s0, s1, s2, s3, s4, s5, s6, s7; }; - __CL_ANON_STRUCT__ struct{ cl_half4 lo, hi; }; -#endif -#if defined( __CL_HALF2__) - __cl_half2 v2[4]; -#endif -#if defined( __CL_HALF4__) - __cl_half4 v4[2]; -#endif -#if defined( __CL_HALF8__ ) - __cl_half8 v8; -#endif -}cl_half8; - -typedef union -{ - cl_half CL_ALIGNED(32) s[16]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_half x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; - __CL_ANON_STRUCT__ struct{ cl_half s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; - __CL_ANON_STRUCT__ struct{ cl_half8 lo, hi; }; -#endif -#if defined( __CL_HALF2__) - __cl_half2 v2[8]; -#endif -#if defined( __CL_HALF4__) - __cl_half4 v4[4]; -#endif -#if defined( __CL_HALF8__ ) - __cl_half8 v8[2]; -#endif -#if defined( __CL_HALF16__ ) - __cl_half16 v16; -#endif -}cl_half16; - -/* ---- cl_intn ---- */ -typedef union -{ - cl_int CL_ALIGNED(8) s[2]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_int x, y; }; - __CL_ANON_STRUCT__ struct{ cl_int s0, s1; }; - __CL_ANON_STRUCT__ struct{ cl_int lo, hi; }; -#endif -#if defined( __CL_INT2__) - __cl_int2 v2; -#endif -}cl_int2; - -typedef union -{ - cl_int CL_ALIGNED(16) s[4]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_int x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_int s0, s1, s2, s3; }; - __CL_ANON_STRUCT__ struct{ cl_int2 lo, hi; }; -#endif -#if defined( __CL_INT2__) - __cl_int2 v2[2]; -#endif -#if defined( __CL_INT4__) - __cl_int4 v4; -#endif -}cl_int4; - -/* cl_int3 is identical in size, alignment and behavior to cl_int4. See section 6.1.5. */ -typedef cl_int4 cl_int3; - -typedef union -{ - cl_int CL_ALIGNED(32) s[8]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_int x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_int s0, s1, s2, s3, s4, s5, s6, s7; }; - __CL_ANON_STRUCT__ struct{ cl_int4 lo, hi; }; -#endif -#if defined( __CL_INT2__) - __cl_int2 v2[4]; -#endif -#if defined( __CL_INT4__) - __cl_int4 v4[2]; -#endif -#if defined( __CL_INT8__ ) - __cl_int8 v8; -#endif -}cl_int8; - -typedef union -{ - cl_int CL_ALIGNED(64) s[16]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_int x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; - __CL_ANON_STRUCT__ struct{ cl_int s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; - __CL_ANON_STRUCT__ struct{ cl_int8 lo, hi; }; -#endif -#if defined( __CL_INT2__) - __cl_int2 v2[8]; -#endif -#if defined( __CL_INT4__) - __cl_int4 v4[4]; -#endif -#if defined( __CL_INT8__ ) - __cl_int8 v8[2]; -#endif -#if defined( __CL_INT16__ ) - __cl_int16 v16; -#endif -}cl_int16; - - -/* ---- cl_uintn ---- */ -typedef union -{ - cl_uint CL_ALIGNED(8) s[2]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_uint x, y; }; - __CL_ANON_STRUCT__ struct{ cl_uint s0, s1; }; - __CL_ANON_STRUCT__ struct{ cl_uint lo, hi; }; -#endif -#if defined( __CL_UINT2__) - __cl_uint2 v2; -#endif -}cl_uint2; - -typedef union -{ - cl_uint CL_ALIGNED(16) s[4]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_uint x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_uint s0, s1, s2, s3; }; - __CL_ANON_STRUCT__ struct{ cl_uint2 lo, hi; }; -#endif -#if defined( __CL_UINT2__) - __cl_uint2 v2[2]; -#endif -#if defined( __CL_UINT4__) - __cl_uint4 v4; -#endif -}cl_uint4; - -/* cl_uint3 is identical in size, alignment and behavior to cl_uint4. See section 6.1.5. */ -typedef cl_uint4 cl_uint3; - -typedef union -{ - cl_uint CL_ALIGNED(32) s[8]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_uint x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_uint s0, s1, s2, s3, s4, s5, s6, s7; }; - __CL_ANON_STRUCT__ struct{ cl_uint4 lo, hi; }; -#endif -#if defined( __CL_UINT2__) - __cl_uint2 v2[4]; -#endif -#if defined( __CL_UINT4__) - __cl_uint4 v4[2]; -#endif -#if defined( __CL_UINT8__ ) - __cl_uint8 v8; -#endif -}cl_uint8; - -typedef union -{ - cl_uint CL_ALIGNED(64) s[16]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_uint x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; - __CL_ANON_STRUCT__ struct{ cl_uint s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; - __CL_ANON_STRUCT__ struct{ cl_uint8 lo, hi; }; -#endif -#if defined( __CL_UINT2__) - __cl_uint2 v2[8]; -#endif -#if defined( __CL_UINT4__) - __cl_uint4 v4[4]; -#endif -#if defined( __CL_UINT8__ ) - __cl_uint8 v8[2]; -#endif -#if defined( __CL_UINT16__ ) - __cl_uint16 v16; -#endif -}cl_uint16; - -/* ---- cl_longn ---- */ -typedef union -{ - cl_long CL_ALIGNED(16) s[2]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_long x, y; }; - __CL_ANON_STRUCT__ struct{ cl_long s0, s1; }; - __CL_ANON_STRUCT__ struct{ cl_long lo, hi; }; -#endif -#if defined( __CL_LONG2__) - __cl_long2 v2; -#endif -}cl_long2; - -typedef union -{ - cl_long CL_ALIGNED(32) s[4]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_long x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_long s0, s1, s2, s3; }; - __CL_ANON_STRUCT__ struct{ cl_long2 lo, hi; }; -#endif -#if defined( __CL_LONG2__) - __cl_long2 v2[2]; -#endif -#if defined( __CL_LONG4__) - __cl_long4 v4; -#endif -}cl_long4; - -/* cl_long3 is identical in size, alignment and behavior to cl_long4. See section 6.1.5. */ -typedef cl_long4 cl_long3; - -typedef union -{ - cl_long CL_ALIGNED(64) s[8]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_long x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_long s0, s1, s2, s3, s4, s5, s6, s7; }; - __CL_ANON_STRUCT__ struct{ cl_long4 lo, hi; }; -#endif -#if defined( __CL_LONG2__) - __cl_long2 v2[4]; -#endif -#if defined( __CL_LONG4__) - __cl_long4 v4[2]; -#endif -#if defined( __CL_LONG8__ ) - __cl_long8 v8; -#endif -}cl_long8; - -typedef union -{ - cl_long CL_ALIGNED(128) s[16]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_long x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; - __CL_ANON_STRUCT__ struct{ cl_long s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; - __CL_ANON_STRUCT__ struct{ cl_long8 lo, hi; }; -#endif -#if defined( __CL_LONG2__) - __cl_long2 v2[8]; -#endif -#if defined( __CL_LONG4__) - __cl_long4 v4[4]; -#endif -#if defined( __CL_LONG8__ ) - __cl_long8 v8[2]; -#endif -#if defined( __CL_LONG16__ ) - __cl_long16 v16; -#endif -}cl_long16; - - -/* ---- cl_ulongn ---- */ -typedef union -{ - cl_ulong CL_ALIGNED(16) s[2]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_ulong x, y; }; - __CL_ANON_STRUCT__ struct{ cl_ulong s0, s1; }; - __CL_ANON_STRUCT__ struct{ cl_ulong lo, hi; }; -#endif -#if defined( __CL_ULONG2__) - __cl_ulong2 v2; -#endif -}cl_ulong2; - -typedef union -{ - cl_ulong CL_ALIGNED(32) s[4]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_ulong x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_ulong s0, s1, s2, s3; }; - __CL_ANON_STRUCT__ struct{ cl_ulong2 lo, hi; }; -#endif -#if defined( __CL_ULONG2__) - __cl_ulong2 v2[2]; -#endif -#if defined( __CL_ULONG4__) - __cl_ulong4 v4; -#endif -}cl_ulong4; - -/* cl_ulong3 is identical in size, alignment and behavior to cl_ulong4. See section 6.1.5. */ -typedef cl_ulong4 cl_ulong3; - -typedef union -{ - cl_ulong CL_ALIGNED(64) s[8]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_ulong x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_ulong s0, s1, s2, s3, s4, s5, s6, s7; }; - __CL_ANON_STRUCT__ struct{ cl_ulong4 lo, hi; }; -#endif -#if defined( __CL_ULONG2__) - __cl_ulong2 v2[4]; -#endif -#if defined( __CL_ULONG4__) - __cl_ulong4 v4[2]; -#endif -#if defined( __CL_ULONG8__ ) - __cl_ulong8 v8; -#endif -}cl_ulong8; - -typedef union -{ - cl_ulong CL_ALIGNED(128) s[16]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_ulong x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; - __CL_ANON_STRUCT__ struct{ cl_ulong s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; - __CL_ANON_STRUCT__ struct{ cl_ulong8 lo, hi; }; -#endif -#if defined( __CL_ULONG2__) - __cl_ulong2 v2[8]; -#endif -#if defined( __CL_ULONG4__) - __cl_ulong4 v4[4]; -#endif -#if defined( __CL_ULONG8__ ) - __cl_ulong8 v8[2]; -#endif -#if defined( __CL_ULONG16__ ) - __cl_ulong16 v16; -#endif -}cl_ulong16; - - -/* --- cl_floatn ---- */ - -typedef union -{ - cl_float CL_ALIGNED(8) s[2]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_float x, y; }; - __CL_ANON_STRUCT__ struct{ cl_float s0, s1; }; - __CL_ANON_STRUCT__ struct{ cl_float lo, hi; }; -#endif -#if defined( __CL_FLOAT2__) - __cl_float2 v2; -#endif -}cl_float2; - -typedef union -{ - cl_float CL_ALIGNED(16) s[4]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_float x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_float s0, s1, s2, s3; }; - __CL_ANON_STRUCT__ struct{ cl_float2 lo, hi; }; -#endif -#if defined( __CL_FLOAT2__) - __cl_float2 v2[2]; -#endif -#if defined( __CL_FLOAT4__) - __cl_float4 v4; -#endif -}cl_float4; - -/* cl_float3 is identical in size, alignment and behavior to cl_float4. See section 6.1.5. */ -typedef cl_float4 cl_float3; - -typedef union -{ - cl_float CL_ALIGNED(32) s[8]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_float x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_float s0, s1, s2, s3, s4, s5, s6, s7; }; - __CL_ANON_STRUCT__ struct{ cl_float4 lo, hi; }; -#endif -#if defined( __CL_FLOAT2__) - __cl_float2 v2[4]; -#endif -#if defined( __CL_FLOAT4__) - __cl_float4 v4[2]; -#endif -#if defined( __CL_FLOAT8__ ) - __cl_float8 v8; -#endif -}cl_float8; - -typedef union -{ - cl_float CL_ALIGNED(64) s[16]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_float x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; - __CL_ANON_STRUCT__ struct{ cl_float s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; - __CL_ANON_STRUCT__ struct{ cl_float8 lo, hi; }; -#endif -#if defined( __CL_FLOAT2__) - __cl_float2 v2[8]; -#endif -#if defined( __CL_FLOAT4__) - __cl_float4 v4[4]; -#endif -#if defined( __CL_FLOAT8__ ) - __cl_float8 v8[2]; -#endif -#if defined( __CL_FLOAT16__ ) - __cl_float16 v16; -#endif -}cl_float16; - -/* --- cl_doublen ---- */ - -typedef union -{ - cl_double CL_ALIGNED(16) s[2]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_double x, y; }; - __CL_ANON_STRUCT__ struct{ cl_double s0, s1; }; - __CL_ANON_STRUCT__ struct{ cl_double lo, hi; }; -#endif -#if defined( __CL_DOUBLE2__) - __cl_double2 v2; -#endif -}cl_double2; - -typedef union -{ - cl_double CL_ALIGNED(32) s[4]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_double x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_double s0, s1, s2, s3; }; - __CL_ANON_STRUCT__ struct{ cl_double2 lo, hi; }; -#endif -#if defined( __CL_DOUBLE2__) - __cl_double2 v2[2]; -#endif -#if defined( __CL_DOUBLE4__) - __cl_double4 v4; -#endif -}cl_double4; - -/* cl_double3 is identical in size, alignment and behavior to cl_double4. See section 6.1.5. */ -typedef cl_double4 cl_double3; - -typedef union -{ - cl_double CL_ALIGNED(64) s[8]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_double x, y, z, w; }; - __CL_ANON_STRUCT__ struct{ cl_double s0, s1, s2, s3, s4, s5, s6, s7; }; - __CL_ANON_STRUCT__ struct{ cl_double4 lo, hi; }; -#endif -#if defined( __CL_DOUBLE2__) - __cl_double2 v2[4]; -#endif -#if defined( __CL_DOUBLE4__) - __cl_double4 v4[2]; -#endif -#if defined( __CL_DOUBLE8__ ) - __cl_double8 v8; -#endif -}cl_double8; - -typedef union -{ - cl_double CL_ALIGNED(128) s[16]; -#if __CL_HAS_ANON_STRUCT__ - __CL_ANON_STRUCT__ struct{ cl_double x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; - __CL_ANON_STRUCT__ struct{ cl_double s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; - __CL_ANON_STRUCT__ struct{ cl_double8 lo, hi; }; -#endif -#if defined( __CL_DOUBLE2__) - __cl_double2 v2[8]; -#endif -#if defined( __CL_DOUBLE4__) - __cl_double4 v4[4]; -#endif -#if defined( __CL_DOUBLE8__ ) - __cl_double8 v8[2]; -#endif -#if defined( __CL_DOUBLE16__ ) - __cl_double16 v16; -#endif -}cl_double16; - -/* Macro to facilitate debugging - * Usage: - * Place CL_PROGRAM_STRING_DEBUG_INFO on the line before the first line of your source. - * The first line ends with: CL_PROGRAM_STRING_DEBUG_INFO \" - * Each line thereafter of OpenCL C source must end with: \n\ - * The last line ends in "; - * - * Example: - * - * const char *my_program = CL_PROGRAM_STRING_DEBUG_INFO "\ - * kernel void foo( int a, float * b ) \n\ - * { \n\ - * // my comment \n\ - * *b[ get_global_id(0)] = a; \n\ - * } \n\ - * "; - * - * This should correctly set up the line, (column) and file information for your source - * string so you can do source level debugging. - */ -#define __CL_STRINGIFY( _x ) # _x -#define _CL_STRINGIFY( _x ) __CL_STRINGIFY( _x ) -#define CL_PROGRAM_STRING_DEBUG_INFO "#line " _CL_STRINGIFY(__LINE__) " \"" __FILE__ "\" \n\n" - -#ifdef __cplusplus -} -#endif - -#undef __CL_HAS_ANON_STRUCT__ -#undef __CL_ANON_STRUCT__ -#if defined( _WIN32) && defined(_MSC_VER) - #if _MSC_VER >=1500 - #pragma warning( pop ) - #endif -#endif - -#endif /* __CL_PLATFORM_H */ diff --git a/src/CL/cl_va_api_media_sharing_intel.h b/src/CL/cl_va_api_media_sharing_intel.h deleted file mode 100644 index 0e7cd4d..0000000 --- a/src/CL/cl_va_api_media_sharing_intel.h +++ /dev/null @@ -1,160 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008-2020 The Khronos Group Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -/*****************************************************************************\ - -Copyright (c) 2013-2019 Intel Corporation All Rights Reserved. - -THESE MATERIALS ARE PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR ITS -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THESE -MATERIALS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -File Name: cl_va_api_media_sharing_intel.h - -Abstract: - -Notes: - -\*****************************************************************************/ - - -#ifndef __OPENCL_CL_VA_API_MEDIA_SHARING_INTEL_H -#define __OPENCL_CL_VA_API_MEDIA_SHARING_INTEL_H - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/****************************************** -* cl_intel_va_api_media_sharing extension * -*******************************************/ - -#define cl_intel_va_api_media_sharing 1 - -/* error codes */ -#define CL_INVALID_VA_API_MEDIA_ADAPTER_INTEL -1098 -#define CL_INVALID_VA_API_MEDIA_SURFACE_INTEL -1099 -#define CL_VA_API_MEDIA_SURFACE_ALREADY_ACQUIRED_INTEL -1100 -#define CL_VA_API_MEDIA_SURFACE_NOT_ACQUIRED_INTEL -1101 - -/* cl_va_api_device_source_intel */ -#define CL_VA_API_DISPLAY_INTEL 0x4094 - -/* cl_va_api_device_set_intel */ -#define CL_PREFERRED_DEVICES_FOR_VA_API_INTEL 0x4095 -#define CL_ALL_DEVICES_FOR_VA_API_INTEL 0x4096 - -/* cl_context_info */ -#define CL_CONTEXT_VA_API_DISPLAY_INTEL 0x4097 - -/* cl_mem_info */ -#define CL_MEM_VA_API_MEDIA_SURFACE_INTEL 0x4098 - -/* cl_image_info */ -#define CL_IMAGE_VA_API_PLANE_INTEL 0x4099 - -/* cl_command_type */ -#define CL_COMMAND_ACQUIRE_VA_API_MEDIA_SURFACES_INTEL 0x409A -#define CL_COMMAND_RELEASE_VA_API_MEDIA_SURFACES_INTEL 0x409B - -typedef cl_uint cl_va_api_device_source_intel; -typedef cl_uint cl_va_api_device_set_intel; - -extern CL_API_ENTRY cl_int CL_API_CALL -clGetDeviceIDsFromVA_APIMediaAdapterINTEL( - cl_platform_id platform, - cl_va_api_device_source_intel media_adapter_type, - void* media_adapter, - cl_va_api_device_set_intel media_adapter_set, - cl_uint num_entries, - cl_device_id* devices, - cl_uint* num_devices) CL_EXT_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_int (CL_API_CALL * clGetDeviceIDsFromVA_APIMediaAdapterINTEL_fn)( - cl_platform_id platform, - cl_va_api_device_source_intel media_adapter_type, - void* media_adapter, - cl_va_api_device_set_intel media_adapter_set, - cl_uint num_entries, - cl_device_id* devices, - cl_uint* num_devices) CL_EXT_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_mem CL_API_CALL -clCreateFromVA_APIMediaSurfaceINTEL( - cl_context context, - cl_mem_flags flags, - VASurfaceID* surface, - cl_uint plane, - cl_int* errcode_ret) CL_EXT_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_mem (CL_API_CALL * clCreateFromVA_APIMediaSurfaceINTEL_fn)( - cl_context context, - cl_mem_flags flags, - VASurfaceID* surface, - cl_uint plane, - cl_int* errcode_ret) CL_EXT_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueAcquireVA_APIMediaSurfacesINTEL( - cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem* mem_objects, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event) CL_EXT_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clEnqueueAcquireVA_APIMediaSurfacesINTEL_fn)( - cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem* mem_objects, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event) CL_EXT_SUFFIX__VERSION_1_2; - -extern CL_API_ENTRY cl_int CL_API_CALL -clEnqueueReleaseVA_APIMediaSurfacesINTEL( - cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem* mem_objects, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event) CL_EXT_SUFFIX__VERSION_1_2; - -typedef CL_API_ENTRY cl_int (CL_API_CALL *clEnqueueReleaseVA_APIMediaSurfacesINTEL_fn)( - cl_command_queue command_queue, - cl_uint num_objects, - const cl_mem* mem_objects, - cl_uint num_events_in_wait_list, - const cl_event* event_wait_list, - cl_event* event) CL_EXT_SUFFIX__VERSION_1_2; - -#ifdef __cplusplus -} -#endif - -#endif /* __OPENCL_CL_VA_API_MEDIA_SHARING_INTEL_H */ - diff --git a/src/CL/cl_version.h b/src/CL/cl_version.h deleted file mode 100644 index f38280a..0000000 --- a/src/CL/cl_version.h +++ /dev/null @@ -1,81 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2018-2020 The Khronos Group Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -#ifndef __CL_VERSION_H -#define __CL_VERSION_H - -/* Detect which version to target */ -#if !defined(CL_TARGET_OPENCL_VERSION) -#pragma message("cl_version.h: CL_TARGET_OPENCL_VERSION is not defined. Defaulting to 220 (OpenCL 2.2)") -#define CL_TARGET_OPENCL_VERSION 220 -#endif -#if CL_TARGET_OPENCL_VERSION != 100 && \ - CL_TARGET_OPENCL_VERSION != 110 && \ - CL_TARGET_OPENCL_VERSION != 120 && \ - CL_TARGET_OPENCL_VERSION != 200 && \ - CL_TARGET_OPENCL_VERSION != 210 && \ - CL_TARGET_OPENCL_VERSION != 220 && \ - CL_TARGET_OPENCL_VERSION != 300 -#pragma message("cl_version: CL_TARGET_OPENCL_VERSION is not a valid value (100, 110, 120, 200, 210, 220, 300). Defaulting to 220 (OpenCL 2.2)") -#undef CL_TARGET_OPENCL_VERSION -#define CL_TARGET_OPENCL_VERSION 220 -#endif - - -/* OpenCL Version */ -#if CL_TARGET_OPENCL_VERSION >= 300 && !defined(CL_VERSION_3_0) -#define CL_VERSION_3_0 1 -#endif -#if CL_TARGET_OPENCL_VERSION >= 220 && !defined(CL_VERSION_2_2) -#define CL_VERSION_2_2 1 -#endif -#if CL_TARGET_OPENCL_VERSION >= 210 && !defined(CL_VERSION_2_1) -#define CL_VERSION_2_1 1 -#endif -#if CL_TARGET_OPENCL_VERSION >= 200 && !defined(CL_VERSION_2_0) -#define CL_VERSION_2_0 1 -#endif -#if CL_TARGET_OPENCL_VERSION >= 120 && !defined(CL_VERSION_1_2) -#define CL_VERSION_1_2 1 -#endif -#if CL_TARGET_OPENCL_VERSION >= 110 && !defined(CL_VERSION_1_1) -#define CL_VERSION_1_1 1 -#endif -#if CL_TARGET_OPENCL_VERSION >= 100 && !defined(CL_VERSION_1_0) -#define CL_VERSION_1_0 1 -#endif - -/* Allow deprecated APIs for older OpenCL versions. */ -#if CL_TARGET_OPENCL_VERSION <= 220 && !defined(CL_USE_DEPRECATED_OPENCL_2_2_APIS) -#define CL_USE_DEPRECATED_OPENCL_2_2_APIS -#endif -#if CL_TARGET_OPENCL_VERSION <= 210 && !defined(CL_USE_DEPRECATED_OPENCL_2_1_APIS) -#define CL_USE_DEPRECATED_OPENCL_2_1_APIS -#endif -#if CL_TARGET_OPENCL_VERSION <= 200 && !defined(CL_USE_DEPRECATED_OPENCL_2_0_APIS) -#define CL_USE_DEPRECATED_OPENCL_2_0_APIS -#endif -#if CL_TARGET_OPENCL_VERSION <= 120 && !defined(CL_USE_DEPRECATED_OPENCL_1_2_APIS) -#define CL_USE_DEPRECATED_OPENCL_1_2_APIS -#endif -#if CL_TARGET_OPENCL_VERSION <= 110 && !defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) -#define CL_USE_DEPRECATED_OPENCL_1_1_APIS -#endif -#if CL_TARGET_OPENCL_VERSION <= 100 && !defined(CL_USE_DEPRECATED_OPENCL_1_0_APIS) -#define CL_USE_DEPRECATED_OPENCL_1_0_APIS -#endif - -#endif /* __CL_VERSION_H */ diff --git a/src/CL/opencl.h b/src/CL/opencl.h deleted file mode 100644 index 1c4e10c..0000000 --- a/src/CL/opencl.h +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008-2020 The Khronos Group Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -#ifndef __OPENCL_H -#define __OPENCL_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include - -#ifdef __cplusplus -} -#endif - -#endif /* __OPENCL_H */ diff --git a/src/acc/Makefile b/src/acc/Makefile deleted file mode 100644 index 55fbfbd..0000000 --- a/src/acc/Makefile +++ /dev/null @@ -1,58 +0,0 @@ - -ifndef COMPILER -define compiler_help -Set COMPILER to ensure correct flags are set. -Available compilers are: - PGI GNU -endef -$(info $(compiler_help)) -endif - -COMPILER_ = $(CXX) -COMPILER_PGI = pgc++ -COMPILER_GNU = g++ - -FLAGS_ = -O3 -std=c++11 - -FLAGS_PGI = -std=c++11 -O3 -acc -ifeq ($(COMPILER), PGI) -define target_help -Set a TARGET to ensure PGI targets the correct offload device. -Available targets are: - SNB, IVB, HSW, SKL, KNL - PWR9, AMD - KEPLER, MAXWELL, PASCAL, VOLTA - HAWAII -endef -ifndef TARGET -$(error $(target_help)) -endif -TARGET_FLAGS_SNB = -ta=multicore -tp=sandybridge -TARGET_FLAGS_IVB = -ta=multicore -tp=ivybridge -TARGET_FLAGS_HSW = -ta=multicore -tp=haswell -TARGET_FLAGS_SKL = -ta=multicore -tp=skylake -TARGET_FLAGS_KNL = -ta=multicore -tp=knl -TARGET_FLAGS_PWR9 = -ta=multicore -tp=pwr9 -TARGET_FLAGS_AMD = -ta=multicore -tp=zen -TARGET_FLAGS_KEPLER = -ta=nvidia:cc35 -TARGET_FLAGS_MAXWELL = -ta=nvidia:cc50 -TARGET_FLAGS_PASCAL = -ta=nvidia:cc60 -TARGET_FLAGS_VOLTA = -ta=nvidia:cc70 -TARGET_FLAGS_HAWAII = -ta=radeon:hawaii -ifeq ($(TARGET_FLAGS_$(TARGET)),) -$(error $(target_help)) -endif - -FLAGS_PGI += $(TARGET_FLAGS_$(TARGET)) - -endif - -FLAGS_GNU = -O3 -std=c++11 -Drestrict=__restrict -fopenacc -CXXFLAGS = $(FLAGS_$(COMPILER)) - -acc-stream: ../main.cpp ACCStream.cpp - $(COMPILER_$(COMPILER)) $(CXXFLAGS) -DACC $^ $(EXTRA_FLAGS) -o $@ -I. -I.. - -.PHONY: clean -clean: - rm -f acc-stream main.o ACCStream.o diff --git a/src/cuda/Makefile b/src/cuda/Makefile deleted file mode 100644 index 153f07d..0000000 --- a/src/cuda/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -CXXFLAGS=-O3 -CUDA_CXX=nvcc - - -ifndef NVARCH -define nvarch_help -Set NVARCH to select sm_?? version. -Default: sm_60 - -endef -$(info $(nvarch_help)) -NVARCH=sm_60 -endif - - -ifndef MEM -define mem_help -Set MEM to select memory mode. -Available options: - DEFAULT - allocate host and device memory pointers. - MANAGED - use CUDA Managed Memory. - PAGEFAULT - shared memory, only host pointers allocated. - -endef -$(info $(mem_help)) -MEM=DEFAULT -endif - -MEM_MANAGED= -DMANAGED -MEM_PAGEFAULT= -DPAGEFAULT -MEM_MODE = $(MEM_$(MEM)) - - -cuda-stream: ../main.cpp CUDAStream.cu - $(CUDA_CXX) -std=c++11 $(CXXFLAGS) -arch=$(NVARCH) $(MEM_MODE) -DCUDA $^ $(EXTRA_FLAGS) -o $@ -I. -I.. - -.PHONY: clean -clean: - rm -f cuda-stream - diff --git a/src/hip/Makefile b/src/hip/Makefile deleted file mode 100644 index 21383b0..0000000 --- a/src/hip/Makefile +++ /dev/null @@ -1,11 +0,0 @@ - -HIP_PATH?= /opt/rocm/hip -HIPCC=$(HIP_PATH)/bin/hipcc - -hip-stream: ../main.cpp HIPStream.cpp - $(HIPCC) $(CXXFLAGS) -O3 -std=c++11 -DHIP $^ $(EXTRA_FLAGS) -o $@ -I. -I.. - -.PHONY: clean -clean: - rm -f hip-stream - diff --git a/java-stream/.gitignore b/src/java/java-stream/.gitignore similarity index 100% rename from java-stream/.gitignore rename to src/java/java-stream/.gitignore diff --git a/java-stream/.mvn/wrapper/maven-wrapper.jar b/src/java/java-stream/.mvn/wrapper/maven-wrapper.jar similarity index 100% rename from java-stream/.mvn/wrapper/maven-wrapper.jar rename to src/java/java-stream/.mvn/wrapper/maven-wrapper.jar diff --git a/java-stream/.mvn/wrapper/maven-wrapper.properties b/src/java/java-stream/.mvn/wrapper/maven-wrapper.properties similarity index 100% rename from java-stream/.mvn/wrapper/maven-wrapper.properties rename to src/java/java-stream/.mvn/wrapper/maven-wrapper.properties diff --git a/java-stream/README.md b/src/java/java-stream/README.md similarity index 100% rename from java-stream/README.md rename to src/java/java-stream/README.md diff --git a/java-stream/mvnw b/src/java/java-stream/mvnw similarity index 100% rename from java-stream/mvnw rename to src/java/java-stream/mvnw diff --git a/java-stream/mvnw.cmd b/src/java/java-stream/mvnw.cmd similarity index 100% rename from java-stream/mvnw.cmd rename to src/java/java-stream/mvnw.cmd diff --git a/java-stream/pom.xml b/src/java/java-stream/pom.xml similarity index 100% rename from java-stream/pom.xml rename to src/java/java-stream/pom.xml diff --git a/java-stream/src/main/java/javastream/FractionalMaths.java b/src/java/java-stream/src/main/java/javastream/FractionalMaths.java similarity index 100% rename from java-stream/src/main/java/javastream/FractionalMaths.java rename to src/java/java-stream/src/main/java/javastream/FractionalMaths.java diff --git a/java-stream/src/main/java/javastream/JavaStream.java b/src/java/java-stream/src/main/java/javastream/JavaStream.java similarity index 100% rename from java-stream/src/main/java/javastream/JavaStream.java rename to src/java/java-stream/src/main/java/javastream/JavaStream.java diff --git a/java-stream/src/main/java/javastream/Main.java b/src/java/java-stream/src/main/java/javastream/Main.java similarity index 100% rename from java-stream/src/main/java/javastream/Main.java rename to src/java/java-stream/src/main/java/javastream/Main.java diff --git a/java-stream/src/main/java/javastream/aparapi/AparapiStreams.java b/src/java/java-stream/src/main/java/javastream/aparapi/AparapiStreams.java similarity index 100% rename from java-stream/src/main/java/javastream/aparapi/AparapiStreams.java rename to src/java/java-stream/src/main/java/javastream/aparapi/AparapiStreams.java diff --git a/java-stream/src/main/java/javastream/aparapi/GenericAparapiStreamKernel.java b/src/java/java-stream/src/main/java/javastream/aparapi/GenericAparapiStreamKernel.java similarity index 100% rename from java-stream/src/main/java/javastream/aparapi/GenericAparapiStreamKernel.java rename to src/java/java-stream/src/main/java/javastream/aparapi/GenericAparapiStreamKernel.java diff --git a/java-stream/src/main/java/javastream/aparapi/SpecialisedDoubleKernel.java b/src/java/java-stream/src/main/java/javastream/aparapi/SpecialisedDoubleKernel.java similarity index 100% rename from java-stream/src/main/java/javastream/aparapi/SpecialisedDoubleKernel.java rename to src/java/java-stream/src/main/java/javastream/aparapi/SpecialisedDoubleKernel.java diff --git a/java-stream/src/main/java/javastream/aparapi/SpecialisedFloatKernel.java b/src/java/java-stream/src/main/java/javastream/aparapi/SpecialisedFloatKernel.java similarity index 100% rename from java-stream/src/main/java/javastream/aparapi/SpecialisedFloatKernel.java rename to src/java/java-stream/src/main/java/javastream/aparapi/SpecialisedFloatKernel.java diff --git a/java-stream/src/main/java/javastream/jdk/GenericPlainStream.java b/src/java/java-stream/src/main/java/javastream/jdk/GenericPlainStream.java similarity index 100% rename from java-stream/src/main/java/javastream/jdk/GenericPlainStream.java rename to src/java/java-stream/src/main/java/javastream/jdk/GenericPlainStream.java diff --git a/java-stream/src/main/java/javastream/jdk/GenericStream.java b/src/java/java-stream/src/main/java/javastream/jdk/GenericStream.java similarity index 100% rename from java-stream/src/main/java/javastream/jdk/GenericStream.java rename to src/java/java-stream/src/main/java/javastream/jdk/GenericStream.java diff --git a/java-stream/src/main/java/javastream/jdk/JdkStreams.java b/src/java/java-stream/src/main/java/javastream/jdk/JdkStreams.java similarity index 100% rename from java-stream/src/main/java/javastream/jdk/JdkStreams.java rename to src/java/java-stream/src/main/java/javastream/jdk/JdkStreams.java diff --git a/java-stream/src/main/java/javastream/jdk/PlainStream.java b/src/java/java-stream/src/main/java/javastream/jdk/PlainStream.java similarity index 100% rename from java-stream/src/main/java/javastream/jdk/PlainStream.java rename to src/java/java-stream/src/main/java/javastream/jdk/PlainStream.java diff --git a/java-stream/src/main/java/javastream/jdk/SpecialisedDoubleStream.java b/src/java/java-stream/src/main/java/javastream/jdk/SpecialisedDoubleStream.java similarity index 100% rename from java-stream/src/main/java/javastream/jdk/SpecialisedDoubleStream.java rename to src/java/java-stream/src/main/java/javastream/jdk/SpecialisedDoubleStream.java diff --git a/java-stream/src/main/java/javastream/jdk/SpecialisedFloatStream.java b/src/java/java-stream/src/main/java/javastream/jdk/SpecialisedFloatStream.java similarity index 100% rename from java-stream/src/main/java/javastream/jdk/SpecialisedFloatStream.java rename to src/java/java-stream/src/main/java/javastream/jdk/SpecialisedFloatStream.java diff --git a/java-stream/src/main/java/javastream/jdk/SpecialisedPlainDoubleStream.java b/src/java/java-stream/src/main/java/javastream/jdk/SpecialisedPlainDoubleStream.java similarity index 100% rename from java-stream/src/main/java/javastream/jdk/SpecialisedPlainDoubleStream.java rename to src/java/java-stream/src/main/java/javastream/jdk/SpecialisedPlainDoubleStream.java diff --git a/java-stream/src/main/java/javastream/jdk/SpecialisedPlainFloatStream.java b/src/java/java-stream/src/main/java/javastream/jdk/SpecialisedPlainFloatStream.java similarity index 100% rename from java-stream/src/main/java/javastream/jdk/SpecialisedPlainFloatStream.java rename to src/java/java-stream/src/main/java/javastream/jdk/SpecialisedPlainFloatStream.java diff --git a/java-stream/src/main/java/javastream/tornadovm/GenericTornadoVMStream.java b/src/java/java-stream/src/main/java/javastream/tornadovm/GenericTornadoVMStream.java similarity index 100% rename from java-stream/src/main/java/javastream/tornadovm/GenericTornadoVMStream.java rename to src/java/java-stream/src/main/java/javastream/tornadovm/GenericTornadoVMStream.java diff --git a/java-stream/src/main/java/javastream/tornadovm/SpecialisedDouble.java b/src/java/java-stream/src/main/java/javastream/tornadovm/SpecialisedDouble.java similarity index 100% rename from java-stream/src/main/java/javastream/tornadovm/SpecialisedDouble.java rename to src/java/java-stream/src/main/java/javastream/tornadovm/SpecialisedDouble.java diff --git a/java-stream/src/main/java/javastream/tornadovm/SpecialisedFloat.java b/src/java/java-stream/src/main/java/javastream/tornadovm/SpecialisedFloat.java similarity index 100% rename from java-stream/src/main/java/javastream/tornadovm/SpecialisedFloat.java rename to src/java/java-stream/src/main/java/javastream/tornadovm/SpecialisedFloat.java diff --git a/java-stream/src/main/java/javastream/tornadovm/TornadoVMStreams.java b/src/java/java-stream/src/main/java/javastream/tornadovm/TornadoVMStreams.java similarity index 100% rename from java-stream/src/main/java/javastream/tornadovm/TornadoVMStreams.java rename to src/java/java-stream/src/main/java/javastream/tornadovm/TornadoVMStreams.java diff --git a/java-stream/src/test/java/javastream/SmokeTest.java b/src/java/java-stream/src/test/java/javastream/SmokeTest.java similarity index 100% rename from java-stream/src/test/java/javastream/SmokeTest.java rename to src/java/java-stream/src/test/java/javastream/SmokeTest.java diff --git a/JuliaStream.jl/.JuliaFormatter.toml b/src/julia/JuliaStream.jl/.JuliaFormatter.toml similarity index 100% rename from JuliaStream.jl/.JuliaFormatter.toml rename to src/julia/JuliaStream.jl/.JuliaFormatter.toml diff --git a/JuliaStream.jl/.gitignore b/src/julia/JuliaStream.jl/.gitignore similarity index 100% rename from JuliaStream.jl/.gitignore rename to src/julia/JuliaStream.jl/.gitignore diff --git a/JuliaStream.jl/AMDGPU/Manifest.toml b/src/julia/JuliaStream.jl/AMDGPU/Manifest.toml similarity index 100% rename from JuliaStream.jl/AMDGPU/Manifest.toml rename to src/julia/JuliaStream.jl/AMDGPU/Manifest.toml diff --git a/JuliaStream.jl/AMDGPU/Project.toml b/src/julia/JuliaStream.jl/AMDGPU/Project.toml similarity index 100% rename from JuliaStream.jl/AMDGPU/Project.toml rename to src/julia/JuliaStream.jl/AMDGPU/Project.toml diff --git a/JuliaStream.jl/CUDA/Manifest.toml b/src/julia/JuliaStream.jl/CUDA/Manifest.toml similarity index 100% rename from JuliaStream.jl/CUDA/Manifest.toml rename to src/julia/JuliaStream.jl/CUDA/Manifest.toml diff --git a/JuliaStream.jl/CUDA/Project.toml b/src/julia/JuliaStream.jl/CUDA/Project.toml similarity index 100% rename from JuliaStream.jl/CUDA/Project.toml rename to src/julia/JuliaStream.jl/CUDA/Project.toml diff --git a/JuliaStream.jl/KernelAbstractions/Manifest.toml b/src/julia/JuliaStream.jl/KernelAbstractions/Manifest.toml similarity index 100% rename from JuliaStream.jl/KernelAbstractions/Manifest.toml rename to src/julia/JuliaStream.jl/KernelAbstractions/Manifest.toml diff --git a/JuliaStream.jl/KernelAbstractions/Project.toml b/src/julia/JuliaStream.jl/KernelAbstractions/Project.toml similarity index 100% rename from JuliaStream.jl/KernelAbstractions/Project.toml rename to src/julia/JuliaStream.jl/KernelAbstractions/Project.toml diff --git a/JuliaStream.jl/Manifest.toml b/src/julia/JuliaStream.jl/Manifest.toml similarity index 100% rename from JuliaStream.jl/Manifest.toml rename to src/julia/JuliaStream.jl/Manifest.toml diff --git a/JuliaStream.jl/Project.toml b/src/julia/JuliaStream.jl/Project.toml similarity index 100% rename from JuliaStream.jl/Project.toml rename to src/julia/JuliaStream.jl/Project.toml diff --git a/JuliaStream.jl/README.md b/src/julia/JuliaStream.jl/README.md similarity index 100% rename from JuliaStream.jl/README.md rename to src/julia/JuliaStream.jl/README.md diff --git a/JuliaStream.jl/Threaded/Manifest.toml b/src/julia/JuliaStream.jl/Threaded/Manifest.toml similarity index 100% rename from JuliaStream.jl/Threaded/Manifest.toml rename to src/julia/JuliaStream.jl/Threaded/Manifest.toml diff --git a/JuliaStream.jl/Threaded/Project.toml b/src/julia/JuliaStream.jl/Threaded/Project.toml similarity index 100% rename from JuliaStream.jl/Threaded/Project.toml rename to src/julia/JuliaStream.jl/Threaded/Project.toml diff --git a/JuliaStream.jl/oneAPI/Manifest.toml b/src/julia/JuliaStream.jl/oneAPI/Manifest.toml similarity index 100% rename from JuliaStream.jl/oneAPI/Manifest.toml rename to src/julia/JuliaStream.jl/oneAPI/Manifest.toml diff --git a/JuliaStream.jl/oneAPI/Project.toml b/src/julia/JuliaStream.jl/oneAPI/Project.toml similarity index 100% rename from JuliaStream.jl/oneAPI/Project.toml rename to src/julia/JuliaStream.jl/oneAPI/Project.toml diff --git a/JuliaStream.jl/src/AMDGPUStream.jl b/src/julia/JuliaStream.jl/src/AMDGPUStream.jl similarity index 100% rename from JuliaStream.jl/src/AMDGPUStream.jl rename to src/julia/JuliaStream.jl/src/AMDGPUStream.jl diff --git a/JuliaStream.jl/src/CUDAStream.jl b/src/julia/JuliaStream.jl/src/CUDAStream.jl similarity index 100% rename from JuliaStream.jl/src/CUDAStream.jl rename to src/julia/JuliaStream.jl/src/CUDAStream.jl diff --git a/JuliaStream.jl/src/DistributedStream.jl b/src/julia/JuliaStream.jl/src/DistributedStream.jl similarity index 100% rename from JuliaStream.jl/src/DistributedStream.jl rename to src/julia/JuliaStream.jl/src/DistributedStream.jl diff --git a/JuliaStream.jl/src/JuliaStream.jl b/src/julia/JuliaStream.jl/src/JuliaStream.jl similarity index 100% rename from JuliaStream.jl/src/JuliaStream.jl rename to src/julia/JuliaStream.jl/src/JuliaStream.jl diff --git a/JuliaStream.jl/src/KernelAbstractionsStream.jl b/src/julia/JuliaStream.jl/src/KernelAbstractionsStream.jl similarity index 100% rename from JuliaStream.jl/src/KernelAbstractionsStream.jl rename to src/julia/JuliaStream.jl/src/KernelAbstractionsStream.jl diff --git a/JuliaStream.jl/src/PlainStream.jl b/src/julia/JuliaStream.jl/src/PlainStream.jl similarity index 100% rename from JuliaStream.jl/src/PlainStream.jl rename to src/julia/JuliaStream.jl/src/PlainStream.jl diff --git a/JuliaStream.jl/src/Stream.jl b/src/julia/JuliaStream.jl/src/Stream.jl similarity index 100% rename from JuliaStream.jl/src/Stream.jl rename to src/julia/JuliaStream.jl/src/Stream.jl diff --git a/JuliaStream.jl/src/StreamData.jl b/src/julia/JuliaStream.jl/src/StreamData.jl similarity index 100% rename from JuliaStream.jl/src/StreamData.jl rename to src/julia/JuliaStream.jl/src/StreamData.jl diff --git a/JuliaStream.jl/src/ThreadedStream.jl b/src/julia/JuliaStream.jl/src/ThreadedStream.jl similarity index 100% rename from JuliaStream.jl/src/ThreadedStream.jl rename to src/julia/JuliaStream.jl/src/ThreadedStream.jl diff --git a/JuliaStream.jl/src/oneAPIStream.jl b/src/julia/JuliaStream.jl/src/oneAPIStream.jl similarity index 100% rename from JuliaStream.jl/src/oneAPIStream.jl rename to src/julia/JuliaStream.jl/src/oneAPIStream.jl diff --git a/JuliaStream.jl/update_all.sh b/src/julia/JuliaStream.jl/update_all.sh similarity index 100% rename from JuliaStream.jl/update_all.sh rename to src/julia/JuliaStream.jl/update_all.sh diff --git a/src/kokkos/Makefile b/src/kokkos/Makefile deleted file mode 100644 index 98d8597..0000000 --- a/src/kokkos/Makefile +++ /dev/null @@ -1,99 +0,0 @@ - -default: kokkos-stream - -ifndef DEVICE -define device_help -Set DEVICE to change flags (defaulting to OpenMP). -Available devices are: - OpenMP, Serial, Pthreads, Cuda, HIP - -endef -$(info $(device_help)) -DEVICE="OpenMP" -endif -KOKKOS_DEVICES="$(DEVICE)" - -ifndef ARCH -define arch_help -Set ARCH to change flags (defaulting to empty). -Available architectures are: - AMDAVX - ARMv80 ARMv81 ARMv8-ThunderX - BGQ Power7 Power8 Power9 - WSM SNB HSW BDW SKX KNC KNL - Kepler30 Kepler32 Kepler35 Kepler37 - Maxwell50 Maxwell52 Maxwell53 - Pascal60 Pascal61 - Volta70 Volta72 - -endef -$(info $(arch_help)) -ARCH="" -endif -KOKKOS_ARCH="$(ARCH)" - -ifndef COMPILER -define compiler_help -Set COMPILER to change flags (defaulting to GNU). -Available compilers are: - GNU INTEL CRAY PGI ARMCLANG HIPCC - - Note: you may have to do `export CXX=\path\to\hipcc` in case Kokkos detects the wrong compiler - -endef -$(info $(compiler_help)) -COMPILER=GNU -endif - -COMPILER_ARMCLANG = armclang++ -COMPILER_HIPCC = hipcc -COMPILER_GNU = g++ -COMPILER_INTEL = icpc -qopt-streaming-stores=always -COMPILER_CRAY = CC -COMPILER_PGI = pgc++ -CXX = $(COMPILER_$(COMPILER)) - -ifndef TARGET -define target_help -Set TARGET to change to offload device. Defaulting to CPU. -Available targets are: - CPU (default) - GPU - -endef -$(info $(target_help)) -TARGET=CPU -endif - -ifeq ($(TARGET), GPU) -ifneq ($(COMPILER), HIPCC) -CXX = $(NVCC_WRAPPER) -endif -endif - -OBJ = KokkosStream.o -CXXFLAGS = -O3 -LINKFLAGS = # empty for now - - - -ifeq ($(COMPILER), GNU) -ifeq ($(DEVICE), OpenMP) -CXXFLAGS += -fopenmp -LINKFLAGS += -fopenmp -endif -endif - -include $(KOKKOS_PATH)/Makefile.kokkos -HEADERS = $(wildcard $(MAKEFILE_PATH)*.hpp) - -kokkos-stream: ../main.cpp $(OBJ) $(KOKKOS_LINK_DEPENDS) - $(CXX) $(KOKKOS_LDFLAGS) $(LINKFLAGS) $(EXTRA_PATH) $(OBJ) $(KOKKOS_LIBS) $(LIB) -DKOKKOS -o $@ -I. -I.. - -%.o: %.cpp $(KOKKOS_CPP_DEPENDS) $(HEADERS) - $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) $(EXTRA_INC) -c $< -o $(notdir $@) - -.PHONY: clean -clean: - rm -f kokkos-stream main.o KokkosStream.o Kokkos_*.o KokkosCore_* - diff --git a/legacy/HCStream.cpp b/src/legacy/HCStream.cpp similarity index 100% rename from legacy/HCStream.cpp rename to src/legacy/HCStream.cpp diff --git a/legacy/HCStream.h b/src/legacy/HCStream.h similarity index 100% rename from legacy/HCStream.h rename to src/legacy/HCStream.h diff --git a/src/CL/cl2.hpp b/src/ocl/CL/cl2.hpp similarity index 100% rename from src/CL/cl2.hpp rename to src/ocl/CL/cl2.hpp diff --git a/src/ocl/Makefile b/src/ocl/Makefile deleted file mode 100644 index 20cd257..0000000 --- a/src/ocl/Makefile +++ /dev/null @@ -1,39 +0,0 @@ - -ifndef COMPILER -define compiler_help -Set COMPILER to change flags (defaulting to GNU). -Available compilers are: - GNU CLANG INTEL CRAY - -endef -$(info $(compiler_help)) -COMPILER=GNU -endif - -COMPILER_GNU = g++ -COMPILER_CLANG = clang++ -COMPILER_INTEL = icpc -COMPILER_CRAY = CC -CXX = $(COMPILER_$(COMPILER)) - -FLAGS_ = -O3 -std=c++11 -FLAGS_GNU = -O3 -std=c++11 -FLAGS_CLANG = -O3 -std=c++11 -FLAGS_INTEL = -O3 -std=c++11 -FLAGS_CRAY = -O3 -hstd=c++11 -CXXFLAGS=$(FLAGS_$(COMPILER)) - -PLATFORM = $(shell uname -s) -ifeq ($(PLATFORM), Darwin) - LIBS = -framework OpenCL -else - LIBS = -lOpenCL -endif - -ocl-stream: ../main.cpp OCLStream.cpp - $(CXX) $(CXXFLAGS) -DOCL $^ $(EXTRA_FLAGS) $(LIBS) -o $@ -I. -I.. - -.PHONY: clean -clean: - rm -f ocl-stream - diff --git a/src/omp/Makefile b/src/omp/Makefile deleted file mode 100644 index 15bab8a..0000000 --- a/src/omp/Makefile +++ /dev/null @@ -1,103 +0,0 @@ - -ifndef COMPILER -define compiler_help -Set COMPILER to change flags (defaulting to GNU). -Available compilers are: - CLANG CRAY GNU GNU_PPC INTEL XL PGI - NEC ARMCLANG AOMP FUJITSU - -Note: GCC on PPC requires -mcpu=native instead of -march=native so we have a special case for it - -endef -$(info $(compiler_help)) -COMPILER=GNU -endif - -ifndef TARGET -define target_help -Set TARGET to change device (defaulting to CPU). -Available targets are: - CPU NVIDIA AMD INTEL_GPU - -endef -$(info $(target_help)) -TARGET=CPU -endif - -ifeq ("$(COMPILER)", "CLANG") - ifdef TARGET - ifeq ("$(TARGET)", "NVIDIA") - ifndef NVARCH - define nvarch_help - Set NVARCH to select sm_?? version. - Default: sm_60 - - endef - $(info $(nvarch_help)) - NVARCH=sm_60 - endif - endif - endif -endif - -COMPILER_ARMCLANG = armclang++ -COMPILER_GNU = g++ -COMPILER_GNU_PPC = g++ -COMPILER_INTEL = icpc -COMPILER_CRAY = CC -COMPILER_CLANG = clang++ -COMPILER_XL = xlc++ -COMPILER_PGI = pgc++ -COMPILER_NEC = /opt/nec/ve/bin/nc++ -COMPILER_AOMP = clang++ -COMPILER_FUJITSU=FCC -CXX = $(COMPILER_$(COMPILER)) - -FLAGS_GNU = -O3 -std=c++11 -march=native -FLAGS_GNU_PPC = -O3 -std=c++11 -mcpu=native -FLAGS_INTEL = -O3 -std=c++11 -FLAGS_CRAY = -O3 -std=c++11 -FLAGS_CLANG = -O3 -std=c++11 -FLAGS_XL = -O5 -qarch=auto -qtune=auto -std=c++11 -FLAGS_PGI = -O3 -std=c++11 -FLAGS_NEC = -O4 -finline -std=c++11 -FLAGS_ARMCLANG = -O3 -std=c++11 -FLAGS_AOMP = -O3 -std=c++11 -FLAGS_FUJITSU=-Kfast -std=c++11 -KA64FX -KSVE -KARMV8_3_A -Kzfill=100 -Kprefetch_sequential=soft -Kprefetch_line=8 -Kprefetch_line_L2=16 -CXXFLAGS = $(FLAGS_$(COMPILER)) - -# OpenMP flags for CPUs -OMP_ARMCLANG_CPU = -fopenmp -OMP_GNU_CPU = -fopenmp -OMP_GNU_PPC_CPU = -fopenmp -OMP_INTEL_CPU = -qopenmp -OMP_CRAY_CPU = -fopenmp -OMP_CLANG_CPU = -fopenmp=libomp -OMP_XL_CPU = -qsmp=omp -qthreaded -OMP_PGI_CPU = -mp -OMP_NEC_CPU = -fopenmp -OMP_FUJITSU_CPU=-Kopenmp - -# OpenMP flags for NVIDIA -OMP_CRAY_NVIDIA = -DOMP_TARGET_GPU -OMP_CLANG_NVIDIA = -DOMP_TARGET_GPU -fopenmp=libomp -fopenmp-targets=nvptx64-nvidia-cuda -Xopenmp-target -march=$(NVARCH) -OMP_GNU_NVIDIA = -DOMP_TARGET_GPU -fopenmp -foffload=nvptx-none -OMP_GNU_AMD = -DOMP_TARGET_GPU -fopenmp -foffload=amdgcn-amdhsa - -OMP_INTEL_CPU = -xHOST -qopt-streaming-stores=always -qopenmp -OMP_INTEL_INTEL_GPU = -DOMP_TARGET_GPU -qnextgen -fiopenmp -fopenmp-targets=spir64 - -OMP_AOMP_GPU = -DOMP_TARGET_GPU -fopenmp -fopenmp-targets=amdgcn-amd-amdhsa -Xopenmp-target=amdgcn-amd-amdhsa -march=gfx906 - -ifndef OMP_$(COMPILER)_$(TARGET) -$(error Targeting $(TARGET) with $(COMPILER) not supported) -endif - -OMP = $(OMP_$(COMPILER)_$(TARGET)) - -omp-stream: ../main.cpp OMPStream.cpp - $(CXX) $(CXXFLAGS) -DOMP $^ $(OMP) $(EXTRA_FLAGS) -o $@ -I. -I.. - -.PHONY: clean -clean: - rm -f omp-stream diff --git a/src/raja/Makefile b/src/raja/Makefile deleted file mode 100644 index 60f2319..0000000 --- a/src/raja/Makefile +++ /dev/null @@ -1,58 +0,0 @@ - -ifndef TARGET -define target_help -Set TARGET to change to offload device. Defaulting to CPU. -Available targets are: - CPU (default) - GPU -endef -$(info $(target_help)) -TARGET=CPU -endif - -ifeq ($(TARGET), CPU) - -ifndef COMPILER -define compiler_help -Set COMPILER to change flags (defaulting to GNU). -Available compilers are: - INTEL GNU CRAY XL -endef -$(info $(compiler_help)) -COMPILER=GNU -endif - -CXX_INTEL = icpc -CXX_GNU = g++ -CXX_CRAY = CC -CXX_XL = xlc++ - -CXXFLAGS_INTEL = -O3 -std=c++11 -qopenmp -xHost -qopt-streaming-stores=always -CXXFLAGS_GNU = -O3 -std=c++11 -fopenmp -CXXFLAGS_CRAY = -O3 -hstd=c++11 -CXXFLAGS_XL = -O5 -std=c++11 -qarch=pwr8 -qtune=pwr8 -qsmp=omp -qthreaded - -CXX = $(CXX_$(COMPILER)) -CXXFLAGS = -DRAJA_TARGET_CPU $(CXXFLAGS_$(COMPILER)) - -else ifeq ($(TARGET), GPU) -CXX = nvcc - -ifndef ARCH -define arch_help -Set ARCH to ensure correct GPU architecture. -Example: - ARCH=sm_35 -endef -$(error $(arch_help)) -endif -CXXFLAGS = --expt-extended-lambda -O3 -std=c++11 -x cu -Xcompiler -fopenmp -arch $(ARCH) -endif - -raja-stream: ../main.cpp RAJAStream.cpp - $(CXX) $(CXXFLAGS) -DUSE_RAJA -I$(RAJA_PATH)/include $^ $(EXTRA_FLAGS) -L$(RAJA_PATH)/lib -lRAJA -o $@ -I. -I.. - -.PHONY: clean -clean: - rm -f raja-stream - diff --git a/scala-stream/.bsp/sbt.json b/src/scala/scala-stream/.bsp/sbt.json similarity index 100% rename from scala-stream/.bsp/sbt.json rename to src/scala/scala-stream/.bsp/sbt.json diff --git a/scala-stream/.gitignore b/src/scala/scala-stream/.gitignore similarity index 100% rename from scala-stream/.gitignore rename to src/scala/scala-stream/.gitignore diff --git a/scala-stream/.jvmopts b/src/scala/scala-stream/.jvmopts similarity index 100% rename from scala-stream/.jvmopts rename to src/scala/scala-stream/.jvmopts diff --git a/scala-stream/.scalafmt.conf b/src/scala/scala-stream/.scalafmt.conf similarity index 100% rename from scala-stream/.scalafmt.conf rename to src/scala/scala-stream/.scalafmt.conf diff --git a/scala-stream/README.md b/src/scala/scala-stream/README.md similarity index 100% rename from scala-stream/README.md rename to src/scala/scala-stream/README.md diff --git a/scala-stream/build.sbt b/src/scala/scala-stream/build.sbt similarity index 100% rename from scala-stream/build.sbt rename to src/scala/scala-stream/build.sbt diff --git a/scala-stream/project/build.properties b/src/scala/scala-stream/project/build.properties similarity index 100% rename from scala-stream/project/build.properties rename to src/scala/scala-stream/project/build.properties diff --git a/scala-stream/project/plugins.sbt b/src/scala/scala-stream/project/plugins.sbt similarity index 100% rename from scala-stream/project/plugins.sbt rename to src/scala/scala-stream/project/plugins.sbt diff --git a/scala-stream/reflect-config.json b/src/scala/scala-stream/reflect-config.json similarity index 100% rename from scala-stream/reflect-config.json rename to src/scala/scala-stream/reflect-config.json diff --git a/scala-stream/sbt b/src/scala/scala-stream/sbt similarity index 100% rename from scala-stream/sbt rename to src/scala/scala-stream/sbt diff --git a/scala-stream/sbt-dist/bin/java9-rt-export.jar b/src/scala/scala-stream/sbt-dist/bin/java9-rt-export.jar similarity index 100% rename from scala-stream/sbt-dist/bin/java9-rt-export.jar rename to src/scala/scala-stream/sbt-dist/bin/java9-rt-export.jar diff --git a/scala-stream/sbt-dist/bin/sbt b/src/scala/scala-stream/sbt-dist/bin/sbt similarity index 100% rename from scala-stream/sbt-dist/bin/sbt rename to src/scala/scala-stream/sbt-dist/bin/sbt diff --git a/scala-stream/sbt-dist/bin/sbt-launch-lib.bash b/src/scala/scala-stream/sbt-dist/bin/sbt-launch-lib.bash similarity index 100% rename from scala-stream/sbt-dist/bin/sbt-launch-lib.bash rename to src/scala/scala-stream/sbt-dist/bin/sbt-launch-lib.bash diff --git a/scala-stream/sbt-dist/bin/sbt-launch.jar b/src/scala/scala-stream/sbt-dist/bin/sbt-launch.jar similarity index 100% rename from scala-stream/sbt-dist/bin/sbt-launch.jar rename to src/scala/scala-stream/sbt-dist/bin/sbt-launch.jar diff --git a/scala-stream/sbt-dist/bin/sbt.bat b/src/scala/scala-stream/sbt-dist/bin/sbt.bat similarity index 100% rename from scala-stream/sbt-dist/bin/sbt.bat rename to src/scala/scala-stream/sbt-dist/bin/sbt.bat diff --git a/scala-stream/sbt-dist/conf/sbtconfig.txt b/src/scala/scala-stream/sbt-dist/conf/sbtconfig.txt similarity index 100% rename from scala-stream/sbt-dist/conf/sbtconfig.txt rename to src/scala/scala-stream/sbt-dist/conf/sbtconfig.txt diff --git a/scala-stream/sbt-dist/conf/sbtopts b/src/scala/scala-stream/sbt-dist/conf/sbtopts similarity index 100% rename from scala-stream/sbt-dist/conf/sbtopts rename to src/scala/scala-stream/sbt-dist/conf/sbtopts diff --git a/scala-stream/src/main/scala/scalastream/J8SStream.scala b/src/scala/scala-stream/src/main/scala/scalastream/J8SStream.scala similarity index 100% rename from scala-stream/src/main/scala/scalastream/J8SStream.scala rename to src/scala/scala-stream/src/main/scala/scalastream/J8SStream.scala diff --git a/scala-stream/src/main/scala/scalastream/ParStream.scala b/src/scala/scala-stream/src/main/scala/scalastream/ParStream.scala similarity index 100% rename from scala-stream/src/main/scala/scalastream/ParStream.scala rename to src/scala/scala-stream/src/main/scala/scalastream/ParStream.scala diff --git a/scala-stream/src/main/scala/scalastream/PlainStream.scala b/src/scala/scala-stream/src/main/scala/scalastream/PlainStream.scala similarity index 100% rename from scala-stream/src/main/scala/scalastream/PlainStream.scala rename to src/scala/scala-stream/src/main/scala/scalastream/PlainStream.scala diff --git a/scala-stream/src/main/scala/scalastream/ScalaStream.scala b/src/scala/scala-stream/src/main/scala/scalastream/ScalaStream.scala similarity index 100% rename from scala-stream/src/main/scala/scalastream/ScalaStream.scala rename to src/scala/scala-stream/src/main/scala/scalastream/ScalaStream.scala diff --git a/scala-stream/src/main/scala/scalastream/ThreadStream.scala b/src/scala/scala-stream/src/main/scala/scalastream/ThreadStream.scala similarity index 100% rename from scala-stream/src/main/scala/scalastream/ThreadStream.scala rename to src/scala/scala-stream/src/main/scala/scalastream/ThreadStream.scala diff --git a/src/std/Makefile b/src/std/Makefile deleted file mode 100644 index a5a8847..0000000 --- a/src/std/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. -# -# For full license terms please see the LICENSE file distributed with this -# source code - -CXXFLAGS=-O3 -std=c++17 -stdpar -DSTD -STD_CXX=nvc++ - -std-stream: ../main.cpp STDStream.cpp - $(STD_CXX) $(CXXFLAGS) $^ $(EXTRA_FLAGS) -o $@ -I. -I.. - -.PHONY: clean -clean: - rm -f std-stream diff --git a/src/std20/Makefile b/src/std20/Makefile deleted file mode 100644 index 3a93bcb..0000000 --- a/src/std20/Makefile +++ /dev/null @@ -1,26 +0,0 @@ - -ifndef COMPILER -define compiler_help -Set COMPILER to change flags (defaulting to GNU). -Available compilers are: - GNU - -endef -$(info $(compiler_help)) -COMPILER=GNU -endif - -COMPILER_GNU = g++ -CXX = $(COMPILER_$(COMPILER)) - -FLAGS_GNU = -O3 -std=c++2a -march=native -CXXFLAGS = $(FLAGS_$(COMPILER)) - - -std20-stream: ../main.cpp STD20Stream.cpp - $(CXX) -DSTD20 $(CXXFLAGS) $^ $(EXTRA_FLAGS) -o $@ -I. -I.. - -.PHONY: clean -clean: - rm -f std20-stream - diff --git a/src/sycl/Makefile b/src/sycl/Makefile deleted file mode 100644 index 05d2022..0000000 --- a/src/sycl/Makefile +++ /dev/null @@ -1,81 +0,0 @@ -ifndef COMPILER -define compiler_help -Set COMPILER to change flags (defaulting to GNU). -Available compilers are: - HIPSYCL, DPCPP, COMPUTECPP - - - For HIPSYCL and COMPUTECPP, SYCL_SDK_DIR must be specified, the directory should contain [/lib, /bin, ...] - For DPCPP, the compiler must be on path -endef -$(info $(compiler_help)) -COMPILER=HIPSYCL -endif - -ifndef TARGET -define target_help -Set TARGET to change device (defaulting to CPU). -Available targets are: - CPU AMD NVIDIA - -endef -$(info $(target_help)) -TARGET=CPU -endif - - -ifndef ARCH -define arch_help -Set ARCH to change device (defaulting to ""). -(GPU *only*) Available targets for HIPSYCL are: - For CUDA, the architecture has the form sm_XX, e.g. sm_60 for Pascal. - For ROCm, the architecture has the form gfxYYY, e.g. gfx900 for Vega 10, gfx906 for Vega 20. - -endef - -ifeq ($(COMPILER), HIPSYCL) -ifneq ($(TARGET), CPU) -$(info $(arch_help)) -ARCH= -endif -endif - -endif - -SYCL_COMPUTECPP_SYCLFLAGS = $(shell $(SYCL_SDK_DIR)/bin/computecpp_info --dump-device-compiler-flags) -no-serial-memop -sycl-driver -SYCL_COMPUTECPP_SYCLFLAGS_CPU = $(SYCL_COMPUTECPP_SYCLFLAGS) -SYCL_COMPUTECPP_SYCLFLAGS_AMD = $(SYCL_COMPUTECPP_SYCLFLAGS) -SYCL_COMPUTECPP_SYCLFLAGS_NVIDIA = $(SYCL_COMPUTECPP_SYCLFLAGS) -sycl-target ptx64 -SYCL_COMPUTECPP_SYCLCXX = $(SYCL_SDK_DIR)/bin/compute++ -SYCL_COMPUTECPP_FLAGS = -O3 -std=c++17 -SYCL_COMPUTECPP_LINK_FLAGS = -Wl,-rpath=$(SYCL_SDK_DIR)/lib/ $(SYCL_SDK_DIR)/lib/libComputeCpp.so -lOpenCL -SYCL_COMPUTECPP_INCLUDE = -I$(SYCL_SDK_DIR)/include - -SYCL_HIPSYCL_SYCLFLAGS_CPU = --hipsycl-platform=cpu -SYCL_HIPSYCL_SYCLFLAGS_AMD = --hipsycl-platform=rocm --hipsycl-gpu-arch=$(ARCH) -SYCL_HIPSYCL_SYCLFLAGS_NVIDIA = --hipsycl-platform=cuda --hipsycl-gpu-arch=$(ARCH) -SYCL_HIPSYCL_SYCLCXX = $(SYCL_SDK_DIR)/bin/syclcc -SYCL_HIPSYCL_FLAGS = -O3 --std=c++17 -SYCL_HIPSYCL_LINK_FLAGS = -L$(SYCL_SDK_DIR)/lib -Wl,-rpath,$(SYCL_SDK_DIR)/lib -SYCL_HIPSYCL_INCLUDE = - -SYCL_DPCPP_SYCLFLAGS_NVIDIA = -fsycl -fsycl-targets=nvptx64-nvidia-cuda-sycldevice -fsycl-unnamed-lambda -SYCL_DPCPP_SYCLCXX = dpcpp -SYCL_DPCPP_FLAGS = -O3 --std=c++17 -SYCL_DPCPP_LINK_FLAGS = -SYCL_DPCPP_INCLUDE = - - -SYCL_SYCLFLAGS = $(SYCL_$(COMPILER)_SYCLFLAGS_$(TARGET)) -SYCL_SYCLCXX = $(SYCL_$(COMPILER)_SYCLCXX) -SYCL_FLAGS = $(SYCL_$(COMPILER)_FLAGS) -SYCL_LINK_FLAGS = $(SYCL_$(COMPILER)_LINK_FLAGS) -SYCL_INCLUDE = $(SYCL_$(COMPILER)_INCLUDE) - -# only ComputeCpp generates .sycl files which is a bit odd to deal with so we opted to compile everything together -sycl-stream: ../main.cpp SYCLStream.cpp - $(SYCL_SYCLCXX) $(SYCL_SYCLFLAGS) $(SYCL_FLAGS) $(SYCL_INCLUDE) -DSYCL $(EXTRA_FLAGS) $(SYCL_LINK_FLAGS) $^ -o $@ -I. -I.. - -.PHONY: clean -clean: - rm -f sycl-stream diff --git a/TBB.cmake b/src/tbb/TBB.cmake similarity index 100% rename from TBB.cmake rename to src/tbb/TBB.cmake diff --git a/TBBStream.cpp b/src/tbb/TBBStream.cpp similarity index 100% rename from TBBStream.cpp rename to src/tbb/TBBStream.cpp diff --git a/TBBStream.hpp b/src/tbb/TBBStream.hpp similarity index 100% rename from TBBStream.hpp rename to src/tbb/TBBStream.hpp diff --git a/THRUST.cmake b/src/thrust/THRUST.cmake similarity index 100% rename from THRUST.cmake rename to src/thrust/THRUST.cmake diff --git a/ThrustStream.cu b/src/thrust/ThrustStream.cu similarity index 100% rename from ThrustStream.cu rename to src/thrust/ThrustStream.cu diff --git a/ThrustStream.h b/src/thrust/ThrustStream.h similarity index 100% rename from ThrustStream.h rename to src/thrust/ThrustStream.h From 565c8c7f95a57835f4900d0d257a72688efb6236 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Tue, 30 Nov 2021 19:03:04 +0000 Subject: [PATCH 59/78] Fix cache path --- .github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 423064a..0d6b736 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -59,7 +59,7 @@ jobs: uses: actions/cache@v2 with: path: compilers - key: ${{ runner.os }}-${{ hashFiles('./src/ci-prepare-bionic.sh') }} + key: ${{ runner.os }}-${{ hashFiles('./ci-prepare-bionic.sh') }} - name: Prepare compilers if: steps.prepare-compilers.outputs.cache-hit != 'true' From 53f3b7b8a4be7481502916b6da319a83220ea923 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Wed, 1 Dec 2021 16:19:50 +0000 Subject: [PATCH 60/78] Fetch CL headers on-demand for ComputeCpp and OCL build Minor CI adjustments for act Fix TBB and Thrust cmake builds --- .github/workflows/main.yaml | 1 + src/CMakeLists.txt | 18 ++++++++++++++++-- src/ci-prepare-bionic.sh | 24 +++++++++++++++--------- src/ocl/model.cmake | 3 +-- src/sycl/SYCLStream.h | 10 ---------- src/sycl/model.cmake | 3 +-- src/tbb/{TBB.cmake => model.cmake} | 0 src/thrust/{THRUST.cmake => model.cmake} | 0 8 files changed, 34 insertions(+), 25 deletions(-) rename src/tbb/{TBB.cmake => model.cmake} (100%) rename src/thrust/{THRUST.cmake => model.cmake} (100%) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 0d6b736..9b7489b 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -55,6 +55,7 @@ jobs: - uses: actions/checkout@v2 - name: Cache compiler + if: ${{ !env.ACT }} id: prepare-compilers uses: actions/cache@v2 with: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 81fa78b..760bbd6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,7 +7,21 @@ project(BabelStream VERSION 3.5 LANGUAGES CXX) # some nicer defaults for standard C++ set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD_REQUIRED ON) +include(FetchContent) +FetchContent_Declare( + opencl_header + URL https://github.com/KhronosGroup/OpenCL-Headers/archive/refs/tags/v2021.06.30.zip + URL_HASH MD5=af7ab7918a6a11c60370c8651a9f0192 +) + +macro(setup_opencl_header_includes) + FetchContent_GetProperties(opencl_header) + if (NOT opencl_header_POPULATED) + FetchContent_Populate(opencl_header) + set(OpenCL_INCLUDE_DIR ${opencl_header_SOURCE_DIR}) + endif () +endmacro() #set(MODEL SYCL) #set(SYCL_COMPILER COMPUTECPP) @@ -149,7 +163,7 @@ message(STATUS "Default ${CMAKE_BUILD_TYPE} flags are `${DEFAULT_${BUILD_TYPE}_F # setup common build flag defaults if there are no overrides if (NOT DEFINED ${BUILD_TYPE}_FLAGS) set(ACTUAL_${BUILD_TYPE}_FLAGS ${DEFAULT_${BUILD_TYPE}_FLAGS}) - elseif() +elseif () set(ACTUAL_${BUILD_TYPE}_FLAGS ${${BUILD_TYPE}_FLAGS}) endif () @@ -192,4 +206,4 @@ if (COMMAND setup_target) setup_target(${EXE_NAME}) endif () -install (TARGETS ${EXE_NAME} DESTINATION bin) \ No newline at end of file +install(TARGETS ${EXE_NAME} DESTINATION bin) \ No newline at end of file diff --git a/src/ci-prepare-bionic.sh b/src/ci-prepare-bionic.sh index 7294905..b77f404 100755 --- a/src/ci-prepare-bionic.sh +++ b/src/ci-prepare-bionic.sh @@ -225,10 +225,7 @@ setup_tbb() { setup_clang_gcc() { - echo "deb http://archive.ubuntu.com/ubuntu focal main universe" | sudo tee -a /etc/apt/sources.list - - sudo apt-get update -qq - sudo apt-get install -y -qq gcc-10-offload-nvptx gcc-10-offload-amdgcn libtbb2 libtbb-dev g++-10 + sudo apt-get install -y -qq gcc-10-offload-nvptx gcc-10-offload-amdgcn libtbb2 libtbb-dev g++-10 clang export_var GCC_CXX "$(which g++-10)" verify_bin_exists "$GCC_CXX" @@ -251,9 +248,6 @@ setup_clang_gcc() { } setup_rocm() { - wget -q -O - "https://repo.radeon.com/rocm/rocm.gpg.key" | sudo apt-key add - - echo 'deb [arch=amd64] https://repo.radeon.com/rocm/apt/4.5 ubuntu main' | sudo tee /etc/apt/sources.list.d/rocm.list - sudo apt-get update -qq sudo apt-get install -y -qq rocm-dev rocthrust-dev export_var ROCM_PATH "/opt/rocm" export_var PATH "$ROCM_PATH/bin:$PATH" # ROCm needs this for many of their libraries' CMake build to work @@ -320,9 +314,21 @@ if [ "${GITHUB_ACTIONS:-false}" = true ]; then echo "Running in GitHub Actions, defaulting to special export" TERM=xterm export TERM=xterm + + # drop the lock in case we got one from a failed run + rm /var/lib/dpkg/lock-frontend || true + rm /var/cache/apt/archives/lock || true + + wget -q -O - "https://repo.radeon.com/rocm/rocm.gpg.key" | sudo apt-key add - + echo "deb http://archive.ubuntu.com/ubuntu focal main universe" | sudo tee -a /etc/apt/sources.list + echo 'deb [arch=amd64] https://repo.radeon.com/rocm/apt/4.5 ubuntu main' | sudo tee /etc/apt/sources.list.d/rocm.list + + sudo apt-get update -qq + sudo apt-get install -y -qq cmake + if [ "$SETUP" = true ]; then - echo "Deleting extra packages for space in 5 seconds..." - sleep 5 + echo "Deleting extra packages for space in 2 seconds..." + sleep 2 echo "Starting apt-get remove:" sudo apt-get remove -y azure-cli google-cloud-sdk hhvm google-chrome-stable firefox powershell mono-devel sudo apt-get autoremove -y diff --git a/src/ocl/model.cmake b/src/ocl/model.cmake index 2be3981..acefe71 100644 --- a/src/ocl/model.cmake +++ b/src/ocl/model.cmake @@ -9,8 +9,7 @@ register_flag_optional(OpenCL_LIBRARY macro(setup) - # don't point to the CL dir as the imports already have the CL prefix - set(OpenCL_INCLUDE_DIR "${CMAKE_SOURCE_DIR}") + setup_opencl_header_includes() find_package(OpenCL REQUIRED) register_link_library(OpenCL::OpenCL) endmacro() diff --git a/src/sycl/SYCLStream.h b/src/sycl/SYCLStream.h index dd13387..d3fa18d 100644 --- a/src/sycl/SYCLStream.h +++ b/src/sycl/SYCLStream.h @@ -10,16 +10,6 @@ #include #include "Stream.h" - -#include "CL/opencl.h" - -// XXX Intel's SYCL impl. needs CL_MEM_CHANNEL_INTEL which is provided in dpcpp's include dir -// however, depending the system configuration, the system CL header sometimes takes precedence -// we only really need this macro to refer to the extension so this is probably OK -#ifndef CL_MEM_CHANNEL_INTEL -#define CL_MEM_CHANNEL_INTEL 0x4213 -#endif - #include "CL/sycl.hpp" #define IMPLEMENTATION_STRING "SYCL" diff --git a/src/sycl/model.cmake b/src/sycl/model.cmake index c0c6c3f..e7b5a1c 100644 --- a/src/sycl/model.cmake +++ b/src/sycl/model.cmake @@ -47,8 +47,7 @@ macro(setup) list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) set(ComputeCpp_DIR ${SYCL_COMPILER_DIR}) - # don't point to the CL dir as the imports already have the CL prefix - set(OpenCL_INCLUDE_DIR "${CMAKE_SOURCE_DIR}") + setup_opencl_header_includes() register_definitions(CL_TARGET_OPENCL_VERSION=220 _GLIBCXX_USE_CXX11_ABI=0) # ComputeCpp needs OpenCL diff --git a/src/tbb/TBB.cmake b/src/tbb/model.cmake similarity index 100% rename from src/tbb/TBB.cmake rename to src/tbb/model.cmake diff --git a/src/thrust/THRUST.cmake b/src/thrust/model.cmake similarity index 100% rename from src/thrust/THRUST.cmake rename to src/thrust/model.cmake From 7bf0b212d9bf18c6e236feebd533afc98859c5af Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Wed, 1 Dec 2021 16:40:05 +0000 Subject: [PATCH 61/78] Fix CI missing omp library Fix thrust model typo --- src/CMakeLists.txt | 2 +- src/ci-prepare-bionic.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 760bbd6..553bfcb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -127,7 +127,7 @@ register_model(acc ACC ACCStream.cpp) # defining RAJA collides with the RAJA namespace so USE_RAJA register_model(raja USE_RAJA RAJAStream.cpp) register_model(tbb TBB TBBStream.cpp) -register_model(thurst THRUST ThrustStream.cu) # Thrust uses cu, even for rocThrust +register_model(thrust THRUST ThrustStream.cu) # Thrust uses cu, even for rocThrust set(USAGE ON CACHE BOOL "Whether to print all custom flags for the selected model") diff --git a/src/ci-prepare-bionic.sh b/src/ci-prepare-bionic.sh index b77f404..656d338 100755 --- a/src/ci-prepare-bionic.sh +++ b/src/ci-prepare-bionic.sh @@ -225,7 +225,7 @@ setup_tbb() { setup_clang_gcc() { - sudo apt-get install -y -qq gcc-10-offload-nvptx gcc-10-offload-amdgcn libtbb2 libtbb-dev g++-10 clang + sudo apt-get install -y -qq gcc-10-offload-nvptx gcc-10-offload-amdgcn libtbb2 libtbb-dev g++-10 clang libomp-dev export_var GCC_CXX "$(which g++-10)" verify_bin_exists "$GCC_CXX" From ed960d88a36a5d22eaa78b0df1430a76adddf1d4 Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Thu, 2 Dec 2021 10:41:55 +0000 Subject: [PATCH 62/78] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 976964a..1530392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ All notable changes to this project will be documented in this file. - Added nstream kernel from PRK with associate command line option. - CMake build system added for all models. - SYCL device check for FP64 support. -- New implementation using TBB. +- New implementations: TBB, Thrust, Julia, Scala, Java. - Compiler options for Fujitsu added to OpenMP. ### Changed @@ -39,6 +39,7 @@ All notable changes to this project will be documented in this file. Pre-building kernels is also not required, and shows no overhead as the first iteration is not timed. - OpenACC Cray compiler flags. - Build support for Kokkos 2.x (No code changes made). +- All Makefiles; build system will now use CMake exclusively. ## [v3.4] - 2019-04-10 From c378f931786c4f929a6e6e0e1f6a3f53b6d83283 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 2 Dec 2021 18:32:58 +0000 Subject: [PATCH 63/78] Fix action cache key --- .github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 9b7489b..c2bcdc9 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -60,7 +60,7 @@ jobs: uses: actions/cache@v2 with: path: compilers - key: ${{ runner.os }}-${{ hashFiles('./ci-prepare-bionic.sh') }} + key: ${{ runner.os }}-${{ hashFiles('./src/ci-prepare-bionic.sh') }} - name: Prepare compilers if: steps.prepare-compilers.outputs.cache-hit != 'true' From c4ee2af23a8ae0625548d5b8f5e5b283a7f71f69 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 2 Dec 2021 23:21:31 +0000 Subject: [PATCH 64/78] Add CI badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d36da1a..5275314 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ BabelStream logo +[![CI](https://github.com/UoB-HPC/BabelStream/actions/workflows/main.yaml/badge.svg?branch=main)](https://github.com/UoB-HPC/BabelStream/actions/workflows/main.yaml) Measure memory transfer rates to/from global device memory on GPUs. This benchmark is similar in spirit, and based on, the STREAM benchmark [1] for CPUs. From 949071b4b964d06044db1c1d1a893e2ac527629b Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Fri, 3 Dec 2021 12:20:40 +0000 Subject: [PATCH 65/78] Add CI debugging --- .github/workflows/main.yaml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index b44197f..8fe064b 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -1,6 +1,13 @@ name: CI -on: [push, pull_request] - +on: + push: + pull_request: + workflow_dispatch: + inputs: + debug_enabled: + description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' + required: false + default: false jobs: @@ -65,6 +72,11 @@ jobs: - name: Setup test environment run: source ./ci-prepare-bionic.sh ./compilers VARS false || true + # Enable tmate debugging of manually-triggered workflows if the input option was provided + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }} + - name: Test compile gcc @ CMake 3.13 if: ${{ ! cancelled() }} run: ./ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_13_BIN }} From 365d1a3601843a9bb727dc0809af0ffd0a843a3e Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Fri, 3 Dec 2021 12:25:57 +0000 Subject: [PATCH 66/78] Fix CI cache path --- .github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index f1ac55b..f0c6c6c 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -66,7 +66,7 @@ jobs: id: prepare-compilers uses: actions/cache@v2 with: - path: compilers + path: ./src/compilers key: ${{ runner.os }}-${{ hashFiles('./src/ci-prepare-bionic.sh') }} - name: Prepare compilers From 65b8b51b35d57a844d78414f204810e5843ada62 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Fri, 3 Dec 2021 13:26:09 +0000 Subject: [PATCH 67/78] Move CMakeList.txt to top level --- .github/workflows/main.yaml | 51 +++++++++---------- src/CMakeLists.txt => CMakeLists.txt | 8 +-- README.md | 6 +-- .../Modules/ComputeCppCompilerChecks.cmake | 0 .../Modules/ComputeCppIRMap.cmake | 0 .../Modules/FindComputeCpp.cmake | 0 {src => cmake}/register_models.cmake | 6 +-- .../toolchains/arm-gcc-poky.cmake | 0 .../toolchains/gcc-generic.cmake | 0 src/ci-test-compile.sh | 30 +++++------ 10 files changed, 49 insertions(+), 52 deletions(-) rename src/CMakeLists.txt => CMakeLists.txt (97%) rename {src/cmake => cmake}/Modules/ComputeCppCompilerChecks.cmake (100%) rename {src/cmake => cmake}/Modules/ComputeCppIRMap.cmake (100%) rename {src/cmake => cmake}/Modules/FindComputeCpp.cmake (100%) rename {src => cmake}/register_models.cmake (96%) rename {src/cmake => cmake}/toolchains/arm-gcc-poky.cmake (100%) rename {src/cmake => cmake}/toolchains/gcc-generic.cmake (100%) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index f0c6c6c..3620d48 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -55,9 +55,6 @@ jobs: test-cpp: runs-on: ubuntu-18.04 - defaults: - run: - working-directory: ./src steps: - uses: actions/checkout@v2 @@ -83,75 +80,75 @@ jobs: - name: Test compile gcc @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_13_BIN }} + run: ./src/ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_13_BIN }} - name: Test compile clang @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_13_BIN }} + run: ./src/ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_13_BIN }} - name: Test compile nvhpc @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_13_BIN }} + run: ./src/ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_13_BIN }} - name: Test compile aocc @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_13_BIN }} + run: ./src/ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_13_BIN }} - name: Test compile aomp @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_13_BIN }} + run: ./src/ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_13_BIN }} - name: Test compile hip @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_13_BIN }} + run: ./src/ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_13_BIN }} - name: Test compile dpcpp @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_13_BIN }} + run: ./src/ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_13_BIN }} - name: Test compile hipsycl @ CMake 3.13 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_13_BIN }} + run: ./src/ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_13_BIN }} - name: Test compile gcc @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_15_BIN }} + run: ./src/ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_15_BIN }} - name: Test compile clang @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_15_BIN }} + run: ./src/ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_15_BIN }} - name: Test compile nvhpc @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_15_BIN }} + run: ./src/ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_15_BIN }} - name: Test compile aocc @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_15_BIN }} + run: ./src/ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_15_BIN }} - name: Test compile aomp @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_15_BIN }} + run: ./src/ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_15_BIN }} - name: Test compile hip @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_15_BIN }} + run: ./src/ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_15_BIN }} - name: Test compile dpcpp @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_15_BIN }} + run: ./src/ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_15_BIN }} - name: Test compile hipsycl @ CMake 3.15 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_15_BIN }} + run: ./src/ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_15_BIN }} - name: Test compile gcc @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_18_BIN }} + run: ./src/ci-test-compile.sh ./build gcc all ${{ env.CMAKE_3_18_BIN }} - name: Test compile clang @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_18_BIN }} + run: ./src/ci-test-compile.sh ./build clang all ${{ env.CMAKE_3_18_BIN }} - name: Test compile nvhpc @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_18_BIN }} + run: ./src/ci-test-compile.sh ./build nvhpc all ${{ env.CMAKE_3_18_BIN }} - name: Test compile aocc @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_18_BIN }} + run: ./src/ci-test-compile.sh ./build aocc all ${{ env.CMAKE_3_18_BIN }} - name: Test compile aomp @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_18_BIN }} + run: ./src/ci-test-compile.sh ./build aomp all ${{ env.CMAKE_3_18_BIN }} - name: Test compile hip @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_18_BIN }} + run: ./src/ci-test-compile.sh ./build hip all ${{ env.CMAKE_3_18_BIN }} - name: Test compile dpcpp @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_18_BIN }} + run: ./src/ci-test-compile.sh ./build dpcpp all ${{ env.CMAKE_3_18_BIN }} - name: Test compile hipsycl @ CMake 3.18 if: ${{ ! cancelled() }} - run: ./ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_18_BIN }} \ No newline at end of file + run: ./src/ci-test-compile.sh ./build hipsycl all ${{ env.CMAKE_3_18_BIN }} \ No newline at end of file diff --git a/src/CMakeLists.txt b/CMakeLists.txt similarity index 97% rename from src/CMakeLists.txt rename to CMakeLists.txt index 553bfcb..58e0a3b 100644 --- a/src/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,7 +49,7 @@ endmacro() #set(CXX_EXTRA_FLAGS -O2) #set(CMAKE_CXX_COMPILER /usr/lib/aomp/bin/clang++) -#set(MODEL OMP) +#set(MODEL omp) ##set(OFFLOAD "AMD:gfx803") #set(OFFLOAD "NVIDIA:sm_35") #set(CXX_EXTRA_FLAGS --cuda-path=/opt/cuda-10.2/) @@ -112,7 +112,7 @@ if ((DEFINED CXX_EXTRA_FLAGS) AND (NOT DEFINED CXX_EXTRA_LINK_FLAGS)) endif () # include our macros -include(register_models.cmake) +include(cmake/register_models.cmake) # register out models register_model(omp OMP OMPStream.cpp) @@ -185,8 +185,8 @@ message(STATUS "Executable : ${EXE_NAME}") # below we have all the usual CMake target setup steps -include_directories(.) -add_executable(${EXE_NAME} ${IMPL_SOURCES} main.cpp) +include_directories(src) +add_executable(${EXE_NAME} ${IMPL_SOURCES} src/main.cpp) target_link_libraries(${EXE_NAME} PUBLIC ${LINK_LIBRARIES}) target_compile_definitions(${EXE_NAME} PUBLIC ${IMPL_DEFINITIONS}) diff --git a/README.md b/README.md index 5275314..fbee03f 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ The project supports building with CMake >= 3.13.0, it can be installed without As with any CMake project, first configure the project: ```shell -> cd babelstream/src +> cd babelstream > cmake -Bbuild -H. -DMODEL= # configure the build, build type defaults to Release > cmake --build build # compile it > ./build/-stream # executable available at ./build/ @@ -85,7 +85,7 @@ There are assigned those to `RELEASE_FLAGS`, and you can override them if requir To find out what flag each model supports or requires, simply configure while only specifying the model. For example: ```shell -> cd babelstream/src +> cd babelstream > cmake -Bbuild -H. -DMODEL=ocl ... - Common Release flags are `-O3`, set RELEASE_FLAGS to override @@ -108,7 +108,7 @@ For example: OpenCL_LIBRARY (optional, default=): Path to OpenCL library, usually called libOpenCL.so ... ``` -Alternatively, refer to the [CI script](./ci-test-compile.sh), which test-compiles most of the models, and see which flags are used there. +Alternatively, refer to the [CI script](./src/ci-test-compile.sh), which test-compiles most of the models, and see which flags are used there. *It is recommended that you delete the `build` directory when you change any of the build flags.* diff --git a/src/cmake/Modules/ComputeCppCompilerChecks.cmake b/cmake/Modules/ComputeCppCompilerChecks.cmake similarity index 100% rename from src/cmake/Modules/ComputeCppCompilerChecks.cmake rename to cmake/Modules/ComputeCppCompilerChecks.cmake diff --git a/src/cmake/Modules/ComputeCppIRMap.cmake b/cmake/Modules/ComputeCppIRMap.cmake similarity index 100% rename from src/cmake/Modules/ComputeCppIRMap.cmake rename to cmake/Modules/ComputeCppIRMap.cmake diff --git a/src/cmake/Modules/FindComputeCpp.cmake b/cmake/Modules/FindComputeCpp.cmake similarity index 100% rename from src/cmake/Modules/FindComputeCpp.cmake rename to cmake/Modules/FindComputeCpp.cmake diff --git a/src/register_models.cmake b/cmake/register_models.cmake similarity index 96% rename from src/register_models.cmake rename to cmake/register_models.cmake index 12e7a3d..f180c03 100644 --- a/src/register_models.cmake +++ b/cmake/register_models.cmake @@ -134,7 +134,7 @@ macro(register_model NAME PREPROCESSOR_NAME) list(APPEND REGISTERED_MODELS "${NAME}") string(TOUPPER ${NAME} MODEL_UPPER) - list(APPEND IMPL_${MODEL_UPPER}_SOURCES "${NAME}/${ARGN}") + list(APPEND IMPL_${MODEL_UPPER}_SOURCES "src/${NAME}/${ARGN}") list(APPEND IMPL_${MODEL_UPPER}_DEFINITIONS "${PREPROCESSOR_NAME}") endmacro() @@ -142,8 +142,8 @@ endmacro() macro(load_model MODEL) if ("${MODEL}" IN_LIST REGISTERED_MODELS) string(TOLOWER "${MODEL}" MODEL_LOWER) - set(MODEL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${MODEL_LOWER}/model.cmake) - include_directories(${CMAKE_CURRENT_SOURCE_DIR}/${MODEL_LOWER}) + set(MODEL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/${MODEL_LOWER}/model.cmake) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/${MODEL_LOWER}) if (NOT EXISTS ${MODEL_FILE}) message(FATAL_ERROR "${MODEL_FILE} not found, perhaps it needs to be implemented?") endif () diff --git a/src/cmake/toolchains/arm-gcc-poky.cmake b/cmake/toolchains/arm-gcc-poky.cmake similarity index 100% rename from src/cmake/toolchains/arm-gcc-poky.cmake rename to cmake/toolchains/arm-gcc-poky.cmake diff --git a/src/cmake/toolchains/gcc-generic.cmake b/cmake/toolchains/gcc-generic.cmake similarity index 100% rename from src/cmake/toolchains/gcc-generic.cmake rename to cmake/toolchains/gcc-generic.cmake diff --git a/src/ci-test-compile.sh b/src/ci-test-compile.sh index 70303b3..3f54aaf 100755 --- a/src/ci-test-compile.sh +++ b/src/ci-test-compile.sh @@ -88,30 +88,30 @@ run_build() { ### # KOKKOS_SRC="/home/tom/Downloads/kokkos-3.3.00" # RAJA_SRC="/home/tom/Downloads/RAJA-v0.13.0" - -# GCC_CXX="/usr/bin/g++" +# +# GCC_CXX="$(which g++-10)" # CLANG_CXX="/usr/bin/clang++" - -# NVHPC_SDK_DIR="/home/tom/Downloads/nvhpc_2021_212_Linux_x86_64_cuda_11.2/install_components/Linux_x86_64/21.2/" +# +# NVHPC_SDK_DIR="/home/tom/Downloads/nvhpc_2021_219_Linux_x86_64_cuda_multi/install_components/Linux_x86_64/21.9/" # NVHPC_NVCXX="$NVHPC_SDK_DIR/compilers/bin/nvc++" -# NVHPC_NVCC="$NVHPC_SDK_DIR/cuda/11.2/bin/nvcc" -# NVHPC_CUDA_DIR="$NVHPC_SDK_DIR/cuda/11.2" +# NVHPC_NVCC="$NVHPC_SDK_DIR/cuda/11.4/bin/nvcc" +# NVHPC_CUDA_DIR="$NVHPC_SDK_DIR/cuda/11.4" # "$NVHPC_SDK_DIR/compilers/bin/makelocalrc" "$NVHPC_SDK_DIR/compilers/bin/" -x - +# # AOCC_CXX="/opt/AMD/aocc-compiler-2.3.0/bin/clang++" # AOMP_CXX="/usr/lib/aomp/bin/clang++" # OCL_LIB="/home/tom/Downloads/oclcpuexp-2020.11.11.0.04_rel/x64/libOpenCL.so" - +# # # AMD needs this rocm_path thing exported... -# export ROCM_PATH="/opt/rocm-4.0.0" -# HIP_CXX="/opt/rocm-4.0.0/bin/hipcc" -# COMPUTECPP_DIR="/home/tom/Desktop/computecpp_archive/ComputeCpp-CE-2.3.0-x86_64-linux-gnu" +# export ROCM_PATH="/opt/rocm-4.5.0" +# HIP_CXX="/opt/rocm-4.5.0/bin/hipcc" +# COMPUTECPP_DIR="/home/tom/Downloads/ComputeCpp-CE-2.7.0-x86_64-linux-gnu/" # DPCPP_DIR="/home/tom/Downloads/dpcpp_compiler" # HIPSYCL_DIR="/opt/hipsycl/cff515c/" - -# ICPX_CXX="/opt/intel/oneapi/compiler/2021.1.2/linux/bin/icpx" -# ICPC_CXX="/opt/intel/oneapi/compiler/2021.1.2/linux/bin/intel64/icpc"# TBB_LIB="/home/tom/Downloads/oneapi-tbb-2021.1.1/" - +# +# ICPX_CXX="/opt/intel/oneapi/compiler/2021.4.0/linux/bin/icpx" +# ICPC_CXX="/opt/intel/oneapi/compiler/2021.4.0/linux/bin/intel64/icpc"# TBB_LIB="/home/tom/Downloads/oneapi-tbb-2021.1.1/" +# # GCC_STD_PAR_LIB="tbb" # CLANG_STD_PAR_LIB="tbb" # GCC_OMP_OFFLOAD_AMD=false From 8fc345793285f14cde9ccde942386ca42394a0fa Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Fri, 3 Dec 2021 13:31:42 +0000 Subject: [PATCH 68/78] Fix CI paths, again --- .github/workflows/main.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 3620d48..c0c0353 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -63,15 +63,15 @@ jobs: id: prepare-compilers uses: actions/cache@v2 with: - path: ./src/compilers + path: ./compilers key: ${{ runner.os }}-${{ hashFiles('./src/ci-prepare-bionic.sh') }} - name: Prepare compilers if: steps.prepare-compilers.outputs.cache-hit != 'true' - run: source ./ci-prepare-bionic.sh ./compilers SETUP true || true + run: source ./src/ci-prepare-bionic.sh ./compilers SETUP true || true - name: Setup test environment - run: source ./ci-prepare-bionic.sh ./compilers VARS false || true + run: source ./src/ci-prepare-bionic.sh ./compilers VARS false || true # Enable tmate debugging of manually-triggered workflows if the input option was provided - name: Setup tmate session From c61b93dc65f6726aa0d6c468e41adfb45423615b Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Mon, 6 Dec 2021 15:55:52 +0000 Subject: [PATCH 69/78] Add unsafe and Arc implementation Add BABELSTREAM_NUM_THREADS env option Refactor driver Bump dependencies --- rust-stream/Cargo.lock | 72 +++---- rust-stream/README.md | 8 +- rust-stream/rustfmt.toml | 2 +- rust-stream/src/arc_stream.rs | 254 ++++++++++++++++++++++++ rust-stream/src/crossbeam_stream.rs | 14 +- rust-stream/src/lib.rs | 212 ++++++++++---------- rust-stream/src/stream.rs | 1 + rust-stream/src/unsafe_stream.rs | 266 ++++++++++++++++++++++++++ rust-stream/tests/integration_test.rs | 2 +- 9 files changed, 681 insertions(+), 150 deletions(-) create mode 100644 rust-stream/src/arc_stream.rs create mode 100644 rust-stream/src/unsafe_stream.rs diff --git a/rust-stream/Cargo.lock b/rust-stream/Cargo.lock index 7c5ec13..5f225f0 100644 --- a/rust-stream/Cargo.lock +++ b/rust-stream/Cargo.lock @@ -30,9 +30,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cfg-if" @@ -102,9 +102,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -186,18 +186,18 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "instant" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] @@ -220,15 +220,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.97" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" +checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" [[package]] name = "lock_api" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" dependencies = [ "scopeguard", ] @@ -253,9 +253,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.7.11" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" dependencies = [ "libc", "log", @@ -303,9 +303,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", @@ -314,9 +314,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ "cfg-if", "instant", @@ -361,18 +361,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.27" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" dependencies = [ "proc-macro2", ] @@ -404,9 +404,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" dependencies = [ "bitflags", ] @@ -502,9 +502,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" [[package]] name = "strsim" @@ -514,9 +514,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.21" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c" dependencies = [ "clap", "lazy_static", @@ -525,9 +525,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.14" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck", "proc-macro-error", @@ -538,9 +538,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.73" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" dependencies = [ "proc-macro2", "quote", @@ -573,15 +573,15 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" [[package]] name = "unicode-segmentation" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" [[package]] name = "unicode-width" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" diff --git a/rust-stream/README.md b/rust-stream/README.md index ee9c056..6696de5 100644 --- a/rust-stream/README.md +++ b/rust-stream/README.md @@ -10,15 +10,21 @@ Currently, we support three CPU threading API as devices: see [rayon_stream.rs](src/rayon_stream.rs) * [Crossbeam](https://github.com/crossbeam-rs/crossbeam) - Parallel with partitions per thread, see [crossbeam_stream.rs](src/crossbeam_stream.rs) +* Arc - Parallel with `Vec` per thread (static partitions) wrapped in `Mutex` contained in `Arc`s, + see [crossbeam_stream.rs](src/arc_stream.rs) +* Unsafe - Parallel with unsafe pointer per thread (static partitions) to `Vec`, + see [crossbeam_stream.rs](src/unsafe_stream.rs) In addition, this implementation also supports the following extra flags: - +**** ``` --init Initialise each benchmark array at allocation time on the main thread --malloc Use libc malloc instead of the Rust's allocator for benchmark array allocation --pin Pin threads to distinct cores, this has NO effect in Rayon devices ``` +Max thread count is controlled by the environment variable `BABELSTREAM_NUM_THREADS` which is compatible for all devices (avoid setting `RAYON_NUM_THREADS`, the implementation will issue a warning if this happened). + There is an ongoing investigation on potential performance issues under NUMA situations. As part of the experiment, this implementation made use of the provisional [Allocator traits](https://github.com/rust-lang/rust/issues/32838) which requires rust diff --git a/rust-stream/rustfmt.toml b/rust-stream/rustfmt.toml index 23c912d..aa2f0e9 100644 --- a/rust-stream/rustfmt.toml +++ b/rust-stream/rustfmt.toml @@ -54,7 +54,7 @@ use_field_init_shorthand = false force_explicit_abi = true condense_wildcard_suffixes = false color = "Auto" -required_version = "1.4.36" +required_version = "1.4.38" unstable_features = false disable_all_formatting = false skip_children = false diff --git a/rust-stream/src/arc_stream.rs b/rust-stream/src/arc_stream.rs new file mode 100644 index 0000000..006f73a --- /dev/null +++ b/rust-stream/src/arc_stream.rs @@ -0,0 +1,254 @@ +use std::iter::Sum; +use std::sync::{Arc, Mutex}; + +use self::core_affinity::CoreId; +use crate::stream::{AllocatorType, ArrayType, RustStream, StreamData}; + +struct ArcHeapData { + a_chunks: Vec>>>, + b_chunks: Vec>>>, + c_chunks: Vec>>>, +} + +pub struct ArcDevice { + pub(crate) ncore: usize, + pub(crate) pin: bool, + pub(crate) core_ids: Vec, + data: ArcHeapData, +} + +impl ArcDevice { + pub fn new(ncore: usize, pin: bool, alloc: A) -> Self { + let mut core_ids = match core_affinity::get_core_ids() { + Some(xs) => xs, + None => { + colour::e_red_ln!("Cannot enumerate cores, pinning will not work if enabled"); + (0..ncore).map(|i| CoreId { id: i }).collect() + } + }; + core_ids.resize(ncore, core_ids[0]); + + let lift = + || (0..ncore).map(|_| return Arc::new(Mutex::new(Vec::new_in(alloc)))).collect::>(); + let data = ArcHeapData { a_chunks: lift(), b_chunks: lift(), c_chunks: lift() }; + + ArcDevice { ncore, pin, core_ids, data } + } + + pub fn ref_a(&self, t: usize) -> Arc>> { self.data.a_chunks[t].clone() } + + pub fn ref_b(&self, t: usize) -> Arc>> { self.data.b_chunks[t].clone() } + + pub fn ref_c(&self, t: usize) -> Arc>> { self.data.c_chunks[t].clone() } + + // divide the length by the number of cores, the last core gets less work if it does not divide + fn chunk_size(&self, len: usize, t: usize) -> usize { + assert!(t < self.ncore); + let chunk = (len as f64 / self.ncore as f64).ceil() as usize; + if t == self.ncore - 1 { + len - (t * chunk) + } else { + chunk + } + } +} + +extern crate core_affinity; + +// Arc+Mutex threaded version, it should be semantically equal to the single threaded version +impl + RustStream for StreamData, A> +{ + fn init_arrays(&mut self) { + let init = self.init; + let pin = self.device.pin; + (0..self.device.ncore) + .map(&|t| { + let ref_a = self.device.ref_a(t); + let ref_b = self.device.ref_b(t); + let ref_c = self.device.ref_c(t); + let core = self.device.core_ids[t]; + let n = self.device.chunk_size(self.size, t); + std::thread::spawn(move || { + if pin { + core_affinity::set_for_current(core); + } + ref_a.lock().unwrap().resize(n, init.0); + ref_b.lock().unwrap().resize(n, init.1); + ref_c.lock().unwrap().resize(n, init.2); + }) + }) + .collect::>() + .into_iter() + .for_each(|t| t.join().unwrap()); + } + fn read_arrays(&mut self) { + let range = self.size; + let unlift = |drain: &mut Vec, source: &Vec>>>| { + let xs = + source.into_iter().flat_map(|x| x.lock().unwrap().clone().into_iter()).collect::>(); + for i in 0..range { + drain[i] = xs[i]; + } + }; + unlift(&mut self.a, &self.device.data.a_chunks); + unlift(&mut self.b, &self.device.data.b_chunks); + unlift(&mut self.c, &self.device.data.c_chunks); + } + + fn copy(&mut self) { + let pin = self.device.pin; + (0..self.device.ncore) + .map(move |t| { + let ref_a = self.device.ref_a(t); + let ref_c = self.device.ref_c(t); + let core = self.device.core_ids[t]; + let n = self.device.chunk_size(self.size, t); + std::thread::spawn(move || { + if pin { + core_affinity::set_for_current(core); + } + let a = ref_a.lock().unwrap(); + let mut c = ref_c.lock().unwrap(); + for i in 0..n { + c[i] = a[i]; + } + }) + }) + .collect::>() + .into_iter() + .for_each(|t| t.join().unwrap()); + } + + fn mul(&mut self) { + let scalar = self.scalar; + let pin = self.device.pin; + (0..self.device.ncore) + .map(move |t| { + let ref_b = self.device.ref_b(t); + let ref_c = self.device.ref_c(t); + let core = self.device.core_ids[t]; + let n = self.device.chunk_size(self.size, t); + std::thread::spawn(move || { + if pin { + core_affinity::set_for_current(core); + } + let mut b = ref_b.lock().unwrap(); + let c = ref_c.lock().unwrap(); + for i in 0..n { + b[i] = scalar * c[i]; + } + }) + }) + .collect::>() + .into_iter() + .for_each(|t| t.join().unwrap()); + } + + fn add(&mut self) { + let pin = self.device.pin; + (0..self.device.ncore) + .map(&|t| { + let ref_a = self.device.ref_a(t); + let ref_b = self.device.ref_b(t); + let ref_c = self.device.ref_c(t); + let core = self.device.core_ids[t]; + let n = self.device.chunk_size(self.size, t); + std::thread::spawn(move || { + if pin { + core_affinity::set_for_current(core); + } + let a = ref_a.lock().unwrap(); + let b = ref_b.lock().unwrap(); + let mut c = ref_c.lock().unwrap(); + for i in 0..n { + c[i] = a[i] + b[i]; + } + }) + }) + .collect::>() + .into_iter() + .for_each(|t| t.join().unwrap()); + } + + fn triad(&mut self) { + let scalar = self.scalar; + let pin = self.device.pin; + (0..self.device.ncore) + .map(&|t| { + let ref_a = self.device.ref_a(t); + let ref_b = self.device.ref_b(t); + let ref_c = self.device.ref_c(t); + let core = self.device.core_ids[t]; + let n = self.device.chunk_size(self.size, t); + std::thread::spawn(move || { + if pin { + core_affinity::set_for_current(core); + } + let mut a = ref_a.lock().unwrap(); + let b = ref_b.lock().unwrap(); + let c = ref_c.lock().unwrap(); + for i in 0..n { + a[i] = b[i] + scalar * c[i] + } + }) + }) + .collect::>() + .into_iter() + .for_each(|t| t.join().unwrap()); + } + + fn nstream(&mut self) { + let scalar = self.scalar; + let pin = self.device.pin; + (0..self.device.ncore) + .map(&|t| { + let ref_a = self.device.ref_a(t); + let ref_b = self.device.ref_b(t); + let ref_c = self.device.ref_c(t); + let core = self.device.core_ids[t]; + let n = self.device.chunk_size(self.size, t); + std::thread::spawn(move || { + if pin { + core_affinity::set_for_current(core); + } + let mut a = ref_a.lock().unwrap(); + let b = ref_b.lock().unwrap(); + let c = ref_c.lock().unwrap(); + for i in 0..n { + a[i] += b[i] + scalar * c[i] + } + }) + }) + .collect::>() + .into_iter() + .for_each(|t| t.join().unwrap()); + } + + fn dot(&mut self) -> T { + let pin = self.device.pin; + (0..self.device.ncore) + .map(&|t| { + let ref_a = self.device.ref_a(t); + let ref_b = self.device.ref_b(t); + let core = self.device.core_ids[t]; + let n = self.device.chunk_size(self.size, t); + std::thread::spawn(move || { + if pin { + core_affinity::set_for_current(core); + } + let a = ref_a.lock().unwrap(); + let b = ref_b.lock().unwrap(); + let mut p = T::default(); + for i in 0..n { + p += a[i] * b[i]; + } + p + }) + }) + .collect::>() + .into_iter() + .map(|t| t.join().unwrap()) + .sum() + } +} diff --git a/rust-stream/src/crossbeam_stream.rs b/rust-stream/src/crossbeam_stream.rs index 85b6503..44358ae 100644 --- a/rust-stream/src/crossbeam_stream.rs +++ b/rust-stream/src/crossbeam_stream.rs @@ -6,13 +6,13 @@ use crossbeam::thread; use self::core_affinity::CoreId; use crate::stream::{AllocatorType, ArrayType, RustStream, StreamData}; -pub struct ThreadedDevice { +pub struct CrossbeamDevice { pub(crate) ncore: usize, pub(crate) pin: bool, pub(crate) core_ids: Vec, } -impl ThreadedDevice { +impl CrossbeamDevice { pub fn new(ncore: usize, pin: bool) -> Self { let mut core_ids = match core_affinity::get_core_ids() { Some(xs) => xs, @@ -22,15 +22,13 @@ impl ThreadedDevice { } }; core_ids.resize(ncore, core_ids[0]); - ThreadedDevice { ncore, pin, core_ids } + CrossbeamDevice { ncore, pin, core_ids } } } -impl ThreadedDevice { +impl CrossbeamDevice { // divide the length by the number of cores, the last core gets less work if it does not divide - fn chunk_size(&self, len: usize) -> usize { - (len as f64 / self.ncore as f64).ceil() as usize - } + fn chunk_size(&self, len: usize) -> usize { (len as f64 / self.ncore as f64).ceil() as usize } // make a mutable chunk from the vec fn mk_mut_chunks<'a, T, A: AllocatorType>(&self, xs: &'a mut Vec) -> ChunksMut<'a, T> { @@ -48,7 +46,7 @@ extern crate core_affinity; // Crossbeam threaded version, it should be semantically equal to the single threaded version impl RustStream - for StreamData + for StreamData { fn init_arrays(&mut self) { thread::scope(|s| { diff --git a/rust-stream/src/lib.rs b/rust-stream/src/lib.rs index 0f34c02..3ac72c3 100644 --- a/rust-stream/src/lib.rs +++ b/rust-stream/src/lib.rs @@ -2,6 +2,7 @@ #![feature(vec_into_raw_parts)] use std::alloc::System; +use std::env; use std::fmt::{Debug, Display}; use std::iter::Sum; use std::mem::size_of; @@ -11,15 +12,19 @@ use num_traits::abs; use structopt::StructOpt; use tabular::{Row, Table}; -use crate::crossbeam_stream::ThreadedDevice; +use crate::arc_stream::ArcDevice; +use crate::crossbeam_stream::CrossbeamDevice; use crate::plain_stream::SerialDevice; use crate::rayon_stream::RayonDevice; use crate::stream::{AllocatorType, ArrayType, RustStream, StreamData}; +use crate::unsafe_stream::UnsafeDevice; +mod arc_stream; mod crossbeam_stream; mod plain_stream; mod rayon_stream; mod stream; +mod unsafe_stream; #[derive(Debug, StructOpt)] struct Options { @@ -119,9 +124,7 @@ fn check_solution, D, A: AllocatorType> fn run_cpu + Display, D, A: AllocatorType>( option: &Options, mut stream: StreamData, ) -> bool -where - StreamData: RustStream, -{ +where StreamData: RustStream { let benchmark = match (option.nstream_only, option.triad_only) { (true, false) => Benchmark::NStream, (false, true) => Benchmark::Triad, @@ -175,7 +178,8 @@ where let tabulate = |xs: &Vec, name: &str, t_size: usize| -> Vec<(&str, String)> { let tail = &xs[1..]; // tail only - // do stats + + // do stats let max = tail.iter().max().map(|d| d.as_secs_f64()); let min = tail.iter().min().map(|d| d.as_secs_f64()); match (min, max) { @@ -234,6 +238,7 @@ where let solutions_correct = match benchmark { Benchmark::All => { let (results, sum) = stream.run_all(option.numtimes); + stream.read_arrays(); let correct = check_solution(benchmark, option.numtimes, &stream, Some(sum)); tabulate_all(vec![ tabulate(&results.copy, "Copy", 2 * array_bytes), @@ -246,12 +251,14 @@ where } Benchmark::NStream => { let results = stream.run_nstream(option.numtimes); + stream.read_arrays(); let correct = check_solution(benchmark, option.numtimes, &stream, None); tabulate_all(vec![tabulate(&results, "Nstream", 4 * array_bytes)]); correct } Benchmark::Triad => { let results = stream.run_triad(option.numtimes); + stream.read_arrays(); let correct = check_solution(benchmark, option.numtimes, &stream, None); let total_bytes = 3 * array_bytes * option.numtimes; let bandwidth = giga_scale * (total_bytes as f64 / results.as_secs_f64()); @@ -260,7 +267,7 @@ where correct } }; - &stream.clean_up(); + stream.clean_up(); solutions_correct } @@ -274,139 +281,138 @@ static START_SCALAR: f32 = 0.4; static FLOAT_INIT_SCALAR: f32 = START_SCALAR; static FLOAT_INIT: (f32, f32, f32) = (START_A, START_B, START_C); -static DOUBLE_START_SCALAR: f64 = START_SCALAR as f64; +static DOUBLE_INIT_SCALAR: f64 = START_SCALAR as f64; static DOUBLE_INIT: (f64, f64, f64) = (START_A as f64, START_B as f64, START_C as f64); pub fn run(args: &Vec) -> bool { + let opt: Options = Options::from_iter(args); - let options: Options = Options::from_iter(args); - - if options.numtimes < 2 { + if opt.numtimes < 2 { panic!("numtimes must be >= 2") } let alloc = System; - let alloc_name = if options.malloc { "libc-malloc" } else { "rust-system" }; + let alloc_name = if opt.malloc { "libc-malloc" } else { "rust-system" }; + + fn mk_data( + opt: &Options, init: (T, T, T), scalar: T, dev: D, alloc: A, + ) -> StreamData { + StreamData::new_in(opt.arraysize, scalar, init, dev, alloc, opt.malloc, opt.init) + } + + let num_thread_key = "BABELSTREAM_NUM_THREADS"; + let max_ncores = num_cpus::get(); + let ncores = match env::var(num_thread_key) { + Ok(v) => match v.parse::() { + Err(bad) => { + colour::e_yellow_ln!( + "Cannot parse {} (reason: {}), defaulting to {}", + bad, + num_thread_key, + max_ncores + ); + max_ncores + } + Ok(n) if n <= 0 || n > max_ncores as i64 => { + println!("{} out of bound ({}), defaulting to {}", num_thread_key, n, max_ncores); + max_ncores + } + Ok(n) => n as usize, + }, + Err(_) => { + println!("{} not set, defaulting to max ({})", num_thread_key, max_ncores); + max_ncores + } + }; let rayon_device = &|| { - let dev = RayonDevice { pool: rayon::ThreadPoolBuilder::default().build().unwrap() }; - if !options.csv { + let rayon_num_thread_key = "RAYON_NUM_THREADS"; + if env::var(rayon_num_thread_key).is_ok() { + colour::e_yellow_ln!("{} is ignored, set {} instead", rayon_num_thread_key, num_thread_key) + } + let dev = RayonDevice { + pool: rayon::ThreadPoolBuilder::default().num_threads(ncores).build().unwrap(), + }; + if !opt.csv { println!("Using {} thread(s), alloc={}", dev.pool.current_num_threads(), alloc_name); - if options.pin { + if opt.pin { colour::e_yellow_ln!("Pinning threads have no effect on Rayon!") } } - if options.float { - run_cpu( - &options, - StreamData::new_in( - options.arraysize, - FLOAT_INIT_SCALAR, - FLOAT_INIT, - dev, - alloc, - options.malloc, - options.init, - ), - ) + if opt.float { + run_cpu(&opt, mk_data(&opt, FLOAT_INIT, FLOAT_INIT_SCALAR, dev, alloc)) } else { - run_cpu( - &options, - StreamData::new_in( - options.arraysize, - DOUBLE_START_SCALAR, - DOUBLE_INIT, - dev, - alloc, - options.malloc, - options.init, - ), - ) + run_cpu(&opt, mk_data(&opt, DOUBLE_INIT, DOUBLE_INIT_SCALAR, dev, alloc)) + } + }; + + let arc_device = &|| { + if !opt.csv { + println!("Using {} thread, pin={}, alloc={}", ncores, opt.pin, alloc_name); + } + if opt.float { + let dev = ArcDevice::::new(ncores, opt.pin, alloc); + run_cpu(&opt, mk_data(&opt, FLOAT_INIT, FLOAT_INIT_SCALAR, dev, alloc)) + } else { + let dev = ArcDevice::::new(ncores, opt.pin, alloc); + run_cpu(&opt, mk_data(&opt, DOUBLE_INIT, DOUBLE_INIT_SCALAR, dev, alloc)) + } + }; + + let unsafe_device = &|| { + if !opt.csv { + println!("Using {} thread, pin={}, alloc={}", ncores, opt.pin, alloc_name); + } + if opt.float { + let dev = UnsafeDevice::::new(ncores, opt.pin); + run_cpu(&opt, mk_data(&opt, FLOAT_INIT, FLOAT_INIT_SCALAR, dev, alloc)) + } else { + let dev = UnsafeDevice::::new(ncores, opt.pin); + run_cpu(&opt, mk_data(&opt, DOUBLE_INIT, DOUBLE_INIT_SCALAR, dev, alloc)) } }; let crossbeam_device = &|| { - let ncores = num_cpus::get(); - let dev = ThreadedDevice::new(ncores, options.pin); - if !options.csv { - println!("Using {} thread(s), pin={}, alloc={}", ncores, options.pin, alloc_name) + let dev = CrossbeamDevice::new(ncores, opt.pin); + if !opt.csv { + println!("Using {} thread(s), pin={}, alloc={}", ncores, opt.pin, alloc_name) } - if options.float { - run_cpu( - &options, - StreamData::new_in( - options.arraysize, - FLOAT_INIT_SCALAR, - FLOAT_INIT, - dev, - alloc, - options.malloc, - options.init, - ), - ) + if opt.float { + run_cpu(&opt, mk_data(&opt, FLOAT_INIT, FLOAT_INIT_SCALAR, dev, alloc)) } else { - run_cpu( - &options, - StreamData::new_in( - options.arraysize, - DOUBLE_START_SCALAR, - DOUBLE_INIT, - dev, - alloc, - options.malloc, - options.init, - ), - ) + run_cpu(&opt, mk_data(&opt, DOUBLE_INIT, DOUBLE_INIT_SCALAR, dev, alloc)) } }; + let st_device = &|| { - let dev = SerialDevice { pin: options.pin }; - if !options.csv { - println!("Using 1 thread, pin={}, alloc={}", options.pin, alloc_name); + let dev = SerialDevice { pin: opt.pin }; + if !opt.csv { + println!("Using 1 thread, pin={}, alloc={}", opt.pin, alloc_name); } - if options.float { - run_cpu( - &options, - StreamData::new_in( - options.arraysize, - FLOAT_INIT_SCALAR, - FLOAT_INIT, - dev, - alloc, - options.malloc, - options.init, - ), - ) + if opt.float { + run_cpu(&opt, mk_data(&opt, FLOAT_INIT, FLOAT_INIT_SCALAR, dev, alloc)) } else { - run_cpu( - &options, - StreamData::new_in( - options.arraysize, - DOUBLE_START_SCALAR, - DOUBLE_INIT, - dev, - alloc, - options.malloc, - options.init, - ), - ) + run_cpu(&opt, mk_data(&opt, DOUBLE_INIT, DOUBLE_INIT_SCALAR, dev, alloc)) } }; + let devices: Vec<(String, &'_ dyn Fn() -> bool)> = vec![ - ("CPU (Rayon)".to_string(), rayon_device), - (format!("CPU (Crossbeam, pinning={})", options.pin), crossbeam_device), ("CPU (Single threaded)".to_string(), st_device), + ("CPU (Rayon)".to_string(), rayon_device), + (format!("CPU (Arc, pinning={})", opt.pin), arc_device), + (format!("CPU (Unsafe, pinning={})", opt.pin), unsafe_device), + (format!("CPU (Crossbeam, pinning={})", opt.pin), crossbeam_device), ]; - if options.list { + if opt.list { devices.iter().enumerate().for_each(|(i, (name, _))| { println!("[{}] {}", i, name); }); true } else { - match devices.get(options.device) { + match devices.get(opt.device) { Some((name, run)) => { - if !&options.csv { + if !&opt.csv { println!( "BabelStream\n\ Version: {}\n\ @@ -414,14 +420,14 @@ pub fn run(args: &Vec) -> bool { VERSION.unwrap_or("unknown"), name ); - if options.init { + if opt.init { println!("Initialising arrays on main thread"); } } run() } None => { - eprintln!("Device index {} not available", options.device); + eprintln!("Device index {} not available", opt.device); false } } diff --git a/rust-stream/src/stream.rs b/rust-stream/src/stream.rs index 1568d4a..560c6f1 100644 --- a/rust-stream/src/stream.rs +++ b/rust-stream/src/stream.rs @@ -124,6 +124,7 @@ impl StreamData { pub trait RustStream { fn init_arrays(&mut self); + fn read_arrays(&mut self) {} // default to no-op as most impl. doesn't need this fn copy(&mut self); fn mul(&mut self); fn add(&mut self); diff --git a/rust-stream/src/unsafe_stream.rs b/rust-stream/src/unsafe_stream.rs new file mode 100644 index 0000000..968cc4e --- /dev/null +++ b/rust-stream/src/unsafe_stream.rs @@ -0,0 +1,266 @@ +extern crate core_affinity; + +use std::alloc::Allocator; +use std::iter::Sum; +use std::ops::Range; + +use crate::stream::{AllocatorType, ArrayType, RustStream, StreamData}; + +use self::core_affinity::CoreId; + +#[derive(Debug, Copy, Clone)] +struct UnsafeData(*mut T, usize); + +impl UnsafeData { + fn empty() -> UnsafeData { UnsafeData(([] as [T; 0]).as_mut_ptr(), 0) } + fn new(xs: &mut Vec) -> UnsafeData { + UnsafeData(xs.as_mut_ptr(), xs.len()) + } + + fn get_slice(&self) -> &mut [T] { unsafe { std::slice::from_raw_parts_mut(self.0, self.1) } } +} + +unsafe impl Send for UnsafeData {} +unsafe impl Sync for UnsafeData {} + +#[derive(Debug, Copy, Clone)] +struct UnsafeRefs { + a: UnsafeData, + b: UnsafeData, + c: UnsafeData, +} + +unsafe impl Send for UnsafeRefs {} +unsafe impl Sync for UnsafeRefs {} + +pub struct UnsafeDevice { + pub(crate) ncore: usize, + pub(crate) pin: bool, + pub(crate) core_ids: Vec, + data: UnsafeRefs, +} + +impl UnsafeDevice { + pub fn new(ncore: usize, pin: bool) -> Self { + let mut core_ids = match core_affinity::get_core_ids() { + Some(xs) => xs, + None => { + colour::e_red_ln!("Cannot enumerate cores, pinning will not work if enabled"); + (0..ncore).map(|i| CoreId { id: i }).collect() + } + }; + core_ids.resize(ncore, core_ids[0]); + + UnsafeDevice { + ncore, + pin, + core_ids, + data: UnsafeRefs { a: UnsafeData::empty(), b: UnsafeData::empty(), c: UnsafeData::empty() }, + } + } + + fn thread_ranges(&self, len: usize) -> Vec<(usize, Range)> { + let chunk = (len as f64 / self.ncore as f64).ceil() as usize; + (0..self.ncore) + .map(|t| { + (t, if t == self.ncore - 1 { (t * chunk)..len } else { (t * chunk)..((t + 1) * chunk) }) + }) + .collect::>() + } +} + +// Unsafe threaded version, it should be semantically equal to the single threaded version +impl RustStream + for StreamData, A> +{ + fn init_arrays(&mut self) { + self.device.data.a = UnsafeData::new(&mut self.a); + self.device.data.b = UnsafeData::new(&mut self.b); + self.device.data.c = UnsafeData::new(&mut self.c); + let init = self.init; + let pin = self.device.pin; + let data = self.device.data; + self + .device + .thread_ranges(self.size) + .into_iter() + .map(|(t, r)| { + let core = self.device.core_ids[t]; + std::thread::spawn(move || { + if pin { + core_affinity::set_for_current(core); + } + let a = data.a.get_slice(); + let b = data.b.get_slice(); + let c = data.c.get_slice(); + for i in r { + a[i] = init.0; + b[i] = init.1; + c[i] = init.2; + } + }) + }) + .collect::>() + .into_iter() + .for_each(|t| t.join().unwrap()); + } + + fn copy(&mut self) { + let pin = self.device.pin; + let data = self.device.data; + self + .device + .thread_ranges(self.size) + .into_iter() + .map(|(t, r)| { + let core = self.device.core_ids[t]; + std::thread::spawn(move || { + if pin { + core_affinity::set_for_current(core); + } + let a = data.a.get_slice(); + let c = data.c.get_slice(); + for i in r { + c[i] = a[i]; + } + }) + }) + .collect::>() + .into_iter() + .for_each(|t| t.join().unwrap()); + } + + fn mul(&mut self) { + let scalar = self.scalar; + let pin = self.device.pin; + let data = self.device.data; + self + .device + .thread_ranges(self.size) + .into_iter() + .map(|(t, r)| { + let core = self.device.core_ids[t]; + std::thread::spawn(move || { + if pin { + core_affinity::set_for_current(core); + } + let b = data.b.get_slice(); + let c = data.c.get_slice(); + for i in r { + b[i] = scalar * c[i]; + } + }) + }) + .collect::>() + .into_iter() + .for_each(|t| t.join().unwrap()); + } + + fn add(&mut self) { + let pin = self.device.pin; + let data = self.device.data; + self + .device + .thread_ranges(self.size) + .into_iter() + .map(|(t, r)| { + let core = self.device.core_ids[t]; + std::thread::spawn(move || { + if pin { + core_affinity::set_for_current(core); + } + let a = data.a.get_slice(); + let b = data.b.get_slice(); + let c = data.c.get_slice(); + for i in r { + c[i] = a[i] + b[i]; + } + }) + }) + .collect::>() + .into_iter() + .for_each(|t| t.join().unwrap()); + } + + fn triad(&mut self) { + let scalar = self.scalar; + let pin = self.device.pin; + let data = self.device.data; + self + .device + .thread_ranges(self.size) + .into_iter() + .map(|(t, r)| { + let core = self.device.core_ids[t]; + std::thread::spawn(move || { + if pin { + core_affinity::set_for_current(core); + } + let a = data.a.get_slice(); + let b = data.b.get_slice(); + let c = data.c.get_slice(); + for i in r { + a[i] = b[i] + scalar * c[i] + } + }) + }) + .collect::>() + .into_iter() + .for_each(|t| t.join().unwrap()); + } + + fn nstream(&mut self) { + let scalar = self.scalar; + let pin = self.device.pin; + let data = self.device.data; + self + .device + .thread_ranges(self.size) + .into_iter() + .map(|(t, r)| { + let core = self.device.core_ids[t]; + std::thread::spawn(move || { + if pin { + core_affinity::set_for_current(core); + } + let a = data.a.get_slice(); + let b = data.b.get_slice(); + let c = data.c.get_slice(); + for i in r { + a[i] += b[i] + scalar * c[i] + } + }) + }) + .collect::>() + .into_iter() + .for_each(|t| t.join().unwrap()); + } + + fn dot(&mut self) -> T { + let pin = self.device.pin; + let data = self.device.data; + self + .device + .thread_ranges(self.size) + .into_iter() + .map(|(t, r)| { + let core = self.device.core_ids[t]; + std::thread::spawn(move || { + if pin { + core_affinity::set_for_current(core); + } + let a = data.a.get_slice(); + let b = data.b.get_slice(); + let mut p = T::default(); + for i in r { + p += a[i] * b[i]; + } + p + }) + }) + .collect::>() + .into_iter() + .map(|t| t.join().unwrap()) + .sum() + } +} diff --git a/rust-stream/tests/integration_test.rs b/rust-stream/tests/integration_test.rs index 101f8f8..8031a79 100644 --- a/rust-stream/tests/integration_test.rs +++ b/rust-stream/tests/integration_test.rs @@ -2,7 +2,7 @@ use rstest::rstest; #[rstest] fn test_main( - #[values(0, 1, 2)] device: usize, // + #[values(0, 1, 2, 3, 4)] device: usize, // #[values("", "--pin")] pin: &str, // #[values("", "--malloc")] malloc: &str, // #[values("", "--init")] init: &str, // From edcc3e79cda649e2e3268268fbaf8d3fa9961566 Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Thu, 9 Dec 2021 11:25:26 +0000 Subject: [PATCH 70/78] fix sycl 2020 after merge from main --- src/sycl/SYCLStream.cpp | 22 ---------------------- src/sycl/SYCLStream.h | 6 +----- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/src/sycl/SYCLStream.cpp b/src/sycl/SYCLStream.cpp index 3f44537..76c0a8b 100644 --- a/src/sycl/SYCLStream.cpp +++ b/src/sycl/SYCLStream.cpp @@ -28,29 +28,7 @@ SYCLStream::SYCLStream(const size_t ARRAY_SIZE, const int device_index) if (device_index >= devices.size()) throw std::runtime_error("Invalid device index"); -<<<<<<< HEAD:SYCLStream.cpp sycl::device dev = devices[device_index]; -======= - // Check device can support FP64 if needed - if (sizeof(T) == sizeof(double)) - { - if (dev.get_info().size() == 0) { - throw std::runtime_error("Device does not support double precision, please use --float"); - } - } - - // Determine sensible dot kernel NDRange configuration - if (dev.is_cpu()) - { - dot_num_groups = dev.get_info(); - dot_wgsize = dev.get_info() * 2; - } - else - { - dot_num_groups = dev.get_info() * 4; - dot_wgsize = dev.get_info(); - } ->>>>>>> main:src/sycl/SYCLStream.cpp // Print out device information std::cout << "Using SYCL device " << getDeviceName(device_index) << std::endl; diff --git a/src/sycl/SYCLStream.h b/src/sycl/SYCLStream.h index 2ed95f6..7481d16 100644 --- a/src/sycl/SYCLStream.h +++ b/src/sycl/SYCLStream.h @@ -11,14 +11,10 @@ #include #include "Stream.h" -<<<<<<< HEAD:SYCLStream.h #include -======= -#include "CL/sycl.hpp" ->>>>>>> main:src/sycl/SYCLStream.h -#define IMPLEMENTATION_STRING "SYCL" +#define IMPLEMENTATION_STRING "SYCL 2020" template class SYCLStream : public Stream From e077d149dcabc8ea29012b8190ab8b91173d1da1 Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Thu, 9 Dec 2021 11:26:17 +0000 Subject: [PATCH 71/78] Retain 1.2.1 and 2020 versions of SYCL --- src/sycl/SYCLStream.cpp | 192 ++++++++++++++---------- src/sycl/SYCLStream.h | 43 ++++-- src/sycl/SYCLStream2020.cpp | 284 ++++++++++++++++++++++++++++++++++++ src/sycl/SYCLStream2020.h | 54 +++++++ 4 files changed, 484 insertions(+), 89 deletions(-) create mode 100644 src/sycl/SYCLStream2020.cpp create mode 100644 src/sycl/SYCLStream2020.h diff --git a/src/sycl/SYCLStream.cpp b/src/sycl/SYCLStream.cpp index 76c0a8b..00c043f 100644 --- a/src/sycl/SYCLStream.cpp +++ b/src/sycl/SYCLStream.cpp @@ -9,41 +9,51 @@ #include +using namespace cl::sycl; + // Cache list of devices bool cached = false; -std::vector devices; +std::vector devices; void getDeviceList(void); template -SYCLStream::SYCLStream(const size_t ARRAY_SIZE, const int device_index) -: array_size {ARRAY_SIZE}, - d_a {ARRAY_SIZE}, - d_b {ARRAY_SIZE}, - d_c {ARRAY_SIZE}, - d_sum {1} +SYCLStream::SYCLStream(const int ARRAY_SIZE, const int device_index) { if (!cached) getDeviceList(); + array_size = ARRAY_SIZE; + if (device_index >= devices.size()) throw std::runtime_error("Invalid device index"); - - sycl::device dev = devices[device_index]; - - // Print out device information - std::cout << "Using SYCL device " << getDeviceName(device_index) << std::endl; - std::cout << "Driver: " << getDeviceDriver(device_index) << std::endl; + device dev = devices[device_index]; // Check device can support FP64 if needed if (sizeof(T) == sizeof(double)) { - if (!dev.has(sycl::aspect::fp64)) - { + if (dev.get_info().size() == 0) { throw std::runtime_error("Device does not support double precision, please use --float"); } } - queue = std::make_unique(dev, sycl::async_handler{[&](sycl::exception_list l) + // Determine sensible dot kernel NDRange configuration + if (dev.is_cpu()) + { + dot_num_groups = dev.get_info(); + dot_wgsize = dev.get_info() * 2; + } + else + { + dot_num_groups = dev.get_info() * 4; + dot_wgsize = dev.get_info(); + } + + // Print out device information + std::cout << "Using SYCL device " << getDeviceName(device_index) << std::endl; + std::cout << "Driver: " << getDeviceDriver(device_index) << std::endl; + std::cout << "Reduction kernel config: " << dot_num_groups << " groups of size " << dot_wgsize << std::endl; + + queue = new cl::sycl::queue(dev, cl::sycl::async_handler{[&](cl::sycl::exception_list l) { bool error = false; for(auto e: l) @@ -52,7 +62,7 @@ SYCLStream::SYCLStream(const size_t ARRAY_SIZE, const int device_index) { std::rethrow_exception(e); } - catch (sycl::exception e) + catch (cl::sycl::exception e) { std::cout << e.what(); error = true; @@ -63,23 +73,33 @@ SYCLStream::SYCLStream(const size_t ARRAY_SIZE, const int device_index) throw std::runtime_error("SYCL errors detected"); } }}); - - // No longer need list of devices - devices.clear(); - cached = true; - - + + // Create buffers + d_a = new buffer(array_size); + d_b = new buffer(array_size); + d_c = new buffer(array_size); + d_sum = new buffer(dot_num_groups); } +template +SYCLStream::~SYCLStream() +{ + delete d_a; + delete d_b; + delete d_c; + delete d_sum; + delete queue; + devices.clear(); +} template void SYCLStream::copy() { - queue->submit([&](sycl::handler &cgh) + queue->submit([&](handler &cgh) { - sycl::accessor ka {d_a, cgh, sycl::read_only}; - sycl::accessor kc {d_c, cgh, sycl::write_only}; - cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) + auto ka = d_a->template get_access(cgh); + auto kc = d_c->template get_access(cgh); + cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) { kc[idx] = ka[idx]; }); @@ -91,11 +111,11 @@ template void SYCLStream::mul() { const T scalar = startScalar; - queue->submit([&](sycl::handler &cgh) + queue->submit([&](handler &cgh) { - sycl::accessor kb {d_b, cgh, sycl::write_only}; - sycl::accessor kc {d_c, cgh, sycl::read_only}; - cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) + auto kb = d_b->template get_access(cgh); + auto kc = d_c->template get_access(cgh); + cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) { kb[idx] = scalar * kc[idx]; }); @@ -106,12 +126,12 @@ void SYCLStream::mul() template void SYCLStream::add() { - queue->submit([&](sycl::handler &cgh) + queue->submit([&](handler &cgh) { - sycl::accessor ka {d_a, cgh, sycl::read_only}; - sycl::accessor kb {d_b, cgh, sycl::read_only}; - sycl::accessor kc {d_c, cgh, sycl::write_only}; - cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) + auto ka = d_a->template get_access(cgh); + auto kb = d_b->template get_access(cgh); + auto kc = d_c->template get_access(cgh); + cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) { kc[idx] = ka[idx] + kb[idx]; }); @@ -123,12 +143,12 @@ template void SYCLStream::triad() { const T scalar = startScalar; - queue->submit([&](sycl::handler &cgh) + queue->submit([&](handler &cgh) { - sycl::accessor ka {d_a, cgh, sycl::write_only}; - sycl::accessor kb {d_b, cgh, sycl::read_only}; - sycl::accessor kc {d_c, cgh, sycl::read_only}; - cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) + auto ka = d_a->template get_access(cgh); + auto kb = d_b->template get_access(cgh); + auto kc = d_c->template get_access(cgh); + cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) { ka[idx] = kb[idx] + scalar * kc[idx]; }); @@ -140,13 +160,12 @@ template void SYCLStream::nstream() { const T scalar = startScalar; - - queue->submit([&](sycl::handler &cgh) + queue->submit([&](handler &cgh) { - sycl::accessor ka {d_a, cgh}; - sycl::accessor kb {d_b, cgh, sycl::read_only}; - sycl::accessor kc {d_c, cgh, sycl::read_only}; - cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) + auto ka = d_a->template get_access(cgh); + auto kb = d_b->template get_access(cgh); + auto kc = d_c->template get_access(cgh); + cgh.parallel_for(range<1>{array_size}, [=](id<1> idx) { ka[idx] += kb[idx] + scalar * kc[idx]; }); @@ -157,55 +176,73 @@ void SYCLStream::nstream() template T SYCLStream::dot() { - - queue->submit([&](sycl::handler &cgh) + queue->submit([&](handler &cgh) { - sycl::accessor ka {d_a, cgh, sycl::read_only}; - sycl::accessor kb {d_b, cgh, sycl::read_only}; + auto ka = d_a->template get_access(cgh); + auto kb = d_b->template get_access(cgh); + auto ksum = d_sum->template get_access(cgh); - cgh.parallel_for(sycl::range<1>{array_size}, - // Reduction object, to perform summation - initialises the result to zero - sycl::reduction(d_sum, cgh, std::plus(), sycl::property::reduction::initialize_to_identity), - [=](sycl::id<1> idx, auto& sum) + auto wg_sum = accessor(range<1>(dot_wgsize), cgh); + + size_t N = array_size; + cgh.parallel_for(nd_range<1>(dot_num_groups*dot_wgsize, dot_wgsize), [=](nd_item<1> item) + { + size_t i = item.get_global_id(0); + size_t li = item.get_local_id(0); + size_t global_size = item.get_global_range()[0]; + + wg_sum[li] = 0.0; + for (; i < N; i += global_size) + wg_sum[li] += ka[i] * kb[i]; + + size_t local_size = item.get_local_range()[0]; + for (int offset = local_size / 2; offset > 0; offset /= 2) { - sum += ka[idx] * kb[idx]; - }); + item.barrier(cl::sycl::access::fence_space::local_space); + if (li < offset) + wg_sum[li] += wg_sum[li + offset]; + } + if (li == 0) + ksum[item.get_group(0)] = wg_sum[0]; + }); }); - // Get access on the host, and return a copy of the data (single number) - // This will block until the result is available, so no need to wait on the queue. - sycl::host_accessor result {d_sum, sycl::read_only}; - return result[0]; + T sum = 0.0; + auto h_sum = d_sum->template get_access(); + for (int i = 0; i < dot_num_groups; i++) + { + sum += h_sum[i]; + } + return sum; } template void SYCLStream::init_arrays(T initA, T initB, T initC) { - queue->submit([&](sycl::handler &cgh) + queue->submit([&](handler &cgh) { - sycl::accessor ka {d_a, cgh, sycl::write_only, sycl::no_init}; - sycl::accessor kb {d_b, cgh, sycl::write_only, sycl::no_init}; - sycl::accessor kc {d_c, cgh, sycl::write_only, sycl::no_init}; - - cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) + auto ka = d_a->template get_access(cgh); + auto kb = d_b->template get_access(cgh); + auto kc = d_c->template get_access(cgh); + cgh.parallel_for(range<1>{array_size}, [=](item<1> item) { - ka[idx] = initA; - kb[idx] = initB; - kc[idx] = initC; + auto id = item.get_id(0); + ka[id] = initA; + kb[id] = initB; + kc[id] = initC; }); }); - queue->wait(); } template void SYCLStream::read_arrays(std::vector& a, std::vector& b, std::vector& c) { - sycl::host_accessor _a {d_a, sycl::read_only}; - sycl::host_accessor _b {d_b, sycl::read_only}; - sycl::host_accessor _c {d_c, sycl::read_only}; + auto _a = d_a->template get_access(); + auto _b = d_b->template get_access(); + auto _c = d_c->template get_access(); for (int i = 0; i < array_size; i++) { a[i] = _a[i]; @@ -217,7 +254,7 @@ void SYCLStream::read_arrays(std::vector& a, std::vector& b, std::vecto void getDeviceList(void) { // Ask SYCL runtime for all devices in system - devices = sycl::device::get_devices(); + devices = cl::sycl::device::get_devices(); cached = true; } @@ -251,7 +288,7 @@ std::string getDeviceName(const int device) if (device < devices.size()) { - name = devices[device].get_info(); + name = devices[device].get_info(); } else { @@ -270,7 +307,7 @@ std::string getDeviceDriver(const int device) if (device < devices.size()) { - driver = devices[device].get_info(); + driver = devices[device].get_info(); } else { @@ -280,5 +317,6 @@ std::string getDeviceDriver(const int device) return driver; } +// TODO: Fix kernel names to allow multiple template specializations template class SYCLStream; template class SYCLStream; diff --git a/src/sycl/SYCLStream.h b/src/sycl/SYCLStream.h index 7481d16..d3fa18d 100644 --- a/src/sycl/SYCLStream.h +++ b/src/sycl/SYCLStream.h @@ -8,13 +8,22 @@ #pragma once #include -#include #include "Stream.h" +#include "CL/sycl.hpp" -#include +#define IMPLEMENTATION_STRING "SYCL" -#define IMPLEMENTATION_STRING "SYCL 2020" +namespace sycl_kernels +{ + template class init; + template class copy; + template class mul; + template class add; + template class triad; + template class nstream; + template class dot; +} template class SYCLStream : public Stream @@ -24,19 +33,29 @@ class SYCLStream : public Stream size_t array_size; // SYCL objects - // Queue is a pointer because we allow device selection - std::unique_ptr queue; + cl::sycl::queue *queue; + cl::sycl::buffer *d_a; + cl::sycl::buffer *d_b; + cl::sycl::buffer *d_c; + cl::sycl::buffer *d_sum; - // Buffers - sycl::buffer d_a; - sycl::buffer d_b; - sycl::buffer d_c; - sycl::buffer d_sum; + // SYCL kernel names + typedef sycl_kernels::init init_kernel; + typedef sycl_kernels::copy copy_kernel; + typedef sycl_kernels::mul mul_kernel; + typedef sycl_kernels::add add_kernel; + typedef sycl_kernels::triad triad_kernel; + typedef sycl_kernels::nstream nstream_kernel; + typedef sycl_kernels::dot dot_kernel; + + // NDRange configuration for the dot kernel + size_t dot_num_groups; + size_t dot_wgsize; public: - SYCLStream(const size_t, const int); - ~SYCLStream() = default; + SYCLStream(const int, const int); + ~SYCLStream(); virtual void copy() override; virtual void add() override; diff --git a/src/sycl/SYCLStream2020.cpp b/src/sycl/SYCLStream2020.cpp new file mode 100644 index 0000000..76c0a8b --- /dev/null +++ b/src/sycl/SYCLStream2020.cpp @@ -0,0 +1,284 @@ + +// Copyright (c) 2015-16 Tom Deakin, Simon McIntosh-Smith, +// University of Bristol HPC +// +// For full license terms please see the LICENSE file distributed with this +// source code + +#include "SYCLStream.h" + +#include + +// Cache list of devices +bool cached = false; +std::vector devices; +void getDeviceList(void); + +template +SYCLStream::SYCLStream(const size_t ARRAY_SIZE, const int device_index) +: array_size {ARRAY_SIZE}, + d_a {ARRAY_SIZE}, + d_b {ARRAY_SIZE}, + d_c {ARRAY_SIZE}, + d_sum {1} +{ + if (!cached) + getDeviceList(); + + if (device_index >= devices.size()) + throw std::runtime_error("Invalid device index"); + + sycl::device dev = devices[device_index]; + + // Print out device information + std::cout << "Using SYCL device " << getDeviceName(device_index) << std::endl; + std::cout << "Driver: " << getDeviceDriver(device_index) << std::endl; + + // Check device can support FP64 if needed + if (sizeof(T) == sizeof(double)) + { + if (!dev.has(sycl::aspect::fp64)) + { + throw std::runtime_error("Device does not support double precision, please use --float"); + } + } + + queue = std::make_unique(dev, sycl::async_handler{[&](sycl::exception_list l) + { + bool error = false; + for(auto e: l) + { + try + { + std::rethrow_exception(e); + } + catch (sycl::exception e) + { + std::cout << e.what(); + error = true; + } + } + if(error) + { + throw std::runtime_error("SYCL errors detected"); + } + }}); + + // No longer need list of devices + devices.clear(); + cached = true; + + +} + + +template +void SYCLStream::copy() +{ + queue->submit([&](sycl::handler &cgh) + { + sycl::accessor ka {d_a, cgh, sycl::read_only}; + sycl::accessor kc {d_c, cgh, sycl::write_only}; + cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) + { + kc[idx] = ka[idx]; + }); + }); + queue->wait(); +} + +template +void SYCLStream::mul() +{ + const T scalar = startScalar; + queue->submit([&](sycl::handler &cgh) + { + sycl::accessor kb {d_b, cgh, sycl::write_only}; + sycl::accessor kc {d_c, cgh, sycl::read_only}; + cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) + { + kb[idx] = scalar * kc[idx]; + }); + }); + queue->wait(); +} + +template +void SYCLStream::add() +{ + queue->submit([&](sycl::handler &cgh) + { + sycl::accessor ka {d_a, cgh, sycl::read_only}; + sycl::accessor kb {d_b, cgh, sycl::read_only}; + sycl::accessor kc {d_c, cgh, sycl::write_only}; + cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) + { + kc[idx] = ka[idx] + kb[idx]; + }); + }); + queue->wait(); +} + +template +void SYCLStream::triad() +{ + const T scalar = startScalar; + queue->submit([&](sycl::handler &cgh) + { + sycl::accessor ka {d_a, cgh, sycl::write_only}; + sycl::accessor kb {d_b, cgh, sycl::read_only}; + sycl::accessor kc {d_c, cgh, sycl::read_only}; + cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) + { + ka[idx] = kb[idx] + scalar * kc[idx]; + }); + }); + queue->wait(); +} + +template +void SYCLStream::nstream() +{ + const T scalar = startScalar; + + queue->submit([&](sycl::handler &cgh) + { + sycl::accessor ka {d_a, cgh}; + sycl::accessor kb {d_b, cgh, sycl::read_only}; + sycl::accessor kc {d_c, cgh, sycl::read_only}; + cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) + { + ka[idx] += kb[idx] + scalar * kc[idx]; + }); + }); + queue->wait(); +} + +template +T SYCLStream::dot() +{ + + queue->submit([&](sycl::handler &cgh) + { + sycl::accessor ka {d_a, cgh, sycl::read_only}; + sycl::accessor kb {d_b, cgh, sycl::read_only}; + + cgh.parallel_for(sycl::range<1>{array_size}, + // Reduction object, to perform summation - initialises the result to zero + sycl::reduction(d_sum, cgh, std::plus(), sycl::property::reduction::initialize_to_identity), + [=](sycl::id<1> idx, auto& sum) + { + sum += ka[idx] * kb[idx]; + }); + + }); + + // Get access on the host, and return a copy of the data (single number) + // This will block until the result is available, so no need to wait on the queue. + sycl::host_accessor result {d_sum, sycl::read_only}; + return result[0]; + +} + +template +void SYCLStream::init_arrays(T initA, T initB, T initC) +{ + queue->submit([&](sycl::handler &cgh) + { + sycl::accessor ka {d_a, cgh, sycl::write_only, sycl::no_init}; + sycl::accessor kb {d_b, cgh, sycl::write_only, sycl::no_init}; + sycl::accessor kc {d_c, cgh, sycl::write_only, sycl::no_init}; + + cgh.parallel_for(sycl::range<1>{array_size}, [=](sycl::id<1> idx) + { + ka[idx] = initA; + kb[idx] = initB; + kc[idx] = initC; + }); + }); + + queue->wait(); +} + +template +void SYCLStream::read_arrays(std::vector& a, std::vector& b, std::vector& c) +{ + sycl::host_accessor _a {d_a, sycl::read_only}; + sycl::host_accessor _b {d_b, sycl::read_only}; + sycl::host_accessor _c {d_c, sycl::read_only}; + for (int i = 0; i < array_size; i++) + { + a[i] = _a[i]; + b[i] = _b[i]; + c[i] = _c[i]; + } +} + +void getDeviceList(void) +{ + // Ask SYCL runtime for all devices in system + devices = sycl::device::get_devices(); + cached = true; +} + +void listDevices(void) +{ + getDeviceList(); + + // Print device names + if (devices.size() == 0) + { + std::cerr << "No devices found." << std::endl; + } + else + { + std::cout << std::endl; + std::cout << "Devices:" << std::endl; + for (int i = 0; i < devices.size(); i++) + { + std::cout << i << ": " << getDeviceName(i) << std::endl; + } + std::cout << std::endl; + } +} + +std::string getDeviceName(const int device) +{ + if (!cached) + getDeviceList(); + + std::string name; + + if (device < devices.size()) + { + name = devices[device].get_info(); + } + else + { + throw std::runtime_error("Error asking for name for non-existant device"); + } + + return name; +} + +std::string getDeviceDriver(const int device) +{ + if (!cached) + getDeviceList(); + + std::string driver; + + if (device < devices.size()) + { + driver = devices[device].get_info(); + } + else + { + throw std::runtime_error("Error asking for driver for non-existant device"); + } + + return driver; +} + +template class SYCLStream; +template class SYCLStream; diff --git a/src/sycl/SYCLStream2020.h b/src/sycl/SYCLStream2020.h new file mode 100644 index 0000000..7481d16 --- /dev/null +++ b/src/sycl/SYCLStream2020.h @@ -0,0 +1,54 @@ + +// Copyright (c) 2015-16 Tom Deakin, Simon McIntosh-Smith, +// University of Bristol HPC +// +// For full license terms please see the LICENSE file distributed with this +// source code + +#pragma once + +#include +#include + +#include "Stream.h" + +#include + +#define IMPLEMENTATION_STRING "SYCL 2020" + +template +class SYCLStream : public Stream +{ + protected: + // Size of arrays + size_t array_size; + + // SYCL objects + // Queue is a pointer because we allow device selection + std::unique_ptr queue; + + // Buffers + sycl::buffer d_a; + sycl::buffer d_b; + sycl::buffer d_c; + sycl::buffer d_sum; + + public: + + SYCLStream(const size_t, const int); + ~SYCLStream() = default; + + virtual void copy() override; + virtual void add() override; + virtual void mul() override; + virtual void triad() override; + virtual void nstream() override; + virtual T dot() override; + + virtual void init_arrays(T initA, T initB, T initC) override; + virtual void read_arrays(std::vector& a, std::vector& b, std::vector& c) override; + +}; + +// Populate the devices list +void getDeviceList(void); From b70c7f0357afa0fb6fa48d4098dcbcae65c10ced Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Thu, 9 Dec 2021 11:27:44 +0000 Subject: [PATCH 72/78] update headers --- src/main.cpp | 4 +++- src/sycl/SYCLStream2020.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2791bdc..5a01b74 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,6 +43,8 @@ #include "ACCStream.h" #elif defined(SYCL) #include "SYCLStream.h" +#elif defined(SYCL2020) +#include "SYCLStream2020.h" #elif defined(OMP) #include "OMPStream.h" #endif @@ -282,7 +284,7 @@ void run() // Use the OpenACC implementation stream = new ACCStream(ARRAY_SIZE, deviceIndex); -#elif defined(SYCL) +#elif defined(SYCL) || defined(SYCL2020) // Use the SYCL implementation stream = new SYCLStream(ARRAY_SIZE, deviceIndex); diff --git a/src/sycl/SYCLStream2020.cpp b/src/sycl/SYCLStream2020.cpp index 76c0a8b..6a0dd96 100644 --- a/src/sycl/SYCLStream2020.cpp +++ b/src/sycl/SYCLStream2020.cpp @@ -5,7 +5,7 @@ // For full license terms please see the LICENSE file distributed with this // source code -#include "SYCLStream.h" +#include "SYCLStream2020.h" #include From 2f1187f0d5290d1f59b0882f40f09895fd5d4198 Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Thu, 9 Dec 2021 11:40:30 +0000 Subject: [PATCH 73/78] Move SYCL2020 to subdirectory --- CMakeLists.txt | 3 +- src/sycl2020/.SYCLStream2020.h.swp | Bin 0 -> 12288 bytes src/{sycl => sycl2020}/SYCLStream2020.cpp | 0 src/{sycl => sycl2020}/SYCLStream2020.h | 3 +- src/sycl2020/model.cmake | 86 ++++++++++++++++++++++ 5 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 src/sycl2020/.SYCLStream2020.h.swp rename src/{sycl => sycl2020}/SYCLStream2020.cpp (100%) rename src/{sycl => sycl2020}/SYCLStream2020.h (95%) create mode 100644 src/sycl2020/model.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 58e0a3b..ad12dbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,6 +123,7 @@ register_model(hip HIP HIPStream.cpp) register_model(cuda CUDA CUDAStream.cu) register_model(kokkos KOKKOS KokkosStream.cpp) register_model(sycl SYCL SYCLStream.cpp) +register_model(sycl2020 SYCL2020 SYCLStream2020.cpp) register_model(acc ACC ACCStream.cpp) # defining RAJA collides with the RAJA namespace so USE_RAJA register_model(raja USE_RAJA RAJAStream.cpp) @@ -206,4 +207,4 @@ if (COMMAND setup_target) setup_target(${EXE_NAME}) endif () -install(TARGETS ${EXE_NAME} DESTINATION bin) \ No newline at end of file +install(TARGETS ${EXE_NAME} DESTINATION bin) diff --git a/src/sycl2020/.SYCLStream2020.h.swp b/src/sycl2020/.SYCLStream2020.h.swp new file mode 100644 index 0000000000000000000000000000000000000000..4b4ae277bfb5a4610edfce2e422910db10b9e910 GIT binary patch literal 12288 zcmeI2O^+Kj7{^_Xg#zN`z=b}f5^SZs?Y5;T8$!EjU?XLhg`^0|A@a;P8<&~!V0*Hp zg>pjTQ1Kl=AjA;~z5yo$M{aNf4hTL&`FkeY-R-K|9w~C9pC-1Sd2Ije87Yc`=3D&@ zI$uA_@OhlEKmPib|8n!s$|oN)W>w;9@A8=;DXX#_kFv9Vh4 zq+xW}F!L~8X|6OI!*{xaVNvgj_34obeWN$v4cwoB+)ipQonU8HmYeQ|Q#;koi=w}H1Kxl);0<^K z-hemY4R{0IfH&X`cmv+R189IpjIAGM?83tsJpTXR{r!LIF~)uXUxP2f+n^8D!76Bh z7r@itDZs$3M;ZGa{0x2qUx3fSXW%NBf_Fg=)IkkA1AaTf*!SQ&@D;cT47dVb0UdA_ zoB_wdKaVi>H~0~J13m=;TmUD*v*1_E=Rn7Vi@~ql`+C+Cbi9Z zOhq0XhmFQuYd$wi#zw8yOoUeH_Nenb@haU}6&$V#Pm`Jntz{%u59gPuw7dC7YhrAxRF-6Fv9L&!~gCW?G!H#X^_jMyPQfq*+$yAbG)Vzs$yE->bk}*_PUna rQ%g&D1*|le&()S+q>)PKlHl*jv`RynsFXIserlDO)`khvR~h>k;R44k literal 0 HcmV?d00001 diff --git a/src/sycl/SYCLStream2020.cpp b/src/sycl2020/SYCLStream2020.cpp similarity index 100% rename from src/sycl/SYCLStream2020.cpp rename to src/sycl2020/SYCLStream2020.cpp diff --git a/src/sycl/SYCLStream2020.h b/src/sycl2020/SYCLStream2020.h similarity index 95% rename from src/sycl/SYCLStream2020.h rename to src/sycl2020/SYCLStream2020.h index 7481d16..74b4221 100644 --- a/src/sycl/SYCLStream2020.h +++ b/src/sycl2020/SYCLStream2020.h @@ -12,7 +12,8 @@ #include "Stream.h" -#include +//#include +#include #define IMPLEMENTATION_STRING "SYCL 2020" diff --git a/src/sycl2020/model.cmake b/src/sycl2020/model.cmake new file mode 100644 index 0000000..e7b5a1c --- /dev/null +++ b/src/sycl2020/model.cmake @@ -0,0 +1,86 @@ + +register_flag_optional(CMAKE_CXX_COMPILER + "Any CXX compiler that is supported by CMake detection, this is used for host compilation when required by the SYCL compiler" + "c++") + +register_flag_required(SYCL_COMPILER + "Compile using the specified SYCL compiler implementation + Supported values are + ONEAPI-DPCPP - dpc++ that is part of an oneAPI Base Toolkit distribution (https://software.intel.com/content/www/us/en/develop/tools/oneapi/base-toolkit.html) + DPCPP - dpc++ as a standalone compiler (https://github.com/intel/llvm) + HIPSYCL - hipSYCL compiler (https://github.com/illuhad/hipSYCL) + COMPUTECPP - ComputeCpp compiler (https://developer.codeplay.com/products/computecpp/ce/home)") + +register_flag_optional(SYCL_COMPILER_DIR + "Absolute path to the selected SYCL compiler directory, most are packaged differently so set the path according to `SYCL_COMPILER`: + ONEAPI-DPCPP - not required but `dpcpp` must be on PATH, load oneAPI as per documentation (i.e `source /opt/intel/oneapi/setvars.sh` first) + HIPSYCL|DPCPP|COMPUTECPP - set to the root of the binary distribution that contains at least `bin/`, `include/`, and `lib/`" + "") + +register_flag_optional(OpenCL_LIBRARY + "[ComputeCpp only] Path to OpenCL library, usually called libOpenCL.so" + "${OpenCL_LIBRARY}") + +macro(setup) + set(CMAKE_CXX_STANDARD 17) + + + if (${SYCL_COMPILER} STREQUAL "HIPSYCL") + + + set(hipSYCL_DIR ${SYCL_COMPILER_DIR}/lib/cmake/hipSYCL) + + if (NOT EXISTS "${hipSYCL_DIR}") + message(WARNING "Falling back to hipSYCL < 0.9.0 CMake structure") + set(hipSYCL_DIR ${SYCL_COMPILER_DIR}/lib/cmake) + endif () + if (NOT EXISTS "${hipSYCL_DIR}") + message(FATAL_ERROR "Can't find the appropriate CMake definitions for hipSYCL") + endif () + + # register_definitions(_GLIBCXX_USE_CXX11_ABI=0) + find_package(hipSYCL CONFIG REQUIRED) + message(STATUS "ok") + + elseif (${SYCL_COMPILER} STREQUAL "COMPUTECPP") + + list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) + set(ComputeCpp_DIR ${SYCL_COMPILER_DIR}) + + setup_opencl_header_includes() + + register_definitions(CL_TARGET_OPENCL_VERSION=220 _GLIBCXX_USE_CXX11_ABI=0) + # ComputeCpp needs OpenCL + find_package(ComputeCpp REQUIRED) + + # this must come after FindComputeCpp (!) + set(COMPUTECPP_USER_FLAGS -O3 -no-serial-memop) + + elseif (${SYCL_COMPILER} STREQUAL "DPCPP") + set(CMAKE_CXX_COMPILER ${SYCL_COMPILER_DIR}/bin/clang++) + include_directories(${SYCL_COMPILER_DIR}/include/sycl) + register_definitions(CL_TARGET_OPENCL_VERSION=220) + register_append_cxx_flags(ANY -fsycl) + register_append_link_flags(-fsycl) + elseif (${SYCL_COMPILER} STREQUAL "ONEAPI-DPCPP") + set(CMAKE_CXX_COMPILER dpcpp) + register_definitions(CL_TARGET_OPENCL_VERSION=220) + else () + message(FATAL_ERROR "SYCL_COMPILER=${SYCL_COMPILER} is unsupported") + endif () + +endmacro() + + +macro(setup_target NAME) + if ( + (${SYCL_COMPILER} STREQUAL "COMPUTECPP") OR + (${SYCL_COMPILER} STREQUAL "HIPSYCL")) + # so ComputeCpp and hipSYCL has this weird (and bad) CMake usage where they append their + # own custom integration header flags AFTER the target has been specified + # hence this macro here + add_sycl_to_target( + TARGET ${NAME} + SOURCES ${IMPL_SOURCES}) + endif () +endmacro() From 9e9cefe8593944cfd6c939a15646f3c4c1ae0ae0 Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Thu, 9 Dec 2021 11:52:47 +0000 Subject: [PATCH 74/78] remove spurious .swp file --- src/sycl2020/.SYCLStream2020.h.swp | Bin 12288 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/sycl2020/.SYCLStream2020.h.swp diff --git a/src/sycl2020/.SYCLStream2020.h.swp b/src/sycl2020/.SYCLStream2020.h.swp deleted file mode 100644 index 4b4ae277bfb5a4610edfce2e422910db10b9e910..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2O^+Kj7{^_Xg#zN`z=b}f5^SZs?Y5;T8$!EjU?XLhg`^0|A@a;P8<&~!V0*Hp zg>pjTQ1Kl=AjA;~z5yo$M{aNf4hTL&`FkeY-R-K|9w~C9pC-1Sd2Ije87Yc`=3D&@ zI$uA_@OhlEKmPib|8n!s$|oN)W>w;9@A8=;DXX#_kFv9Vh4 zq+xW}F!L~8X|6OI!*{xaVNvgj_34obeWN$v4cwoB+)ipQonU8HmYeQ|Q#;koi=w}H1Kxl);0<^K z-hemY4R{0IfH&X`cmv+R189IpjIAGM?83tsJpTXR{r!LIF~)uXUxP2f+n^8D!76Bh z7r@itDZs$3M;ZGa{0x2qUx3fSXW%NBf_Fg=)IkkA1AaTf*!SQ&@D;cT47dVb0UdA_ zoB_wdKaVi>H~0~J13m=;TmUD*v*1_E=Rn7Vi@~ql`+C+Cbi9Z zOhq0XhmFQuYd$wi#zw8yOoUeH_Nenb@haU}6&$V#Pm`Jntz{%u59gPuw7dC7YhrAxRF-6Fv9L&!~gCW?G!H#X^_jMyPQfq*+$yAbG)Vzs$yE->bk}*_PUna rQ%g&D1*|le&()S+q>)PKlHl*jv`RynsFXIserlDO)`khvR~h>k;R44k From 610c1734a9e27c2b01d76a51278994193dd08fde Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Thu, 9 Dec 2021 11:53:21 +0000 Subject: [PATCH 75/78] Fix SYCL 2020 header file name and reduction identity typos --- src/sycl2020/SYCLStream2020.cpp | 2 +- src/sycl2020/SYCLStream2020.h | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sycl2020/SYCLStream2020.cpp b/src/sycl2020/SYCLStream2020.cpp index 6a0dd96..17a5ab5 100644 --- a/src/sycl2020/SYCLStream2020.cpp +++ b/src/sycl2020/SYCLStream2020.cpp @@ -165,7 +165,7 @@ T SYCLStream::dot() cgh.parallel_for(sycl::range<1>{array_size}, // Reduction object, to perform summation - initialises the result to zero - sycl::reduction(d_sum, cgh, std::plus(), sycl::property::reduction::initialize_to_identity), + sycl::reduction(d_sum, cgh, std::plus(), sycl::property::reduction::initialize_to_identity{}), [=](sycl::id<1> idx, auto& sum) { sum += ka[idx] * kb[idx]; diff --git a/src/sycl2020/SYCLStream2020.h b/src/sycl2020/SYCLStream2020.h index 74b4221..7481d16 100644 --- a/src/sycl2020/SYCLStream2020.h +++ b/src/sycl2020/SYCLStream2020.h @@ -12,8 +12,7 @@ #include "Stream.h" -//#include -#include +#include #define IMPLEMENTATION_STRING "SYCL 2020" From 6fd80713997974ad9eb5c0ee356756be38e208ec Mon Sep 17 00:00:00 2001 From: Tom Deakin Date: Thu, 9 Dec 2021 12:55:44 +0000 Subject: [PATCH 76/78] Update README.md --- README.md | 102 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index fbee03f..508b358 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -BabelStream -========== +# BabelStream logo @@ -10,8 +9,26 @@ This benchmark is similar in spirit, and based on, the STREAM benchmark [1] for Unlike other GPU memory bandwidth benchmarks this does *not* include the PCIe transfer time. -There are multiple implementations of this benchmark in a variety of programming models. -Currently implemented are: +There are multiple implementations of this benchmark in a variety of [programming models](#models). + +This code was previously called GPU-STREAM. + +## Table of Contents +- [Programming Models](#programming-models) +- [How is this different to STREAM?](#how-is-this-different-to-stream) +- [Building](#building) + - [CMake](#cmake) + - [GNU Make (removed)](#gnu-make) +- [Results](#results) +- [Contributing](#contributing) +- [Citing](#citing) + - [Other BabelStream publications](#other-babelstream-publications) + + +## Programming Models + +BabelStream is currently implemented in the following parallel programming models, listed in no particular order: + - OpenCL - CUDA - HIP @@ -20,19 +37,17 @@ Currently implemented are: - C++ Parallel STL - Kokkos - RAJA - - SYCL + - SYCL and SYCL 2020 - TBB - Thrust (via CUDA or HIP) -This code was previously called GPU-STREAM. This project also contains implementations in alternative languages with different build systems: * Julia - [JuliaStream.jl](./src/julia/JuliaStream.jl) * Java - [java-stream](./src/java/java-stream) * Scala - [scala-stream](./src/scala/scala-stream) -How is this different to STREAM? --------------------------------- +## How is this different to STREAM? BabelStream implements the four main kernels of the STREAM benchmark (along with a dot product), but by utilising different programming models expands the platforms which the code can run beyond CPUs. @@ -49,36 +64,46 @@ BabelStream therefore provides a measure of what memory bandwidth performance ca BabelStream also includes the nstream kernel from the Parallel Research Kernels (PRK) project, available on [GitHub](https://github.com/ParRes/Kernels). Details about PRK can be found in the following references: -> Van der Wijngaart, Rob F., and Timothy G. Mattson. The parallel research kernels. IEEE High Performance Extreme Computing Conference (HPEC). IEEE, 2014. +* Van der Wijngaart, Rob F., and Timothy G. Mattson. The parallel research kernels. IEEE High Performance Extreme Computing Conference (HPEC). IEEE, 2014. -> R. F. Van der Wijngaart, A. Kayi, J. R. Hammond, G. Jost, T. St. John, S. Sridharan, T. G. Mattson, J. Abercrombie, and J. Nelson. Comparing runtime systems with exascale ambitions using the Parallel Research Kernels. ISC 2016, [DOI: 10.1007/978-3-319-41321-1_17](https://doi.org/10.1007/978-3-319-41321-1_17). +* R. F. Van der Wijngaart, A. Kayi, J. R. Hammond, G. Jost, T. St. John, S. Sridharan, T. G. Mattson, J. Abercrombie, and J. Nelson. Comparing runtime systems with exascale ambitions using the Parallel Research Kernels. ISC 2016, [DOI: 10.1007/978-3-319-41321-1_17](https://doi.org/10.1007/978-3-319-41321-1_17). -> Jeff R. Hammond and Timothy G. Mattson. Evaluating data parallelism in C++ using the Parallel Research Kernels. IWOCL 2019, [DOI: 10.1145/3318170.3318192](https://doi.org/10.1145/3318170.3318192). +* Jeff R. Hammond and Timothy G. Mattson. Evaluating data parallelism in C++ using the Parallel Research Kernels. IWOCL 2019, [DOI: 10.1145/3318170.3318192](https://doi.org/10.1145/3318170.3318192). -Website -------- -[uob-hpc.github.io/BabelStream/](https://uob-hpc.github.io/BabelStream/) - -Usage ------ +## Building Drivers, compiler and software applicable to whichever implementation you would like to build against is required. ### CMake -The project supports building with CMake >= 3.13.0, it can be installed without root via the [official script](https://cmake.org/download/). -As with any CMake project, first configure the project: +The project supports building with CMake >= 3.13.0, which can be installed without root via the [official script](https://cmake.org/download/). + +Each BabelStream implementation (programming model) is built as follows: ```shell -> cd babelstream -> cmake -Bbuild -H. -DMODEL= # configure the build, build type defaults to Release -> cmake --build build # compile it -> ./build/-stream # executable available at ./build/ +$ cd babelstream + +# configure the build, build type defaults to Release +# The -DMODEL flag is required +$ cmake -Bbuild -H. -DMODEL= + +# compile +$ cmake --build build + +# run executables in ./build +$ ./build/-stream ``` -Source for each model's implementations are located in `./src/`. +The `MODEL` option selects one implementation of BabelStream to build. +The source for each model's implementations are located in `./src/`. +Currently available models are: +``` +omp;ocl;std;std20;hip;cuda;kokkos;sycl;sycl2020;acc;raja;tbb;thrust +``` + +#### Overriding default flags By default, we have defined a set of optimal flags for known HPC compilers. There are assigned those to `RELEASE_FLAGS`, and you can override them if required. @@ -108,6 +133,7 @@ For example: OpenCL_LIBRARY (optional, default=): Path to OpenCL library, usually called libOpenCL.so ... ``` + Alternatively, refer to the [CI script](./src/ci-test-compile.sh), which test-compiles most of the models, and see which flags are used there. *It is recommended that you delete the `build` directory when you change any of the build flags.* @@ -119,39 +145,39 @@ However, as the build process only involves a few source files, the required com -Results -------- +## Results -Sample results can be found in the `results` subdirectory. If you would like to submit updated results, please submit a Pull Request. +Sample results can be found in the `results` subdirectory. +Newer results are found in our [Performance Portability](https://github.com/UoB-HPC/performance-portability) repository. -Contributing ------------- + +## Contributing As of v4.0, the `main` branch of this repository will hold the latest released version. The `develop` branch will contain unreleased features due for the next (major and/or minor) release of BabelStream. Pull Requests should be made against the `develop` branch. -Citing ------- +## Citing + Please cite BabelStream via this reference: -> Deakin T, Price J, Martineau M, McIntosh-Smith S. GPU-STREAM v2.0: Benchmarking the achievable memory bandwidth of many-core processors across diverse parallel programming models. 2016. Paper presented at P^3MA Workshop at ISC High Performance, Frankfurt, Germany. DOI: 10.1007/978- 3-319-46079-6_34 +Deakin T, Price J, Martineau M, McIntosh-Smith S. GPU-STREAM v2.0: Benchmarking the achievable memory bandwidth of many-core processors across diverse parallel programming models. 2016. Paper presented at P^3MA Workshop at ISC High Performance, Frankfurt, Germany. DOI: 10.1007/978- 3-319-46079-6_34 -**Other BabelStream publications:** +### Other BabelStream publications -> Deakin T, Price J, Martineau M, McIntosh-Smith S. Evaluating attainable memory bandwidth of parallel programming models via BabelStream. International Journal of Computational Science and Engineering. Special issue. Vol. 17, No. 3, pp. 247–262. 2018.DOI: 10.1504/IJCSE.2018.095847 +* Deakin T, Price J, Martineau M, McIntosh-Smith S. Evaluating attainable memory bandwidth of parallel programming models via BabelStream. International Journal of Computational Science and Engineering. Special issue. Vol. 17, No. 3, pp. 247–262. 2018.DOI: 10.1504/IJCSE.2018.095847 -> Deakin T, McIntosh-Smith S. GPU-STREAM: Benchmarking the achievable memory bandwidth of Graphics Processing Units. 2015. Poster session presented at IEEE/ACM SuperComputing, Austin, United States. +* Deakin T, McIntosh-Smith S. GPU-STREAM: Benchmarking the achievable memory bandwidth of Graphics Processing Units. 2015. Poster session presented at IEEE/ACM SuperComputing, Austin, United States. You can view the [Poster and Extended Abstract](http://sc15.supercomputing.org/sites/all/themes/SC15images/tech_poster/tech_poster_pages/post150.html). -> Deakin T, Price J, Martineau M, McIntosh-Smith S. GPU-STREAM: Now in 2D!. 2016. Poster session presented at IEEE/ACM SuperComputing, Salt Lake City, United States. +* Deakin T, Price J, Martineau M, McIntosh-Smith S. GPU-STREAM: Now in 2D!. 2016. Poster session presented at IEEE/ACM SuperComputing, Salt Lake City, United States. You can view the [Poster and Extended Abstract](http://sc16.supercomputing.org/sc-archive/tech_poster/tech_poster_pages/post139.html). -> Raman K, Deakin T, Price J, McIntosh-Smith S. Improving achieved memory bandwidth from C++ codes on Intel Xeon Phi Processor (Knights Landing). IXPUG Spring Meeting, Cambridge, UK, 2017. +* Raman K, Deakin T, Price J, McIntosh-Smith S. Improving achieved memory bandwidth from C++ codes on Intel Xeon Phi Processor (Knights Landing). IXPUG Spring Meeting, Cambridge, UK, 2017. -> Deakin T, Price J, McIntosh-Smith S. Portable methods for measuring cache hierarchy performance. 2017. Poster sessions presented at IEEE/ACM SuperComputing, Denver, United States. +* Deakin T, Price J, McIntosh-Smith S. Portable methods for measuring cache hierarchy performance. 2017. Poster sessions presented at IEEE/ACM SuperComputing, Denver, United States. You can view the [Poster and Extended Abstract](http://sc17.supercomputing.org/SC17%20Archive/tech_poster/tech_poster_pages/post155.html) From 18376f77d800602611306b9fdda90f0993bf0254 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 9 Dec 2021 16:19:49 +0000 Subject: [PATCH 77/78] Move rust to appropriate folder --- {rust-stream => src/rust/rust-stream}/.cargo/config.toml | 0 {rust-stream => src/rust/rust-stream}/.gitignore | 0 {rust-stream => src/rust/rust-stream}/Cargo.lock | 0 {rust-stream => src/rust/rust-stream}/Cargo.toml | 0 {rust-stream => src/rust/rust-stream}/README.md | 0 {rust-stream => src/rust/rust-stream}/rustfmt.toml | 0 {rust-stream => src/rust/rust-stream}/src/arc_stream.rs | 0 {rust-stream => src/rust/rust-stream}/src/crossbeam_stream.rs | 0 {rust-stream => src/rust/rust-stream}/src/lib.rs | 0 {rust-stream => src/rust/rust-stream}/src/main.rs | 0 {rust-stream => src/rust/rust-stream}/src/plain_stream.rs | 0 {rust-stream => src/rust/rust-stream}/src/rayon_stream.rs | 0 {rust-stream => src/rust/rust-stream}/src/stream.rs | 0 {rust-stream => src/rust/rust-stream}/src/unsafe_stream.rs | 0 {rust-stream => src/rust/rust-stream}/tests/integration_test.rs | 0 15 files changed, 0 insertions(+), 0 deletions(-) rename {rust-stream => src/rust/rust-stream}/.cargo/config.toml (100%) rename {rust-stream => src/rust/rust-stream}/.gitignore (100%) rename {rust-stream => src/rust/rust-stream}/Cargo.lock (100%) rename {rust-stream => src/rust/rust-stream}/Cargo.toml (100%) rename {rust-stream => src/rust/rust-stream}/README.md (100%) rename {rust-stream => src/rust/rust-stream}/rustfmt.toml (100%) rename {rust-stream => src/rust/rust-stream}/src/arc_stream.rs (100%) rename {rust-stream => src/rust/rust-stream}/src/crossbeam_stream.rs (100%) rename {rust-stream => src/rust/rust-stream}/src/lib.rs (100%) rename {rust-stream => src/rust/rust-stream}/src/main.rs (100%) rename {rust-stream => src/rust/rust-stream}/src/plain_stream.rs (100%) rename {rust-stream => src/rust/rust-stream}/src/rayon_stream.rs (100%) rename {rust-stream => src/rust/rust-stream}/src/stream.rs (100%) rename {rust-stream => src/rust/rust-stream}/src/unsafe_stream.rs (100%) rename {rust-stream => src/rust/rust-stream}/tests/integration_test.rs (100%) diff --git a/rust-stream/.cargo/config.toml b/src/rust/rust-stream/.cargo/config.toml similarity index 100% rename from rust-stream/.cargo/config.toml rename to src/rust/rust-stream/.cargo/config.toml diff --git a/rust-stream/.gitignore b/src/rust/rust-stream/.gitignore similarity index 100% rename from rust-stream/.gitignore rename to src/rust/rust-stream/.gitignore diff --git a/rust-stream/Cargo.lock b/src/rust/rust-stream/Cargo.lock similarity index 100% rename from rust-stream/Cargo.lock rename to src/rust/rust-stream/Cargo.lock diff --git a/rust-stream/Cargo.toml b/src/rust/rust-stream/Cargo.toml similarity index 100% rename from rust-stream/Cargo.toml rename to src/rust/rust-stream/Cargo.toml diff --git a/rust-stream/README.md b/src/rust/rust-stream/README.md similarity index 100% rename from rust-stream/README.md rename to src/rust/rust-stream/README.md diff --git a/rust-stream/rustfmt.toml b/src/rust/rust-stream/rustfmt.toml similarity index 100% rename from rust-stream/rustfmt.toml rename to src/rust/rust-stream/rustfmt.toml diff --git a/rust-stream/src/arc_stream.rs b/src/rust/rust-stream/src/arc_stream.rs similarity index 100% rename from rust-stream/src/arc_stream.rs rename to src/rust/rust-stream/src/arc_stream.rs diff --git a/rust-stream/src/crossbeam_stream.rs b/src/rust/rust-stream/src/crossbeam_stream.rs similarity index 100% rename from rust-stream/src/crossbeam_stream.rs rename to src/rust/rust-stream/src/crossbeam_stream.rs diff --git a/rust-stream/src/lib.rs b/src/rust/rust-stream/src/lib.rs similarity index 100% rename from rust-stream/src/lib.rs rename to src/rust/rust-stream/src/lib.rs diff --git a/rust-stream/src/main.rs b/src/rust/rust-stream/src/main.rs similarity index 100% rename from rust-stream/src/main.rs rename to src/rust/rust-stream/src/main.rs diff --git a/rust-stream/src/plain_stream.rs b/src/rust/rust-stream/src/plain_stream.rs similarity index 100% rename from rust-stream/src/plain_stream.rs rename to src/rust/rust-stream/src/plain_stream.rs diff --git a/rust-stream/src/rayon_stream.rs b/src/rust/rust-stream/src/rayon_stream.rs similarity index 100% rename from rust-stream/src/rayon_stream.rs rename to src/rust/rust-stream/src/rayon_stream.rs diff --git a/rust-stream/src/stream.rs b/src/rust/rust-stream/src/stream.rs similarity index 100% rename from rust-stream/src/stream.rs rename to src/rust/rust-stream/src/stream.rs diff --git a/rust-stream/src/unsafe_stream.rs b/src/rust/rust-stream/src/unsafe_stream.rs similarity index 100% rename from rust-stream/src/unsafe_stream.rs rename to src/rust/rust-stream/src/unsafe_stream.rs diff --git a/rust-stream/tests/integration_test.rs b/src/rust/rust-stream/tests/integration_test.rs similarity index 100% rename from rust-stream/tests/integration_test.rs rename to src/rust/rust-stream/tests/integration_test.rs From 07c7e237c22a0838456fbe1f89a1d5dde83cef31 Mon Sep 17 00:00:00 2001 From: Tom Lin Date: Thu, 9 Dec 2021 16:22:52 +0000 Subject: [PATCH 78/78] Fix rust CI path --- .github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 42eada7..08eed2d 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-18.04 defaults: run: - working-directory: ./rust-stream + working-directory: ./src/rust/rust-stream steps: - uses: actions/checkout@v2 - name: Setup project