|
19 | 19 | * If not, see <http://www.gnu.org/licenses/>. |
20 | 20 | */ |
21 | 21 | #include "openPMD/IO/HDF5/ParallelHDF5IOHandler.hpp" |
| 22 | +#include "openPMD/Error.hpp" |
22 | 23 | #include "openPMD/IO/HDF5/ParallelHDF5IOHandlerImpl.hpp" |
23 | 24 | #include "openPMD/auxiliary/Environment.hpp" |
| 25 | +#include "openPMD/auxiliary/JSON_internal.hpp" |
| 26 | +#include "openPMD/auxiliary/StringManip.hpp" |
| 27 | +#include "openPMD/auxiliary/Variant.hpp" |
| 28 | +#include <type_traits> |
| 29 | + |
| 30 | +#ifdef H5_HAVE_SUBFILING_VFD |
| 31 | +#include <H5FDsubfiling.h> |
| 32 | +#endif |
24 | 33 |
|
25 | 34 | #if openPMD_HAVE_MPI |
26 | 35 | #include <mpi.h> |
@@ -61,7 +70,7 @@ std::future<void> ParallelHDF5IOHandler::flush(internal::ParsedFlushParams &) |
61 | 70 |
|
62 | 71 | ParallelHDF5IOHandlerImpl::ParallelHDF5IOHandlerImpl( |
63 | 72 | AbstractIOHandler *handler, MPI_Comm comm, json::TracingJSON config) |
64 | | - : HDF5IOHandlerImpl{handler, std::move(config)} |
| 73 | + : HDF5IOHandlerImpl{handler, std::move(config), /* do_warn_unused_params = */ false} |
65 | 74 | , m_mpiComm{comm} |
66 | 75 | , m_mpiInfo{MPI_INFO_NULL} /* MPI 3.0+: MPI_INFO_ENV */ |
67 | 76 | { |
@@ -164,6 +173,173 @@ ParallelHDF5IOHandlerImpl::ParallelHDF5IOHandlerImpl( |
164 | 173 | VERIFY( |
165 | 174 | status >= 0, |
166 | 175 | "[HDF5] Internal error: Failed to set HDF5 file access property"); |
| 176 | + |
| 177 | + if (!m_config.json().is_null() && m_config.json().contains("vfd")) |
| 178 | + { |
| 179 | + auto vfd_json_config = m_config["vfd"]; |
| 180 | + if (!vfd_json_config.json().contains("type")) |
| 181 | + { |
| 182 | + throw error::BackendConfigSchema( |
| 183 | + {"hdf5", "vfd"}, |
| 184 | + "VFD configuration requires specifying the VFD type."); |
| 185 | + } |
| 186 | + std::string user_specified_type; |
| 187 | + if (auto value = |
| 188 | + json::asLowerCaseStringDynamic(vfd_json_config["type"].json()); |
| 189 | + value.has_value()) |
| 190 | + { |
| 191 | + user_specified_type = *value; |
| 192 | + } |
| 193 | + else |
| 194 | + { |
| 195 | + throw error::BackendConfigSchema( |
| 196 | + {"hdf5", "vfd", "type"}, "VFD type must be given as a string."); |
| 197 | + } |
| 198 | + |
| 199 | + if (user_specified_type == "default") |
| 200 | + { /* no-op */ |
| 201 | + } |
| 202 | + else if (user_specified_type == "subfiling") |
| 203 | + { |
| 204 | +#ifdef H5_HAVE_SUBFILING_VFD |
| 205 | + int thread_level = 0; |
| 206 | + MPI_Query_thread(&thread_level); |
| 207 | + if (thread_level >= MPI_THREAD_MULTIPLE) |
| 208 | + { |
| 209 | + H5FD_subfiling_config_t vfd_config; |
| 210 | + // query default subfiling parameters |
| 211 | + H5Pget_fapl_subfiling(m_fileAccessProperty, &vfd_config); |
| 212 | + |
| 213 | + auto int_accessor = |
| 214 | + [&vfd_json_config]( |
| 215 | + std::string const &key) -> std::optional<long long> { |
| 216 | + if (!vfd_json_config.json().contains(key)) |
| 217 | + { |
| 218 | + return std::nullopt; |
| 219 | + } |
| 220 | + auto const &val = vfd_json_config[key].json(); |
| 221 | + if (val.is_number_integer()) |
| 222 | + { |
| 223 | + return val.get<long long>(); |
| 224 | + } |
| 225 | + else |
| 226 | + { |
| 227 | + throw error::BackendConfigSchema( |
| 228 | + {"hdf5", "vfd", key}, |
| 229 | + "Excpecting value of type integer."); |
| 230 | + } |
| 231 | + }; |
| 232 | + auto string_accessor = |
| 233 | + [&vfd_json_config]( |
| 234 | + std::string const &key) -> std::optional<std::string> { |
| 235 | + if (!vfd_json_config.json().contains(key)) |
| 236 | + { |
| 237 | + return std::nullopt; |
| 238 | + } |
| 239 | + auto const &val = vfd_json_config[key].json(); |
| 240 | + if (auto str_val = json::asLowerCaseStringDynamic(val); |
| 241 | + str_val.has_value()) |
| 242 | + { |
| 243 | + return *str_val; |
| 244 | + } |
| 245 | + else |
| 246 | + { |
| 247 | + throw error::BackendConfigSchema( |
| 248 | + {"hdf5", "vfd", key}, |
| 249 | + "Excpecting value of type string."); |
| 250 | + } |
| 251 | + }; |
| 252 | + |
| 253 | + auto set_param = [](std::string const &key, |
| 254 | + auto *target, |
| 255 | + auto const &accessor) { |
| 256 | + if (auto val = accessor(key); val.has_value()) |
| 257 | + { |
| 258 | + *target = static_cast< |
| 259 | + std::remove_reference_t<decltype(*target)>>(*val); |
| 260 | + } |
| 261 | + }; |
| 262 | + |
| 263 | + set_param( |
| 264 | + "stripe_size", |
| 265 | + &vfd_config.shared_cfg.stripe_size, |
| 266 | + int_accessor); |
| 267 | + set_param( |
| 268 | + "stripe_count", |
| 269 | + &vfd_config.shared_cfg.stripe_count, |
| 270 | + int_accessor); |
| 271 | + std::optional<std::string> ioc_selection_raw; |
| 272 | + set_param("ioc_selection", &ioc_selection_raw, string_accessor); |
| 273 | + |
| 274 | + std::map<std::string, H5FD_subfiling_ioc_select_t> const |
| 275 | + ioc_selection_map{ |
| 276 | + {"one_per_node", SELECT_IOC_ONE_PER_NODE}, |
| 277 | + {"every_nth_rank", SELECT_IOC_EVERY_NTH_RANK}, |
| 278 | + {"with_config", SELECT_IOC_WITH_CONFIG}, |
| 279 | + {"total", SELECT_IOC_TOTAL}}; |
| 280 | + if (ioc_selection_raw.has_value()) |
| 281 | + { |
| 282 | + if (auto ioc_selection = |
| 283 | + ioc_selection_map.find(*ioc_selection_raw); |
| 284 | + ioc_selection != ioc_selection_map.end()) |
| 285 | + { |
| 286 | + vfd_config.shared_cfg.ioc_selection = |
| 287 | + ioc_selection->second; |
| 288 | + } |
| 289 | + else |
| 290 | + { |
| 291 | + throw error::BackendConfigSchema( |
| 292 | + {"hdf5", "vfd", "ioc_selection"}, |
| 293 | + "Unexpected value: '" + *ioc_selection_raw + "'."); |
| 294 | + } |
| 295 | + } |
| 296 | + |
| 297 | + // ... and set them |
| 298 | + H5Pset_fapl_subfiling(m_fileAccessProperty, &vfd_config); |
| 299 | + } |
| 300 | + else |
| 301 | + { |
| 302 | + std::cerr << "[HDF5 Backend] The requested subfiling VFD of " |
| 303 | + "HDF5 requires the use of threaded MPI." |
| 304 | + << std::endl; |
| 305 | + } |
| 306 | +#else |
| 307 | + std::cerr |
| 308 | + << "[HDF5 Backend] No support for the requested subfiling VFD " |
| 309 | + "found in the installed version of HDF5. Will continue with " |
| 310 | + "default settings. Tip: Configure a recent version of HDF5 " |
| 311 | + "with '-DHDF5_ENABLE_SUBFILING_VFD=ON'." |
| 312 | + << std::endl; |
| 313 | +#endif |
| 314 | + } |
| 315 | + else |
| 316 | + { |
| 317 | + throw error::BackendConfigSchema( |
| 318 | + {"hdf5", "vfd", "type"}, |
| 319 | + "Unknown value: '" + user_specified_type + "'."); |
| 320 | + } |
| 321 | + |
| 322 | + // unused params |
| 323 | + auto shadow = m_config.invertShadow(); |
| 324 | + if (shadow.size() > 0) |
| 325 | + { |
| 326 | + switch (m_config.originallySpecifiedAs) |
| 327 | + { |
| 328 | + case json::SupportedLanguages::JSON: |
| 329 | + std::cerr << "Warning: parts of the backend configuration for " |
| 330 | + "HDF5 remain unused:\n" |
| 331 | + << shadow << std::endl; |
| 332 | + break; |
| 333 | + case json::SupportedLanguages::TOML: { |
| 334 | + auto asToml = json::jsonToToml(shadow); |
| 335 | + std::cerr << "Warning: parts of the backend configuration for " |
| 336 | + "HDF5 remain unused:\n" |
| 337 | + << asToml << std::endl; |
| 338 | + break; |
| 339 | + } |
| 340 | + } |
| 341 | + } |
| 342 | + } |
167 | 343 | } |
168 | 344 |
|
169 | 345 | ParallelHDF5IOHandlerImpl::~ParallelHDF5IOHandlerImpl() |
|
0 commit comments