Skip to content

Commit 1fabe85

Browse files
authored
Python API support for CuPy.arrays and torch.Tensor (#4528)
* Rename the files generated by the examples using the python bindings * Python API for writing and reading GPU buffers * Examples of using the python api for cupy and torch tensors * Add testing for the new function of reading in a preallocated buffer
1 parent 4775b32 commit 1fabe85

File tree

7 files changed

+383
-19
lines changed

7 files changed

+383
-19
lines changed

examples/hello/bpStepsWriteReadCuda/bpStepsWriteReadCupy-bindings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,5 @@ def read_array(fileName, nSteps):
6969
pinned_mempool.n_free_blocks())
7070

7171
nSteps = 2
72-
write_array("StepsWriteReadCuPy.bp", nSteps, gpuArray, cpuArray)
73-
read_array("StepsWriteReadCuPy.bp", nSteps)
72+
write_array("StepsCuPyBindings.bp", nSteps, gpuArray, cpuArray)
73+
read_array("StepsCuPyBindings.bp", nSteps)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import numpy as np
2+
import cupy as cp
3+
from adios2 import Stream
4+
5+
def write_array(fileName, nSteps, gpuArray, cpuArray):
6+
with Stream(fileName, "w") as wStream:
7+
for _ in wStream.steps(nSteps):
8+
wStream.write("cpuArray", cpuArray, cpuArray.shape,
9+
[0] * len(cpuArray.shape), cpuArray.shape)
10+
wStream.write("gpuArray", gpuArray, gpuArray.shape,
11+
[0] * len(gpuArray.shape), gpuArray.shape)
12+
# update buffers
13+
gpuArray = gpuArray * 2
14+
cpuArray = cpuArray + 1
15+
print("Write to file %s: %s data from GPU and %s data from CPU" % (
16+
fileName, gpuArray.shape, cpuArray.shape))
17+
18+
def read_array(fileName, readGpuShape, readCpuShape):
19+
with Stream(fileName, "r") as rStream:
20+
for _ in rStream.steps():
21+
step = rStream.current_step()
22+
cpuBuffer = np.zeros(readCpuShape, dtype=np.float32)
23+
rStream.read("cpuArray", cpuBuffer)
24+
25+
gpuBuffer = cp.zeros(readGpuShape, dtype=np.float32)
26+
rStream.read("gpuArray", buffer=gpuBuffer)
27+
28+
print("Step %d: read GPU data\n %s" % (step, gpuBuffer))
29+
print("Step %d: read CPU data\n %s" % (step, cpuBuffer))
30+
31+
32+
if __name__ == '__main__':
33+
cpuArray = np.array([[0, 1.0, 2.0], [3.0, 4.0, 5.0]], dtype=np.float32)
34+
gpuArray = cp.array([[0, 1.0, 2.0], [3.0, 4.0, 5.0]], dtype=np.float32)
35+
print("Array allocation: ", gpuArray.device)
36+
print("Bytes required to store the gpu array", gpuArray.nbytes)
37+
38+
nSteps = 2
39+
write_array("StepsWriteReadTorch.bp", nSteps, gpuArray, cpuArray)
40+
read_array("StepsWriteReadTorch.bp", gpuArray.shape, cpuArray.shape)

examples/hello/bpStepsWriteReadCuda/bpStepsWriteReadTorch-bindings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,5 @@ def read_array(fileName, nSteps):
6262
print("Bytes required to store the gpu array", gpuArray.nbytes)
6363

6464
nSteps = 2
65-
write_array("StepsWriteReadTorch.bp", nSteps, gpuArray, cpuArray)
66-
read_array("StepsWriteReadTorch.bp", nSteps)
65+
write_array("StepsTorchBindings.bp", nSteps, gpuArray, cpuArray)
66+
read_array("StepsTorchBindings.bp", nSteps)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import numpy as np
2+
import torch
3+
from adios2 import Stream, FileReader
4+
5+
def write_array(fileName, nSteps, gpuArray, cpuArray):
6+
with Stream(fileName, "w") as wStream:
7+
for _ in wStream.steps(nSteps):
8+
wStream.write("cpuArray", cpuArray, cpuArray.shape,
9+
[0] * len(cpuArray.shape), cpuArray.shape)
10+
wStream.write("gpuArray", gpuArray, gpuArray.shape,
11+
[0] * len(gpuArray.shape), gpuArray.shape)
12+
# update buffers
13+
gpuArray = gpuArray * 2
14+
cpuArray = cpuArray + 1
15+
print("Write to file %s: %s data from GPU and %s data from CPU" % (
16+
fileName, gpuArray.shape, cpuArray.shape))
17+
18+
def read_array(fileName, readGpuShape, readCpuShape):
19+
with Stream(fileName, "r") as rStream:
20+
for _ in rStream.steps():
21+
step = rStream.current_step()
22+
cpuBuffer = np.zeros(readCpuShape, dtype=np.float32)
23+
rStream.read_in_buffer("cpuArray", cpuBuffer)
24+
25+
cuda0 = torch.device('cuda:0')
26+
gpuBuffer = torch.zeros(readGpuShape, dtype=torch.float32, device=cuda0)
27+
rStream.read_in_buffer("gpuArray", gpuBuffer)
28+
29+
print("Step %d: read GPU data\n %s" % (step, gpuBuffer))
30+
print("Step %d: read CPU data\n %s" % (step, cpuBuffer))
31+
32+
33+
if __name__ == '__main__':
34+
cpuArray = np.array([[0, 1.0, 2.0], [3.0, 4.0, 5.0]], dtype=np.float32)
35+
cuda0 = torch.device('cuda:0')
36+
gpuArray = torch.tensor([[0, 1.0, 2.0], [3.0, 4.0, 5.0]],
37+
dtype=torch.float32, device=cuda0)
38+
print("Array allocation: ", gpuArray.device)
39+
print("Bytes required to store the gpu array", gpuArray.nbytes)
40+
41+
nSteps = 2
42+
write_array("StepsWriteReadTorch.bp", nSteps, gpuArray, cpuArray)
43+
read_array("StepsWriteReadTorch.bp", gpuArray.shape, cpuArray.shape)

python/adios2/engine.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@
55

66
import numpy as np
77

8+
# pylint: disable=duplicate-code
9+
try:
10+
import cupy as cp
11+
12+
ADIOS2_HAS_CUPY = True
13+
except ImportError:
14+
ADIOS2_HAS_CUPY = False
15+
try:
16+
import torch
17+
18+
ADIOS2_HAS_TORCH = True
19+
except ImportError:
20+
ADIOS2_HAS_TORCH = False
21+
# pylint: enable=duplicate-code
22+
823
from adios2 import bindings
924

1025

@@ -110,6 +125,14 @@ def put(self, variable, content, mode=bindings.Mode.Deferred):
110125
content = np.array([content])
111126
self.impl.Put(variable.impl, content)
112127
else:
128+
if ADIOS2_HAS_CUPY:
129+
if isinstance(content, cp.ndarray):
130+
self.impl.Put(variable.impl, content.data.ptr, mode)
131+
return
132+
if ADIOS2_HAS_TORCH:
133+
if isinstance(content, torch.Tensor):
134+
self.impl.Put(variable.impl, content.data_ptr(), mode)
135+
return
113136
raise ValueError
114137

115138
def perform_puts(self):
@@ -140,6 +163,14 @@ def get(self, variable, content=None, mode=bindings.Mode.Sync):
140163
self.impl.Get(variable.impl, content, mode)
141164
return None
142165

166+
if ADIOS2_HAS_CUPY:
167+
if isinstance(content, cp.ndarray):
168+
self.impl.Get(variable.impl, content.data.ptr, mode)
169+
return None
170+
if ADIOS2_HAS_TORCH:
171+
if isinstance(content, torch.Tensor):
172+
self.impl.Get(variable.impl, content.data_ptr(), mode)
173+
return None
143174
return self.impl.Get(variable.impl, mode)
144175

145176
def perform_gets(self):

0 commit comments

Comments
 (0)