Skip to content

Commit cd70e14

Browse files
Merge pull request #359 from NordicHPC/larstha-207-xpu
Fix #207 - XPU support
2 parents c851048 + e0e9f00 commit cd70e14

27 files changed

Lines changed: 1236 additions & 47 deletions

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ repository = "https://github.com/NordicHPC/sonar"
1010
nvidia = []
1111
amd = []
1212
xpu = []
13+
habana = []
1314
daemon = []
1415
kafka = [ "dep:rdkafka" ]
15-
default = [ "nvidia", "amd", "daemon", "kafka" ]
16+
default = [ "nvidia", "amd", "xpu", "habana", "daemon", "kafka" ]
1617

1718
[dependencies]
1819
cty = "0.2.2"

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: debug release test
1+
.PHONY: debug release test format
22
ARCH=$(shell uname -m)
33

44
debug:
@@ -11,3 +11,7 @@ test: debug release
1111
cargo test
1212
( cd util ; $(MAKE) test )
1313
( cd tests; ./run_tests.sh )
14+
15+
format:
16+
cargo fmt
17+
( cd util ; $(MAKE) format )

gpuapi/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
nvidia-shell
22
amd-shell
3+
xpu-shell
34
*.o

gpuapi/Makefile

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
# loaded GPU management libraries.
44
#
55
# The .a files must be built on systems that have the SDKs for the various GPUs available; in
6-
# practice, libsonar-amd.a is built on a system with an AMD GPU and toolchain, and libsonar-nvidia.a
7-
# is built on a system with an NVIDIA GPU and toolchain. For this reason the .a files are checked
8-
# into the git repo, they cannot easily be rebuilt on CI and on random developer systems.
6+
# practice, libsonar-amd.a is built on a system with an AMD GPU and toolchain, libsonar-nvidia.a is
7+
# built on a system with an NVIDIA GPU and toolchain, and libsonar-xpu.a is built on a system with
8+
# an XPU and XPU toolchain. For this reason the .a files are checked into the git repo, they cannot
9+
# easily be rebuilt on CI and on random developer systems.
910
#
1011
# And of course, for each CPU architecture the build host must be of that architecture as well, and
1112
# the .a files are ultimately stored in architecture-specific subdirectories of this directory.
@@ -36,7 +37,8 @@
3637
#
3738
# (build) module load GCC/11.3.0
3839

39-
CFLAGS=-g -O2 -Wall -fPIC
40+
# -Wformat-truncation is arguably a GCC misfeature.
41+
CFLAGS=-g -O2 -Wall -Wno-format-truncation -fPIC
4042

4143
.PHONY: default clean realclean
4244

@@ -46,11 +48,13 @@ default:
4648
@echo " libsonar-nvidia-stub.a"
4749
@echo " libsonar-amd.a the ROCM/hip SDK must be installed or loaded"
4850
@echo " libsonar-amd-stub.a"
51+
@echo " libsonar-xpu.a the XPU SDK must be installed or loaded"
52+
@echo " libsonar-xpu-stub.a"
4953
@echo ""
5054
@echo "See comments in Makefile for more information."
5155

5256
clean:
53-
rm -f *.o *.a *~ nvidia-shell amd-shell
57+
rm -f *.o *.a *~ nvidia-shell amd-shell xpu-shell
5458

5559
realclean:
5660
$(MAKE) clean
@@ -81,6 +85,19 @@ libsonar-amd-stub.a: sonar-amd-stub.o Makefile
8185
sonar-amd-stub.o: sonar-amd.c sonar-amd.h Makefile
8286
$(CC) -c $(CFLAGS) -o $@ $<
8387

88+
89+
libsonar-xpu.a: sonar-xpu.o Makefile
90+
ar rs $@ $<
91+
92+
sonar-xpu.o: sonar-xpu.c sonar-xpu.h strtcpy.h Makefile
93+
$(CC) -c $(CFLAGS) -DSONAR_XPU_GPU -o $@ $<
94+
95+
libsonar-xpu-stub.a: sonar-xpu-stub.o Makefile
96+
ar rs $@ $<
97+
98+
sonar-xpu-stub.o: sonar-xpu.c sonar-xpu.h Makefile
99+
$(CC) -c $(CFLAGS) -o $@ $<
100+
84101
# Various test code.
85102
#
86103
# Remember that it may be necessary to `module load` things first, see the build-*.bash scripts in
@@ -91,3 +108,6 @@ nvidia-shell: nvidia-shell.c sonar-nvidia.c sonar-nvidia.h Makefile
91108

92109
amd-shell: amd-shell.c sonar-amd.c sonar-amd.h Makefile
93110
$(CC) $(CFLAGS) -DSONAR_AMD_GPU -DLOGGING -I/opt/rocm/include -o amd-shell amd-shell.c sonar-amd.c -ldl
111+
112+
xpu-shell: xpu-shell.c sonar-xpu.c sonar-xpu.h Makefile
113+
$(CC) $(CFLAGS) -DSONAR_XPU_GPU -DLOGGING -o xpu-shell xpu-shell.c sonar-xpu.c -ldl

gpuapi/build-ex3-xpu-x86_64.bash

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env bash
2+
#
3+
# See Makefile for information.
4+
5+
set -e
6+
if [[ ! ( $(hostname) =~ n022 ) ]]; then
7+
echo "Wrong host!"
8+
exit 1
9+
fi
10+
module purge
11+
module list
12+
make libsonar-xpu.a
13+
mkdir -p x86_64
14+
mv libsonar-xpu.a x86_64
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
sycl-mmul

gpuapi/interactive-tests/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.PHONY: default
2+
3+
default:
4+
@echo "Pick an explicit target"
5+
6+
clean:
7+
rm -f sycl-mmul *.o *~
8+
9+
sycl-mmul: sycl-mmul.cpp
10+
@echo "Remember, you must have loaded intel icx support first, see sycl-mmul.cpp"
11+
icpx -fsycl -DSYCL_EXT_ONEAPI_MATRIX_VERSION=4 -o $@ $^

gpuapi/interactive-tests/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Here are programs that can be used to exercise the GPUs so that we can
2+
get interesting data from the various SMI programs or from Sonar's GPU
3+
shells (in the parent directory).
4+
5+
For example, running sycl-mmul in the background on a node with an
6+
Intel XPU, one can run ../xpu-shell -proc and should see the MMUL
7+
process running one one of the accelerators on the node, with
8+
non-trivial memory allocation, or ../xpu-shell -state to see that a
9+
card is busy.
10+
11+
The .cpp files in this directory have instructions for how to load and
12+
use toolchains appropriately, some of this is also encoded in
13+
Makefile. And also instructions for how to schedule on different
14+
devices and so on.
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
// XPU test case (maybe also Habana eventually).
2+
//
3+
// This was modified from
4+
// <URL:https://github.com/intel/llvm-test-suite/blob/intel/SYCL/Matrix/joint_matrix_bfloat16.cpp>.
5+
// See below for copyright notice.
6+
//
7+
// On eX3 XPU node eg n022 (note the 2025 version will not work with this code):
8+
//
9+
// $ module load intel/oneapi/2023.1/tbb
10+
// $ module load intel/oneapi/2023.1/compiler-rt
11+
// $ module load intel/oneapi/2023.1/compiler
12+
// $ icpx -fsycl -DSYCL_EXT_ONEAPI_MATRIX_VERSION=4 sycl-mmul.cpp -o sycl-mmul
13+
// $ ./sycl-mmul &
14+
//
15+
// It runs for about 45s on that node.
16+
//
17+
// While it's running, run `xpu-smi stats -d 0` or (from Sonar) `xpu-shell -state` or other similar
18+
// commands to verify that the compute engine is busy, and run both to verify that they are in
19+
// agreement.
20+
//
21+
// To schedule the load on specific devices, use an environment variable, here GPU 1:
22+
//
23+
// $ ONEAPI_DEVICE_SELECTOR='*:1' ./sycl-mmul
24+
//
25+
// There is a rich device selection language. See
26+
// <URL:https://github.com/intel/llvm/blob/sycl/sycl/doc/EnvironmentVariables.md#oneapi_device_selector>
27+
// for further documentation.
28+
29+
//==-------- joint_matrix_bfloat16.cpp - DPC++ joint_matrix----------- ----==//
30+
//
31+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
32+
// See https://llvm.org/LICENSE.txt for license information.
33+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
34+
//
35+
//===----------------------------------------------------------------------===//
36+
// REQUIRES: matrix
37+
38+
// RUN: %clangxx -fsycl %s -o %t.out -DSYCL_EXT_ONEAPI_MATRIX_VERSION=4
39+
// RUN: %CPU_RUN_PLACEHOLDER %t.out
40+
// RUN: %GPU_RUN_PLACEHOLDER %t.out
41+
42+
#include <iostream>
43+
#include <ctime>
44+
#include <sycl/sycl.hpp>
45+
46+
using namespace sycl;
47+
using namespace sycl::ext::oneapi::experimental::matrix;
48+
using bfloat16 = sycl::ext::oneapi::bfloat16;
49+
50+
#define SG_SZ 16
51+
52+
#define TM 8
53+
#define TN SG_SZ
54+
#define TK 16
55+
56+
#define BF16_EPSILON 0.00781250
57+
58+
template <typename T, size_t NUM_ROWS, size_t NUM_COLS> struct big_matrix {
59+
private:
60+
T *mat;
61+
62+
public:
63+
T *get_data() { return mat; }
64+
void set_data(T *data) { mat = data; }
65+
big_matrix(T *data) : mat(data) {}
66+
};
67+
68+
template <typename T1, typename T2, size_t M, size_t N, size_t K>
69+
void matrix_multiply(big_matrix<T1, M, N> &C, big_matrix<T2, M, K> &A,
70+
big_matrix<T2, K / 2, N * 2> &B) {
71+
size_t NDRangeM = M / TM;
72+
size_t NDRangeN = N / TN;
73+
buffer<bfloat16, 2> bufA(A.get_data(), range<2>(M, K));
74+
buffer<bfloat16, 2> bufB(B.get_data(), range<2>(K, N));
75+
buffer<float, 2> bufC((float *)C.get_data(), range<2>(M, N));
76+
77+
queue q;
78+
q.submit([&](handler &cgh) {
79+
auto accC = bufC.get_access<access::mode::read_write>(cgh);
80+
auto accA = bufA.get_access<access::mode::read_write>(cgh);
81+
auto accB = bufB.get_access<access::mode::read_write>(cgh);
82+
83+
cgh.parallel_for<class imatrix>(
84+
nd_range<2>({NDRangeM, NDRangeN * SG_SZ}, {1, 1 * SG_SZ}),
85+
[=](nd_item<2> spmd_item) [[intel::reqd_sub_group_size(SG_SZ)]]
86+
87+
{
88+
// The submatrix API has to be accessed by all the workitems in a
89+
// subgroup these functions will be called once by the subgroup no
90+
// code divergence between the workitems
91+
const auto global_idx = spmd_item.get_global_id(0);
92+
const auto global_idy = spmd_item.get_global_id(1);
93+
const auto sg_startx = global_idx - spmd_item.get_local_id(0);
94+
const auto sg_starty = global_idy - spmd_item.get_local_id(1);
95+
96+
sub_group sg = spmd_item.get_sub_group();
97+
joint_matrix<sub_group, bfloat16, use::a, TM, TK, layout::row_major>
98+
sub_a;
99+
// For B, we assume B has been already VNNIed.
100+
joint_matrix<sub_group, bfloat16, use::b, TK, TN,
101+
ext::intel::experimental::matrix::layout::packed>
102+
sub_b;
103+
joint_matrix<sub_group, float, use::accumulator, TM, TN> sub_c;
104+
105+
joint_matrix_load(sg, sub_c,
106+
accC.get_pointer() + (sg_startx * TM) * N +
107+
sg_starty / SG_SZ * TN,
108+
N, layout::row_major);
109+
for (int k = 0; k < K / TK; k += 1) { //
110+
joint_matrix_load(
111+
sg, sub_a, accA.get_pointer() + (sg_startx * TM) * K + k * TK,
112+
K);
113+
joint_matrix_load(sg, sub_b,
114+
accB.get_pointer() + (k * TK / 2) * (N * 2) +
115+
sg_starty / SG_SZ * TN * 2,
116+
N * 2);
117+
sub_c = joint_matrix_mad(sg, sub_a, sub_b, sub_c);
118+
}
119+
joint_matrix_store(sg, sub_c,
120+
accC.get_pointer() + (sg_startx * TM) * N +
121+
sg_starty / SG_SZ * TN,
122+
N, layout::row_major);
123+
}); // parallel for
124+
}).wait();
125+
}
126+
127+
static constexpr size_t MATRIX_M = TM * 2000;
128+
static constexpr size_t MATRIX_N = TN * 2000;
129+
static constexpr size_t MATRIX_K = TK * 2000;
130+
131+
int main() {
132+
// bfloat16 A[MATRIX_M][MATRIX_K];
133+
// bfloat16 B[MATRIX_K / 2][MATRIX_N * 2];
134+
// float C[MATRIX_M][MATRIX_N];
135+
136+
// Dynamic allocation or the linker will toss its cookies.
137+
bfloat16 (*A)[MATRIX_K] = (bfloat16 (*)[MATRIX_K])malloc(2*MATRIX_M*MATRIX_K);
138+
bfloat16 (*B)[MATRIX_N * 2] = (bfloat16(*)[MATRIX_N * 2])malloc(2*(MATRIX_K/2)*(MATRIX_N)*2);
139+
float (*C)[MATRIX_N] = (float(*)[MATRIX_N])malloc(4*MATRIX_M*MATRIX_N);
140+
141+
for (int i = 0; i < MATRIX_M; i++) {
142+
for (int j = 0; j < MATRIX_K; j++) {
143+
A[i][j] = bfloat16(1.0f * (i + j));
144+
}
145+
}
146+
for (int i = 0; i < MATRIX_K / 2; i++) {
147+
for (int j = 0; j < MATRIX_N * 2; j++) {
148+
B[i][j] = bfloat16(2.0f * i + 3.0f * j);
149+
}
150+
}
151+
for (int i = 0; i < MATRIX_M; i++) {
152+
for (int j = 0; j < MATRIX_N; j++) {
153+
C[i][j] = 1.0;
154+
}
155+
}
156+
157+
big_matrix<float, MATRIX_M, MATRIX_N> MC((float *)C);
158+
big_matrix<bfloat16, MATRIX_M, MATRIX_K> MA((bfloat16 *)A);
159+
big_matrix<bfloat16, MATRIX_K / 2, MATRIX_N * 2> MB((bfloat16 *)B);
160+
161+
time_t then = time(NULL);
162+
matrix_multiply(MC, MA, MB);
163+
time_t now = time(NULL);
164+
printf("Running time: %lds\n", now-then);
165+
}

0 commit comments

Comments
 (0)