Skip to content

Commit 70afcca

Browse files
committed
Enable ncmpi_inq_header_size() to be called in define mode.
This API returns the file header size with metadata defined by the time of the call. This information can be helpful to pick proper values for arguments h_minfree, v_align, v_minfree, r_align when calling ncmpi__enddef() to allocate a sufficiently large free space for file header extent and variable data sections to grow without moving data already stored in the file, i.e. when adding new variables, dimensions, or attributes. Test program, tst_inq_header_size.c tests this change.
1 parent 4a261fb commit 70afcca

File tree

4 files changed

+302
-3
lines changed

4 files changed

+302
-3
lines changed

sneak_peek.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,14 @@ This is essentially a placeholder for the next release note ...
2626
+ none
2727

2828
* API semantics updates
29-
+ none
29+
+ API ncmpi_inq_header_size() now can be called in the define mode. This API
30+
returns the file header size with metadata defined by the time of the call.
31+
This information can be helpful to pick proper values for arguments
32+
h_minfree, v_align, v_minfree, r_align when calling ncmpi__enddef() to
33+
allocate a sufficiently large free space for file header extent and
34+
variable data sections to grow without moving data already stored in the
35+
file, i.e. when adding new variables, dimensions, or attributes.
36+
See [PR #201](https://github.com/Parallel-NetCDF/PnetCDF/pull/201).
3037

3138
* New error code precedence
3239
+ none

src/drivers/ncmpio/ncmpio_file_misc.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,17 @@ ncmpio_inq_misc(void *ncdp,
423423

424424
if (recsize != NULL) *recsize = ncp->recsize;
425425

426-
if (header_size != NULL) *header_size = ncp->xsz;
426+
if (header_size != NULL) {
427+
if (NC_indef(ncp))
428+
/* When called in define mode, calculate and return the current
429+
* header size. Cannot do the same for header extent, as the empty
430+
* space depends on arguments h_minfree and v_align of
431+
* ncmpi__enddef().
432+
*/
433+
*header_size = ncmpio_hdr_len_NC(ncp);
434+
else
435+
*header_size = ncp->xsz;
436+
}
427437

428438
if (header_extent != NULL) *header_extent = ncp->begin_var;
429439

test/testcases/Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ TESTPROGRAMS = file_create_open \
101101
tst_grow_header \
102102
tst_varn_var1 \
103103
tst_multi_redefine \
104-
tst_grow_data
104+
tst_grow_data \
105+
tst_inq_header_size
105106

106107
M4_SRCS = put_all_kinds.m4 \
107108
erange_fill.m4 \
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
/*
2+
* Copyright (C) 2025, Northwestern University and Argonne National Laboratory
3+
* See COPYRIGHT notice in top-level directory.
4+
*/
5+
6+
/*
7+
* This program tests a call to ncmpi_inq_header_size() when in the define
8+
* mode, which should calculate and return the latest file header size. This
9+
* can be useful for application users to decide how much free space to be
10+
* preserved in the file header section, i.e. by setting argument h_minfree
11+
* and/or v_align when calling ncmpi__enddef().
12+
*/
13+
14+
#include <stdio.h>
15+
#include <stdlib.h>
16+
#include <string.h>
17+
#include <strings.h> /* strcasecmp() */
18+
#include <libgen.h> /* basename() */
19+
#include <mpi.h>
20+
#include <pnetcdf.h>
21+
22+
#include <testutils.h>
23+
24+
static int debug;
25+
26+
static int
27+
tst_fmt(char *filename, int cmode)
28+
{
29+
char *str;
30+
int err, nerrs=0, rank, ncid, dimids[2], varid, int_buf;
31+
float flt_buf;
32+
double *dbl_buf;
33+
MPI_Offset old_h_size, old_h_extent, new_h_size, new_h_extent;
34+
35+
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
36+
37+
/* create a file */
38+
cmode |= NC_CLOBBER;
39+
err = ncmpi_create(MPI_COMM_WORLD, filename, cmode, MPI_INFO_NULL, &ncid);
40+
CHECK_ERR
41+
42+
flt_buf = 1.234;
43+
err = ncmpi_put_att(ncid, NC_GLOBAL, "_FillValue", NC_FLOAT, 1, &flt_buf);
44+
CHECK_ERR
45+
46+
err = ncmpi_def_dim(ncid, "X", 10, &dimids[0]); CHECK_ERR
47+
CHECK_ERR
48+
49+
err = ncmpi_def_var(ncid, "int_var", NC_INT, 1, dimids, &varid);
50+
CHECK_ERR
51+
52+
err = ncmpi_put_att(ncid, varid, "_FillValue", NC_FLOAT, 1, &flt_buf);
53+
EXP_ERR(NC_EBADTYPE)
54+
55+
int_buf = 5678;
56+
err = ncmpi_put_att(ncid, varid, "_FillValue", NC_INT, 1, &int_buf);
57+
CHECK_ERR
58+
59+
err = ncmpi_def_var(ncid, "dbl_var", NC_DOUBLE, 1, dimids, &varid);
60+
CHECK_ERR
61+
62+
err = ncmpi_def_var(ncid, "short_var", NC_SHORT, 1, dimids, &varid);
63+
CHECK_ERR
64+
65+
err = ncmpi_set_fill(ncid, NC_FILL, NULL); CHECK_ERR
66+
67+
err = ncmpi_inq_header_size(ncid, &old_h_size); CHECK_ERR
68+
69+
err = ncmpi_inq_header_extent(ncid, &old_h_extent); CHECK_ERR
70+
71+
if (debug && rank == 0)
72+
printf("%s at %d: header size=%lld extent=%lld\n", __FILE__,__LINE__,
73+
old_h_size, old_h_extent);
74+
75+
if (old_h_extent != 0) {
76+
printf("Error at %d: expect file extent size to be 0 but got %lld\n",
77+
__LINE__, old_h_extent);
78+
nerrs++;
79+
goto err_out;
80+
}
81+
82+
err = ncmpi_enddef(ncid); CHECK_ERR
83+
84+
err = ncmpi_inq_header_size(ncid, &new_h_size); CHECK_ERR
85+
86+
err = ncmpi_inq_header_extent(ncid, &new_h_extent); CHECK_ERR
87+
88+
if (debug && rank == 0)
89+
printf("%s at %d: header size=%lld extent=%lld\n", __FILE__,__LINE__,
90+
new_h_size, new_h_extent);
91+
92+
if (new_h_size != old_h_size) {
93+
printf("Error at %d: expect file header size %lld but got %lld\n",
94+
__LINE__, old_h_size, new_h_size);
95+
nerrs++;
96+
goto err_out;
97+
}
98+
99+
if (new_h_extent <= old_h_extent) {
100+
printf("Error at %d: expect file extent size > %lld but got %lld\n",
101+
__LINE__, old_h_extent, new_h_extent);
102+
nerrs++;
103+
goto err_out;
104+
}
105+
106+
err = ncmpi_close(ncid); CHECK_ERR
107+
108+
old_h_size = new_h_size;
109+
old_h_extent = new_h_extent;
110+
111+
/* open the file */
112+
err = ncmpi_open(MPI_COMM_WORLD, filename, NC_WRITE, MPI_INFO_NULL, &ncid);
113+
CHECK_ERR
114+
115+
err = ncmpi_inq_header_size(ncid, &new_h_size); CHECK_ERR
116+
117+
err = ncmpi_inq_header_extent(ncid, &new_h_extent); CHECK_ERR
118+
119+
if (debug && rank == 0)
120+
printf("%s at %d: header size=%lld extent=%lld\n", __FILE__,__LINE__,
121+
new_h_size, new_h_extent);
122+
123+
if (new_h_size != old_h_size) {
124+
printf("Error at %d: expect file header size %lld but got %lld\n",
125+
__LINE__, old_h_size, new_h_size);
126+
nerrs++;
127+
goto err_out;
128+
}
129+
130+
if (new_h_extent != old_h_extent) {
131+
printf("Error at %d: expect file extent size %lld but got %lld\n",
132+
__LINE__, old_h_extent, new_h_extent);
133+
nerrs++;
134+
goto err_out;
135+
}
136+
137+
old_h_size = new_h_size;
138+
old_h_extent = new_h_extent;
139+
140+
/* enter define mode and add new a dimension and a variable */
141+
err = ncmpi_redef(ncid); CHECK_ERR
142+
143+
str = "new global attribute of text data type";
144+
err = ncmpi_put_att_text(ncid, NC_GLOBAL, "global_attr", strlen(str), str);
145+
CHECK_ERR
146+
147+
err = ncmpi_def_dim(ncid, "time", NC_UNLIMITED, &dimids[0]); CHECK_ERR
148+
CHECK_ERR
149+
150+
err = ncmpi_def_dim(ncid, "Y", 10, &dimids[1]); CHECK_ERR
151+
CHECK_ERR
152+
153+
err = ncmpi_def_var(ncid, "new_int_var", NC_INT, 2, dimids, &varid);
154+
CHECK_ERR
155+
156+
dbl_buf = (double*) calloc(16, sizeof(double));
157+
err = ncmpi_put_att_double(ncid, varid, "attr", NC_DOUBLE, 16, dbl_buf);
158+
CHECK_ERR
159+
free(dbl_buf);
160+
161+
err = ncmpi_inq_header_size(ncid, &new_h_size); CHECK_ERR
162+
163+
err = ncmpi_inq_header_extent(ncid, &new_h_extent); CHECK_ERR
164+
165+
if (debug && rank == 0)
166+
printf("%s at %d: header size=%lld extent=%lld\n", __FILE__,__LINE__,
167+
new_h_size, new_h_extent);
168+
169+
if (new_h_size <= old_h_size) {
170+
printf("Error at %d: expect file header size > %lld but got %lld\n",
171+
__LINE__, old_h_size, new_h_size);
172+
nerrs++;
173+
goto err_out;
174+
}
175+
176+
if (new_h_extent != old_h_extent) {
177+
printf("Error at %d: expect file extent size %lld but got %lld\n",
178+
__LINE__, old_h_extent, new_h_extent);
179+
nerrs++;
180+
goto err_out;
181+
}
182+
183+
if (new_h_size > old_h_extent)
184+
/* header size grows beyond the current file extent size */
185+
err = ncmpi__enddef(ncid, 0, 512, 0, 0);
186+
else
187+
err = ncmpi_enddef(ncid);
188+
CHECK_ERR
189+
190+
old_h_size = new_h_size;
191+
old_h_extent = new_h_extent;
192+
193+
err = ncmpi_inq_header_size(ncid, &new_h_size); CHECK_ERR
194+
195+
err = ncmpi_inq_header_extent(ncid, &new_h_extent); CHECK_ERR
196+
197+
if (debug && rank == 0)
198+
printf("%s at %d: header size=%lld extent=%lld\n", __FILE__,__LINE__,
199+
new_h_size, new_h_extent);
200+
201+
if (new_h_size != old_h_size) {
202+
printf("Error at %d: expect file header size %lld but got %lld\n",
203+
__LINE__, old_h_size, new_h_size);
204+
nerrs++;
205+
goto err_out;
206+
}
207+
208+
if (new_h_extent < old_h_extent) {
209+
printf("Error at %d: expect file extent size >= %lld but got %lld\n",
210+
__LINE__, old_h_extent, new_h_extent);
211+
nerrs++;
212+
goto err_out;
213+
}
214+
215+
err = ncmpi_close(ncid); CHECK_ERR
216+
217+
err_out:
218+
return nerrs;
219+
}
220+
221+
int main(int argc, char **argv) {
222+
char filename[256], *hint_value;
223+
int err, nerrs=0, rank, bb_enabled=0;
224+
225+
MPI_Init(&argc, &argv);
226+
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
227+
228+
debug = 0;
229+
230+
if (argc > 2) {
231+
if (!rank) printf("Usage: %s [filename]\n",argv[0]);
232+
MPI_Finalize();
233+
return 1;
234+
}
235+
if (argc == 2) snprintf(filename, 256, "%s", argv[1]);
236+
else strcpy(filename, "testfile.nc");
237+
MPI_Bcast(filename, 256, MPI_CHAR, 0, MPI_COMM_WORLD);
238+
239+
if (rank == 0) {
240+
char *cmd_str = (char*)malloc(strlen(argv[0]) + 256);
241+
sprintf(cmd_str, "*** TESTING C %s for get header size in define mode", basename(argv[0]));
242+
printf("%-66s ------ ", cmd_str); fflush(stdout);
243+
free(cmd_str);
244+
}
245+
246+
/* check whether burst buffering is enabled */
247+
if (inq_env_hint("nc_burst_buf", &hint_value)) {
248+
if (strcasecmp(hint_value, "enable") == 0) bb_enabled = 1;
249+
free(hint_value);
250+
}
251+
252+
nerrs += tst_fmt(filename, 0);
253+
nerrs += tst_fmt(filename, NC_64BIT_OFFSET);
254+
if (!bb_enabled) {
255+
#ifdef ENABLE_NETCDF4
256+
nerrs += tst_fmt(filename, NC_NETCDF4);
257+
nerrs += tst_fmt(filename, NC_NETCDF4 | NC_CLASSIC_MODEL);
258+
#endif
259+
}
260+
nerrs += tst_fmt(filename, NC_64BIT_DATA);
261+
262+
/* check if PnetCDF freed all internal malloc */
263+
MPI_Offset malloc_size, sum_size;
264+
err = ncmpi_inq_malloc_size(&malloc_size);
265+
if (err == NC_NOERR) {
266+
MPI_Reduce(&malloc_size, &sum_size, 1, MPI_OFFSET, MPI_SUM, 0, MPI_COMM_WORLD);
267+
if (rank == 0 && sum_size > 0)
268+
printf("heap memory allocated by PnetCDF internally has "OFFFMT" bytes yet to be freed\n",
269+
sum_size);
270+
if (malloc_size > 0) ncmpi_inq_malloc_list();
271+
}
272+
273+
MPI_Allreduce(MPI_IN_PLACE, &nerrs, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
274+
if (rank == 0) {
275+
if (nerrs) printf(FAIL_STR,nerrs);
276+
else printf(PASS_STR);
277+
}
278+
279+
MPI_Finalize();
280+
return (nerrs > 0);
281+
}

0 commit comments

Comments
 (0)