|
1 | 1 | .. currentmodule:: pnetcdf
|
2 | 2 | ==============================
|
3 |
| -Non-blocking Reads and Writes |
| 3 | +Nonblocking Reads and Writes |
4 | 4 | ==============================
|
5 | 5 |
|
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. |
15 | 11 |
|
16 | 12 | Nonblocking Write
|
17 | 13 | -------------------
|
18 | 14 |
|
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. |
22 | 22 |
|
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. |
31 | 31 |
|
32 | 32 | .. code-block:: Python
|
33 | 33 |
|
34 | 34 | req_ids = []
|
35 | 35 | write_buff = [randint(0,10, size=(xdim,ydim,zdim)).astype('i4')] * 10
|
| 36 | +
|
36 | 37 | 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] |
43 | 40 |
|
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``. |
45 | 51 |
|
46 | 52 | Nonblocking Read
|
47 | 53 | ------------------
|
48 | 54 |
|
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`. |
53 | 63 |
|
54 | 64 | - `buff` - Request to read an entire variable
|
55 | 65 | - `buff`, `index` - Request to read a single data value
|
56 | 66 | - `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 |
58 | 68 | - `buff`, `start`, `count`, `imap` - Request to read a mapped array of values
|
59 | 69 | - `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. |
62 | 72 |
|
63 | 73 | .. code-block:: Python
|
64 | 74 |
|
65 |
| - req_ids = [] |
66 |
| - # initialize the list of returned array references |
| 75 | + # initialize the list of references to read buffers |
67 | 76 | 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``. |
78 | 97 |
|
79 | 98 | Commit Read/Write Requests
|
80 | 99 | ----------------------------
|
81 | 100 |
|
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`). |
86 | 109 |
|
87 | 110 | Here's a python example to commit selected requests:
|
88 | 111 |
|
89 | 112 | .. code-block:: Python
|
90 | 113 |
|
91 |
| - # collective i/o |
| 114 | + # when the file is in the collective I/O mode |
92 | 115 | req_errs = [None] * num_reqs
|
93 | 116 | 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 |
98 | 117 |
|
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 |
100 | 128 | -----------------------------
|
101 | 129 |
|
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: |
107 | 143 |
|
108 |
| - Here's a python example to post a number of write requests and commit them using buffered non-blocking API: |
109 |
| - |
110 | 144 | .. code-block:: Python
|
111 | 145 |
|
112 |
| - f.enddef() |
113 | 146 | data = randint(0,10, size=(xdim,ydim,zdim)).astype('i4')
|
| 147 | +
|
114 | 148 | write_buff = [data] * num_reqs
|
115 | 149 | # Estimate the memory buffer size of all write requests
|
116 | 150 | buffsize = num_reqs * data.nbytes
|
| 151 | +
|
117 | 152 | # Attach buffer for buffered put requests
|
118 | 153 | f.attach_buff(buffsize)
|
| 154 | +
|
119 | 155 | req_ids = []
|
120 | 156 | for i in range(num_reqs):
|
121 | 157 | v = f.variables[f'data{i}']
|
122 |
| - # Post the request to write the whole variable |
| 158 | + # Post a request to write the whole variable |
123 | 159 | req_id = v.bput_var(write_buff[i])
|
124 | 160 | # Track the request ID for each write request
|
125 | 161 | 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 |
127 | 166 | f.wait_all()
|
| 167 | +
|
| 168 | + # Tell PnetCDF to detach the internal buffer |
128 | 169 | f.detach_buff()
|
129 |
| - |
130 |
| - For the full example program, see ``examples/non_blocking_write.py``. |
131 | 170 |
|
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