Skip to content

Commit c0e5bb7

Browse files
committed
update descriptions of nonblocking APIs
1 parent f4fd3c2 commit c0e5bb7

File tree

2 files changed

+164
-103
lines changed

2 files changed

+164
-103
lines changed

docs/source/tutorial/compare_netcdf4.rst

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,56 +2,76 @@
22
=================================
33
Comparing with netCDF4-python API
44
=================================
5-
.. warning::
65

7-
Under construction.
8-
9-
PnetCDF-python inherits many features from netCDF4-python, making the transition from the former to the latter a seamless process
10-
for most netCDF4-python applications. However, there are some exceptions to this as listed below. On the other hand, PnetCDF-python supports
11-
some new APIs not available in netCDF4-python.
6+
PnetCDF-python programming in a way is very similar to netCDF4-python.
7+
However, there are some differences as listed below.
128

139
Supported File Formats
1410
--------------------------
1511

16-
NetCDF4-python supports NETCDF4 formats(HDF5) in addition to classic netCDF formats(NETCDF3_CLASSIC, NETCDF3_64BIT_OFFSET, NETCDF3_64BIT_DATA). However,
17-
PnetCDF-python library only supports netCDF classic file formats, which means all netCDF4-dependent features are **not** supported, including user-defined types,
18-
compression, hirarchical structure, etc.
12+
NetCDF4-python supports NETCDF4 format (HDF5) in addition to classic netCDF
13+
formats (NETCDF3_CLASSIC, NETCDF3_64BIT_OFFSET, NETCDF3_64BIT_DATA). However,
14+
PnetCDF-python library only supports netCDF classic file formats, which means
15+
all netCDF4-dependent features are **not** supported, including user-defined
16+
types, compound data types, compression, hierarchical structure, etc.
1917

2018
Difference in Programming Model
2119
--------------------------------
2220

2321
Data/Define Mode
24-
NetCDF4-python library automatically switches between data and define mode for the user by calling ``redef`` and ``enddef`` internally within the define-mode
25-
operation functions, which is **not** implemented in Pnetcdf-python. A manual call to :meth:`File.redef` is compulsory to activate define mode before swtiching
26-
to define mode opearations, following the C library convention. Similarly, :meth:`File.enddef` is required before switching to data mode operations. This design is based on considerations of
27-
the following aspects:
22+
NetCDF4-python library automatically switches between data and define mode
23+
for the user by calling ``redef`` and ``enddef`` internally within the
24+
define-mode operation functions. For performance reason, this is **not**
25+
adopted in Pnetcdf-python. A manual call to :meth:`File.redef` is compulsory
26+
to re-enter the define mode, following the C library convention. Similarly,
27+
:meth:`File.enddef` is required before switching to data mode operations.
28+
This design is based on considerations of the following aspects:
29+
30+
- Minimize overheads during consecutive define operations: Automatically
31+
wrapping all define functions with :meth:`File.redef` and
32+
:meth:`File.enddef` could introduce significant overhead between
33+
consecutive define operations. The netCDF4-python approach results in
34+
unnecessary data/define mode switches, impacting performance.
2835

29-
- Minimize overheads during consecutive define operations: Automatically wrapping all define functions with :meth:`File.redef` and :meth:`File.enddef` could introduce
30-
significant overhead between consecutive define operations. This approach results in unnecessary data/define mode switches, impacting performance.
31-
- Avoid potential hanging when performing independent I/O: if :meth:`File.enddef` is automatically embeded in all data mode operation functions, the program will hang when
32-
partial processes are performing independent I/O(while others don't) because :meth:`File.enddef` is a collective call which requires all processes to participate.
36+
- Avoid potential hanging when performing independent I/O: if
37+
:meth:`File.enddef` is automatically embedded in all data mode operation
38+
functions, the program will hang when partial processes are performing
39+
independent I/O (while others don't) because :meth:`File.enddef` is a
40+
collective call which requires all processes to participate.
3341

3442
Independent/Collective I/O Mode
35-
There are two types of parallel I/O, independent I/O and collective I/O supported both in PnetCDF-python and netCDF4-python. NetCDF4-python toggles back and forth
36-
between the two types at variable-level. However, PnetCDF-python manages this at file-level through :meth:`File.begin_indep` and :meth:`File.end_indep`. The default I/O mode
37-
is collective I/O in PnetCDF-python.
43+
There are two types of parallel I/O operations, independent I/O and
44+
collective I/O supported both in PnetCDF-python and netCDF4-python.
45+
NetCDF4-python toggles back and forth between the two types at
46+
variable-level. However, PnetCDF-python manages this at file-level through
47+
:meth:`File.begin_indep` and :meth:`File.end_indep`. The default I/O mode is
48+
collective I/O in PnetCDF-python.
3849

3950

4051
Alternative Reads and Writes Methods
4152
------------------------------------------
4253

43-
For reading from and writing to netCDF4 variables, PnetCDF-python provides alternative methods in addition to numpy-like indexer syntax. The :meth:`Variable.get_var` and
44-
:meth:`Variable.put_var` methods are faithfull python-implementations of the put/get_var families from the original PnetCDF-C library. By overloading the input arguments,
45-
these methods can fulfill specific I/O needs to the target variable depending on the requirements of the applications: the entire variable, a single data value, an
46-
(subsampled) array of values, a mapped array or a list of subarrays. These methods require an array argument as read/write buffer, which is a prerequisite non-blocking
47-
I/O as introduced below.
54+
For reading from and writing to netCDF4 variables, PnetCDF-python provides
55+
alternative methods in addition to numpy-like indexer syntax. The
56+
:meth:`Variable.get_var` and :meth:`Variable.put_var` methods are faithful
57+
python-implementations of the put/get_var families from the original PnetCDF-C
58+
library. By overloading the input arguments, these methods can fulfill
59+
specific I/O needs to the target variable depending on the requirements of the
60+
applications: the entire variable, a single data value, a subarray of values,
61+
a mapped array or a list of subarrays. These methods require an array argument
62+
as read/write buffer, which is a prerequisite non-blocking I/O as introduced
63+
below.
4864

49-
For the example program, see ``examples/get_vara.py``.
65+
An example program can be found in ``examples/get_vara.py``.
5066

51-
Non-blocking I/O
67+
Nonblocking I/O
5268
------------------------------------------
53-
In additional to blocking read/writes, PnetCDF also offers the nonblocking API that enables users to initiate multiple requests without actually doing I/O and subsequently
54-
flush them altogether. This approach is designed to enhance performance by merging small I/O requests and maximizing I/O efficiency. This features is faithfully
55-
preserved in PnetCDF-python API.
69+
In additional to blocking read/write APIs, PnetCDF also offers the nonblocking
70+
APIs that lets users to post multiple requests and subsequently flush them
71+
altogether. This feature allows PnetCDF to improve performance by aggregating
72+
small I/O requests and maximizing I/O bandwidth utilization. This feature
73+
implemented in PnetCDF-C library is faithfully preserved in PnetCDF-python
74+
nonblocking APIs.
75+
76+
An example program can be found in ``examples/nonblocking/nonblocking_write.py``.
5677

57-
For the example program, see ``examples/nonblocking/nonblocking_write.py``.

docs/source/tutorial/non_blocking.rst

Lines changed: 113 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,134 +1,175 @@
11
.. currentmodule:: pnetcdf
22
==============================
3-
Non-blocking Reads and Writes
3+
Nonblocking Reads and Writes
44
==============================
55

6-
.. warning::
7-
8-
Under construction.
9-
10-
11-
12-
Alternative to blocking read/writes, PnetCDF-python nonblocking APIs allow users to first post multiple requests and later flush them altogether
13-
in order to achieve a better performance. A common practice is writing (or reading) subarrays to (from) multiple variables, e.g. one or more
14-
subarrays for each variable defined in the NetCDF file.
6+
Alternative to blocking read/writes, PnetCDF-python nonblocking APIs allow
7+
users to first post multiple requests and later flush them altogether in order
8+
to achieve a better performance. A common practice is writing (or reading)
9+
subarrays to (from) multiple variables, e.g. one or more subarrays for each
10+
variable defined in the NetCDF file.
1511

1612
Nonblocking Write
1713
-------------------
1814

19-
Write requests can be posted by the method call of :meth:`Variable.iput_var`. Same as :meth:`Variable.put_var`, the behavior of :meth:`Variable.iput_var` varies
20-
depending on the pattern of provided optional arguments - `index`, `start`, `count`, `stride`, `num` and `imap` as shown below. Note that the method only posts the
21-
request, which is not commited until :meth:`File.wait`. The method call returns a request id that can be optionally passed to :meth:`File.wait` to select this request.
15+
Write requests can be posted by the method call of :meth:`Variable.iput_var`.
16+
Same as :meth:`Variable.put_var`, the behavior of :meth:`Variable.iput_var`
17+
varies depending on the pattern of provided optional arguments - `index`,
18+
`start`, `count`, `stride`, `num` and `imap` as shown below. Note that the
19+
method only posts the request, which is not committed until :meth:`File.wait`.
20+
The method call returns a request id that can be optionally passed to
21+
:meth:`File.wait` to select this request.
2222

23-
- `data` - Reqeust to write an entire variable
24-
- `data`, `index` - Reqeust to write a single data value
25-
- `data`, `start`, `count` - Reqeust to write an array of values
26-
- `data`, `start`, `count`, `stride` - Reqeust to write a subsampled array of values
27-
- `data`, `start`, `count`, `imap` - Reqeust to write a mapped array of values
28-
- `start`, `count`, `num` - Reqeust to write a list of subarrays of values
29-
30-
Here's a python example to post 10 write requests that write to 10 netCDF variables in the same file.
23+
- `data` - Request to write an entire variable
24+
- `data`, `index` - Request to write a single data value
25+
- `data`, `start`, `count` - Request to write an array of values
26+
- `data`, `start`, `count`, `stride` - Request to write a subarray of values
27+
- `data`, `start`, `count`, `imap` - Request to write a mapped array of values
28+
- `start`, `count`, `num` - Request to write a list of subarrays of values
29+
30+
Here's a python example to post 10 write requests that write to 10 netCDF variables in the same file.
3131

3232
.. code-block:: Python
3333
3434
req_ids = []
3535
write_buff = [randint(0,10, size=(xdim,ydim,zdim)).astype('i4')] * 10
36+
3637
for i in range(num_reqs):
37-
v = f.variables[f'data{i}']
38-
datam = write_buff[i]
39-
# post the request to write the whole variable
40-
req_id = v.iput_var(datam)
41-
# track the request ID for each write request
42-
req_ids.append(req_id)
38+
v = f.variables[f'data{i}']
39+
datam = write_buff[i]
4340
44-
For the full example program, see ``examples/non_blocking_write.py``.
41+
# post a request to write the whole variable
42+
req_id = v.iput_var(datam)
43+
# track the request ID for each write request
44+
req_ids.append(req_id)
45+
46+
# wait for nonblocking writes to complete
47+
errs = [None] * num_reqs
48+
f.wait_all(num_reqs, req_ids, errs)
49+
50+
For the full example program, see ``examples/nonblocking/nonblocking_write.py``.
4551

4652
Nonblocking Read
4753
------------------
4854

49-
Read requests can be posted by the method call of :meth:`Variable.iget_var`. Note that unlike :meth:`Variable.get_var`, this method requires a
50-
mandatory argument - an empty numpy array reserved to be filled in the future. Again, the method call returns a request id that can be optionally passed to
51-
:meth:`File.wait` to select this request. Similar to :meth:`Variable.get_var`, the behavior of :meth:`Variable.iget_var` varies depending on
52-
the pattern of provided optional arguments - `index`, `start`, `count`, `stride`, `num` and `imap`.
55+
Read requests can be posted by the method call of :meth:`Variable.iget_var`.
56+
Note that unlike :meth:`Variable.get_var`, this method requires a mandatory
57+
argument - an empty numpy array reserved to be filled in the future. Again,
58+
the method call returns a request id that can be optionally passed to
59+
:meth:`File.wait` to select this request. Similar to :meth:`Variable.get_var`,
60+
the behavior of :meth:`Variable.iget_var` varies depending on the pattern of
61+
provided optional arguments - `index`, `start`, `count`, `stride`, `num` and
62+
`imap`.
5363

5464
- `buff` - Request to read an entire variable
5565
- `buff`, `index` - Request to read a single data value
5666
- `buff`, `start`, `count` - Request to read an array of values
57-
- `buff`, `start`, `count`, `stride` - Request to read a subsampled array of values
67+
- `buff`, `start`, `count`, `stride` - Request to read a subarray of values
5868
- `buff`, `start`, `count`, `imap` - Request to read a mapped array of values
5969
- `buff`, `start`, `count`, `num` - Request to read a list of subarrays of a netCDF variable
60-
61-
Here's a python example to post 10 read requests that read from 10 netCDF variables in the same file.
70+
71+
Here's a python example to post 10 read requests that read from 10 netCDF variables in the same file.
6272

6373
.. code-block:: Python
6474
65-
req_ids = []
66-
# initialize the list of returned array references
75+
# initialize the list of references to read buffers
6776
v_datas = []
68-
for i in range(num_reqs):
69-
v = f.variables[f'data{i}']
70-
buff = np.empty(shape = v.shape, dtype = v.datatype)# empty numpy array to hold returned variable values
71-
req_id = v.iget_var(buff)
72-
# track the request ID for each read request
73-
req_ids.append(req_id)
74-
# store the reference of variable values
75-
v_datas.append(buff)
76-
77-
For the full example program, see ``examples/flexible_api.py``.
77+
78+
req_ids = []
79+
for i in range(num_reqs):
80+
v = f.variables[f'data{i}']
81+
# allocate read buffer, a numpy array
82+
buff = np.empty(shape = v.shape, dtype = v.datatype)
83+
84+
# post a request to read the whole variable
85+
req_id = v.iget_var(buff)
86+
# track the request ID for each read request
87+
req_ids.append(req_id)
88+
89+
# store the reference of variable values
90+
v_datas.append(buff)
91+
92+
# wait for nonblocking reads to complete
93+
errs = [None] * num_reqs
94+
f.wait_all(num_reqs, req_ids, errs)
95+
96+
For the full example program, see ``examples/nonblocking/nonblocking_read.py``.
7897

7998
Commit Read/Write Requests
8099
----------------------------
81100

82-
Pending requests are eventually processed by :meth:`File.wait`. Requests to commited can be specified selectively specified by a request id list.
83-
If so, optionally, user can pass in a empty list to collect error statuses of each request, which is useful in request-wise error tracking and debugging.
84-
Alternatively, user can flush all pending write and/or read requests using module-level NC constants (e.g. `NC_REQ_ALL`) as input parameters. The suffix
85-
`_all` indicates this is collective I/O in contrast to indepedent I/O (without `_all`).
101+
Pending requests are eventually processed by :meth:`File.wait`. Requests to
102+
committed can be specified selectively specified by a request id list. If so,
103+
optionally, user can pass in a empty list to collect error statuses of each
104+
request, which is useful in request-wise error tracking and debugging.
105+
Alternatively, user can flush all pending write and/or read requests using
106+
module-level NC constants (e.g. `NC_REQ_ALL`) as input parameters. The suffix
107+
`_all` indicates this is collective I/O in contrast to independent I/O
108+
(without `_all`).
86109

87110
Here's a python example to commit selected requests:
88111

89112
.. code-block:: Python
90113
91-
# collective i/o
114+
# when the file is in the collective I/O mode
92115
req_errs = [None] * num_reqs
93116
f.wait_all(num_reqs, req_ids, req_errs)
94-
# f.wait() # independent i/o
95-
# f.wait_all() # commit all requests
96-
# f.wait_all(num = NC_PUT_REQ_ALL) # commit all write requests
97-
# f.wait_all(num = NC_GET_REQ_ALL) # commit all read requests
98117
99-
Buffered Non-blocking Write
118+
# when the file is in the independent I/O mode
119+
f.wait(num_reqs, req_ids, req_errs)
120+
121+
# commit all pending write requests
122+
f.wait_all(num = NC_PUT_REQ_ALL)
123+
124+
# commit all pending read requests
125+
f.wait_all(num = NC_GET_REQ_ALL)
126+
127+
Buffered Nonblocking Write
100128
-----------------------------
101129

102-
One limitation of the above non-blocking write is that users should not alter the contents of the write buffer once the request is posted until the wait API is returned.
103-
Any change to the buffer contents in between will result in unexpected error. To alleviate the this limitation, use can post buffered nonblocking write requests using
104-
:meth:`Variable.bput_var`. The input parameters and returned values are identical to :meth:`Variable.iput_var`. However, user are free to alter/reuse/delete the write
105-
buffer once the requests is postsed. As a prerequisite, the user need to tell PnetCDF the size of memory space required for all future reqests to this netCDF file. This step
106-
is achieved by :meth:`File.attach_buff`.
130+
One limitation of the above nonblocking write is that users should not alter
131+
the contents of the write buffer once the request is posted until the wait API
132+
is returned. Any change to the buffer contents in between will result in
133+
unexpected error. To alleviate the this limitation, use can post buffered
134+
nonblocking write requests using :meth:`Variable.bput_var`. The input
135+
parameters and returned values are identical to :meth:`Variable.iput_var`.
136+
However, user are free to alter/reuse/delete the write buffer once the
137+
requests is posted. As a prerequisite, the user need to tell PnetCDF the size
138+
of memory space required for all future requests to this netCDF file. This step
139+
is achieved by :meth:`File.attach_buff`.
140+
141+
Here's a python example to post a number of write requests and commit them
142+
using buffered nonblocking API:
107143

108-
Here's a python example to post a number of write requests and commit them using buffered non-blocking API:
109-
110144
.. code-block:: Python
111145
112-
f.enddef()
113146
data = randint(0,10, size=(xdim,ydim,zdim)).astype('i4')
147+
114148
write_buff = [data] * num_reqs
115149
# Estimate the memory buffer size of all write requests
116150
buffsize = num_reqs * data.nbytes
151+
117152
# Attach buffer for buffered put requests
118153
f.attach_buff(buffsize)
154+
119155
req_ids = []
120156
for i in range(num_reqs):
121157
v = f.variables[f'data{i}']
122-
# Post the request to write the whole variable
158+
# Post a request to write the whole variable
123159
req_id = v.bput_var(write_buff[i])
124160
# Track the request ID for each write request
125161
req_ids.append(req_id)
126-
# Free to alter the contents of write_buff here enabled by buffered non-blocking
162+
163+
# Users can now alter the contents of write_buff here
164+
165+
# wait for nonblocking, buffered writes to complete
127166
f.wait_all()
167+
168+
# Tell PnetCDF to detach the internal buffer
128169
f.detach_buff()
129-
130-
For the full example program, see ``examples/non_blocking_write.py``.
131170
132-
Remember to detach the write buffer after write requets are executed.
133-
134-
171+
For the full example program, see ``examples/nonblocking/nonblocking_write.py``.
172+
173+
Remember to detach the write buffer to free up the memory space.
174+
175+

0 commit comments

Comments
 (0)