@@ -15,7 +15,7 @@ functionality that is missing from those frameworks.
1515## Some Examples
1616
1717Below are a few representative examples showcasing the three main categories of
18- features nvmath-python offers: host, device , and distributed APIs.
18+ features nvmath-python offers: host, distributed host , and device APIs.
1919
2020### Host APIs
2121
@@ -102,6 +102,83 @@ s = cp.fft.fftn(a, axes=[-1], norm="ortho")
102102assert cp.allclose(r, s)
103103```
104104
105+ ### Distributed Host APIs
106+
107+ Distributed Host APIs are called from host code but execute on a distributed
108+ (multi-node multi-GPU) system. The following example shows the use of the
109+ function-form distributed FFT with CuPy ndarrays:
110+
111+ ``` python
112+ import cupy as cp
113+ from mpi4py import MPI
114+
115+ import nvmath.distributed
116+ from nvmath.distributed.distribution import Slab
117+
118+ # Initialize nvmath.distributed.
119+ comm = MPI .COMM_WORLD
120+ rank = comm.Get_rank()
121+ nranks = comm.Get_size()
122+ device_id = rank % cp.cuda.runtime.getDeviceCount()
123+ nvmath.distributed.initialize(device_id, comm, backends = [" nvshmem" ])
124+
125+ # The global 3-D FFT size is (512, 256, 512).
126+ # In this example, the input data is distributed across processes according to
127+ # the cuFFTMp Slab distribution on the X axis.
128+ shape = 512 // nranks, 256 , 512
129+
130+ # cuFFTMp uses the NVSHMEM PGAS model for distributed computation, which requires GPU
131+ # operands to be on the symmetric heap.
132+ a = nvmath.distributed.allocate_symmetric_memory(shape, cp, dtype = cp.complex128)
133+ # a is a cupy ndarray and can be operated on using in-place cupy operations.
134+ with cp.cuda.Device(device_id):
135+ a[:] = (
136+ cp.random.rand(* shape, dtype = cp.float64)
137+ + 1j * cp.random.rand(* shape, dtype = cp.float64)
138+ )
139+
140+ # Forward FFT.
141+ # In this example, the forward FFT operand is distributed according
142+ # to Slab.X distribution. With redistribute=False, the FFT result will be
143+ # distributed according to Slab.Y distribution.
144+ b = nvmath.distributed.fft.fft(a, distribution = Slab.X, options = {" redistribute" : False })
145+
146+ # Distributed FFT performs computations in-place. The result is stored in the same
147+ # buffer as operand a. Note, however, that operand b has a different shape (due
148+ # to Slab.Y distribution).
149+ if rank == 0 :
150+ print (f " Shape of a on rank { rank} is { a.shape} " )
151+ print (f " Shape of b on rank { rank} is { b.shape} " )
152+
153+ # Inverse FFT.
154+ # Recall from previous transform that the inverse FFT operand is distributed according
155+ # to Slab.Y. With redistribute=False, the inverse FFT result will be distributed
156+ # according to Slab.X distribution.
157+ c = nvmath.distributed.fft.ifft(b, distribution = Slab.Y, options = {" redistribute" : False })
158+
159+ # The shape of c is the same as a (due to Slab.X distribution). Once again, note that
160+ # a, b and c are sharing the same symmetric memory buffer (distributed FFT operations
161+ # are in-place).
162+ if rank == 0 :
163+ print (f " Shape of c on rank { rank} is { c.shape} " )
164+
165+ # Synchronize the default stream
166+ with cp.cuda.Device(device_id):
167+ cp.cuda.get_current_stream().synchronize()
168+
169+ if rank == 0 :
170+ print (f " Input type = { type (a)} , device = { a.device} " )
171+ print (f " FFT output type = { type (b)} , device = { b.device} " )
172+ print (f " IFFT output type = { type (c)} , device = { c.device} " )
173+
174+ # GPU operands on the symmetric heap are not garbage-collected and the user is
175+ # responsible for freeing any that they own (this deallocation is a collective
176+ # operation that must be called by all processes at the same point in the execution).
177+ # All cuFFTMp operations are inplace (a, b, and c share the same memory buffer), so
178+ # we take care to only free the buffer once.
179+ nvmath.distributed.free_symmetric_memory(a)
180+ ```
181+
105182### Device-side APIs
106183
107184nvmath-python exposes NVIDIA's device-side (Dx) APIs. This allows developers to call NVIDIA
@@ -195,89 +272,12 @@ if __name__ == "__main__":
195272 main()
196273```
197274
198- ### Distributed APIs
199-
200- Distributed APIs are called from host code but execute on a distributed
201- (multi-node multi-GPU) system. The following example shows the use of the
202- function-form distributed FFT with CuPy ndarrays:
203-
204- ``` python
205- import cupy as cp
206- from mpi4py import MPI
207-
208- import nvmath.distributed
209- from nvmath.distributed.distribution import Slab
210-
211- # Initialize nvmath.distributed.
212- comm = MPI .COMM_WORLD
213- rank = comm.Get_rank()
214- nranks = comm.Get_size()
215- device_id = rank % cp.cuda.runtime.getDeviceCount()
216- nvmath.distributed.initialize(device_id, comm, backends = [" nvshmem" ])
217-
218- # The global 3-D FFT size is (512, 256, 512).
219- # In this example, the input data is distributed across processes according to
220- # the cuFFTMp Slab distribution on the X axis.
221- shape = 512 // nranks, 256 , 512
222-
223- # cuFFTMp uses the NVSHMEM PGAS model for distributed computation, which requires GPU
224- # operands to be on the symmetric heap.
225- a = nvmath.distributed.allocate_symmetric_memory(shape, cp, dtype = cp.complex128)
226- # a is a cupy ndarray and can be operated on using in-place cupy operations.
227- with cp.cuda.Device(device_id):
228- a[:] = (
229- cp.random.rand(* shape, dtype = cp.float64)
230- + 1j * cp.random.rand(* shape, dtype = cp.float64)
231- )
232-
233- # Forward FFT.
234- # In this example, the forward FFT operand is distributed according
235- # to Slab.X distribution. With reshape=False, the FFT result will be
236- # distributed according to Slab.Y distribution.
237- b = nvmath.distributed.fft.fft(a, distribution = Slab.X, options = {" reshape" : False })
238-
239- # Distributed FFT performs computations in-place. The result is stored in the same
240- # buffer as operand a. Note, however, that operand b has a different shape (due
241- # to Slab.Y distribution).
242- if rank == 0 :
243- print (f " Shape of a on rank { rank} is { a.shape} " )
244- print (f " Shape of b on rank { rank} is { b.shape} " )
245-
246- # Inverse FFT.
247- # Recall from previous transform that the inverse FFT operand is distributed according
248- # to Slab.Y. With reshape=False, the inverse FFT result will be distributed according
249- # to Slab.X distribution.
250- c = nvmath.distributed.fft.ifft(b, distribution = Slab.Y, options = {" reshape" : False })
251-
252- # The shape of c is the same as a (due to Slab.X distribution). Once again, note that
253- # a, b and c are sharing the same symmetric memory buffer (distributed FFT operations
254- # are in-place).
255- if rank == 0 :
256- print (f " Shape of c on rank { rank} is { c.shape} " )
257-
258- # Synchronize the default stream
259- with cp.cuda.Device(device_id):
260- cp.cuda.get_current_stream().synchronize()
261-
262- if rank == 0 :
263- print (f " Input type = { type (a)} , device = { a.device} " )
264- print (f " FFT output type = { type (b)} , device = { b.device} " )
265- print (f " IFFT output type = { type (c)} , device = { c.device} " )
266-
267- # GPU operands on the symmetric heap are not garbage-collected and the user is
268- # responsible for freeing any that they own (this deallocation is a collective
269- # operation that must be called by all processes at the same point in the execution).
270- # All cuFFTMp operations are inplace (a, b, and c share the same memory buffer), so
271- # we take care to only free the buffer once.
272- nvmath.distributed.free_symmetric_memory(a)
273- ```
274-
275275## License
276276
277277All files hosted in this repository are subject to the [ Apache 2.0] ( ./LICENSE ) license.
278278
279279## Disclaimer
280280
281- nvmath-python is in a Beta state. Beta products may not be fully functional, may contain
282- errors or design flaws, and may be changed at any time without notice. We appreciate your
283- feedback to improve and iterate on our Beta products .
281+ nvmath-python contains features marked as experimental. Experimental features may not be
282+ fully functional, may contain errors or design flaws, and may be changed at any time without
283+ notice. We appreciate your feedback to improve and iterate on our experimental features .
0 commit comments