mirror of
https://github.com/HDFGroup/hdf5.git
synced 2026-09-25 04:09:44 +03:00
* Consolidate documentation under doc/ directory
Move user-facing guides from release_docs/ and doxygen/ into a single
doc/ root. release_docs/ now holds only release artifacts (changelogs,
history, release process, maintainer info).
- git mv release_docs/INSTALL*.md, USING_*.md, README_HPC.md,
BuildSystemNotes.md, AutotoolsToCMakeOptions.md,
HDF5_Library_2.0.0_Migration_Guide.md → doc/
- git mv doxygen/ → doc/doxygen/
- Update CMakeLists.txt: HDF5_DOXYGEN_DIR and add_subdirectory path
- Update CMakeInstallation.cmake: all install paths for moved files
- Update bin/make_vers: hardcoded doxygen/ path substitution
- Update doc/doxygen/CMakeLists.txt: EXAMPLES_DIRECTORY and comments
- Update README.md, CONTRIBUTING.md, SECURITY.md, config/README.md,
release_docs/RELEASE_PROCESS.md: links to moved files
- Update doxygen .dox files: release_docs/ URLs for moved guides
- Rewrite release_docs/README.md for narrowed scope
* Add HDF5_DOCS_DIR variable for doc/ root path
Introduce HDF5_DOCS_DIR = \${HDF5_SOURCE_DIR}/doc so that
CMakeInstallation.cmake and future callers reference the doc/
directory symbolically rather than by hardcoded path.
HDF5_DOXYGEN_DIR is now derived from HDF5_DOCS_DIR.
654 lines
41 KiB
Plaintext
654 lines
41 KiB
Plaintext
/** \page improve_compressed_perf Improving I/O Performance When Working with HDF5 Compressed Datasets
|
||
*
|
||
* Navigate back: \ref index "Main" / \ref TN
|
||
* <hr>
|
||
*
|
||
* Internal compression is one of several powerful HDF5 features that distinguish HDF5
|
||
* from other binary formats and make it very attractive for storing and organizing data.
|
||
* Internal HDF5 compression saves storage space and I/O bandwidth and allows efficient
|
||
* partial access to data. Chunked storage has to be used when HDF5 compression is
|
||
* enabled.
|
||
*
|
||
* Certain combinations of compression, chunked storage, and access pattern may cause
|
||
* I/O performance degradation if used inappropriately, but the HDF5 Library provides
|
||
* tuning parameters to achieve I/O performance comparable with the I/O performance on
|
||
* raw data that uses contiguous storage.
|
||
*
|
||
* In this paper, we discuss the factors that should be considered when storing
|
||
* compressed data in HDF5 files and how to tune those parameters to optimize the I/O
|
||
* performance of an HDF5 application when working with compressed datasets.
|
||
*
|
||
* <ul>
|
||
* <li>@ref sec_improve_compressed_perf_intro</li>
|
||
* <li>@ref sec_improve_compressed_perf_case</li>
|
||
* <li>@ref sec_improve_compressed_perf_chunk</li>
|
||
* <li>@ref sec_improve_compressed_perf_tune</li>
|
||
* <li>@ref sec_improve_compressed_perf_rec</li>
|
||
* </ul>
|
||
*
|
||
* \section sec_improve_compressed_perf_intro Introduction
|
||
* One of the most powerful features of HDF5 is its ability to store and modify compressed data. The HDF5
|
||
* Library comes with two pre-defined compression methods, GNU \b zip or \b gzip and \b szip or \b libaec, and has
|
||
* the capability to use third-party compression methods, \ref subsubsec_dataset_filters_dyn. The variety of available compression
|
||
* methods means users can choose the compression method that is best suited for achieving the desired
|
||
* balance between the CPU time needed to compress or un-compress data and storage performance.
|
||
*
|
||
* Compressed data is stored in a data array of an HDF5 dataset using a chunked storage mechanism.
|
||
* When chunked storage is used, the data array is split into equally sized chunks each of which is stored
|
||
* separately in the file.
|
||
* <table>
|
||
* <caption>Data array is logically split into equally sized chunks each of which is stored separately in the file</caption>
|
||
* <tr><td>
|
||
* \image html improve_perf-compress_fig_1.png "Figure 1"
|
||
* </td></tr>
|
||
* </table>
|
||
*
|
||
* Compression is applied to each individual chunk. When an I/O operation is performed on a subset of the
|
||
* data array, only chunks that include data from the subset participate in I/O and need to be
|
||
* uncompressed or compressed.
|
||
* <table>
|
||
* <caption>Library will only read highlighted chunks when reading selected columns</caption>
|
||
* <tr><td>
|
||
* \image html improve_perf-compress_fig_2.png "Figure 2"
|
||
* </td></tr>
|
||
* </table>
|
||
*
|
||
* Chunked storage also enables adding more data to a dataset without rewriting the whole dataset. Figure
|
||
* 3 below shows more rows and columns added to a data array stored in HDF5 by writing highlighted
|
||
* chunks that contain new data.
|
||
* <table>
|
||
* <caption>More rows and columns were added to the dataset</caption>
|
||
* <tr><td>
|
||
* \image html improve_perf-compress_fig_3.png "Figure 3"
|
||
* </td></tr>
|
||
* </table>
|
||
*
|
||
* While HDF5 chunk storage and compression obviously provide great benefits in working with data, many
|
||
* HDF5 users have found that sometimes I/O performance is slower for compressed data than for
|
||
* uncompressed data. For example, as we show in this paper, there may be a huge performance
|
||
* difference between an application reading compressed data and reading the same data that was not
|
||
* compressed. For an application that writes compressed data, I/O performance may be excellent, but
|
||
* when data is moved to another system and read back, I/O performance drastically drops making data
|
||
* virtually unusable.
|
||
*
|
||
* Many of these cases of drastically slower reading performance can be ameliorated by more careful
|
||
* consideration of avoiding chunking arrangements that may cause poor reading performance when
|
||
* creating datasets or by a few simple changes to the application reading the data. In this paper, we will
|
||
* discuss the factors that should be considered when storing compressed data in HDF5 files and when
|
||
* tuning an HDF5 application that writes or reads compressed data. We assume that the reader knows
|
||
* HDF5 \ref LearnBasics and would like to learn a set of performance tuning techniques when working with
|
||
* compressed data.
|
||
*
|
||
* In our discussion, we use an HD5 file with Cross-track Infrared Sounder (CriS) data from the Suomi NPP
|
||
* satellite to illustrate several performance tuning techniques for HDF5 applications. The paper is
|
||
* organized as follows:
|
||
* \li The structure of the file and the properties of the datasets are discussed in the
|
||
\ref sec_improve_compressed_perf_case section.
|
||
* \li In the \ref sec_improve_compressed_perf_chunk section, we review HDF5 chunking and
|
||
* compression features in more detail.
|
||
* \li In the \ref sec_improve_compressed_perf_tune section, we discuss the performance tuning approach.
|
||
* \li The \ref sec_improve_compressed_perf_rec section summarizes our recommendations.
|
||
*
|
||
* In the near future, we intend to make available a new CCP (Chunking and Compression Performance)
|
||
* tool. This tool will allow users to vary access patterns, chunk sizes, compression method, and cache
|
||
* settings using the tool’s command options, reducing the need to create and compile test programs such
|
||
* as those used in the “Case Study” section on page 7.
|
||
*
|
||
* For more information on other things that can affect performance, see the “Things That Can Affect
|
||
* Performance” page in the FAQ on the website.
|
||
*
|
||
* \section sec_improve_compressed_perf_case Case Study
|
||
* We will use two HDF5 files to compare I/O performance and to illustrate the issues users may encounter
|
||
* when working with compressed data. These HDF5 files and the application programs used to read them
|
||
* can be downloaded [ 7 ] by readers wishing to reproduce the performance results discussed in this
|
||
* paper.<sup>1</sup>
|
||
* \li <sup>1</sup>:Performance results provided in the paper are intended to show the difference in
|
||
* performance when different HDF5 parameters are used. The reader should be aware that the numbers
|
||
* on his/her system would differ from those provided in the paper, but the effect of the HDF5
|
||
* parameters should be the same.
|
||
*
|
||
* <code>SCRIS_npp_d20140522_t0754579_e0802557_b13293_c20140522142425734814_noaa_pop.h5</code> is the
|
||
* first file we will use. It is an original data file with Cross-track Infrared Sounder (CriS) data from the
|
||
* Suomi NPP satellite. For brevity, we will refer to this file in this document as <code>File.h5</code>.
|
||
*
|
||
* The second file is <code>gz6_SCRIS_npp_d20140522_t0754579_e0802557_b13293__noaa_pop.h5</code>. We
|
||
* will refer to this file as <code>File_with_compression.h5</code>. The file was created from
|
||
* <code>File.h5</code> by the \ref sec_cltools_h5repack tool that applied the \b gzip compression
|
||
* to all datasets using level 6 effort. Repacking <code>File.h5</code> using \b gzip compression
|
||
* reduced the storage space by 1.3 times. We will use the file to demonstrate the most common issues
|
||
* HDF5 users encounter when working with compressed data in HDF5.
|
||
*
|
||
* We selected these files because they have characteristics that would be the first ones to look at when
|
||
* tuning I/O performance of both writing and reading HDF5 applications. First, this data file represents
|
||
* files generated on a big-endian system that is usually not available to general users of the data. The data
|
||
* provider used the HDF5 parameters to minimize storage space for data and to maximize write speed
|
||
* that were not necessarily the optimum parameters for the systems where the data would be read.
|
||
* Second, the users’ applications read data in a way that was optimized for scientific data analysis but not
|
||
* optimal for the HDF5 I/O performance. We will use the files to show what the users can do to improve
|
||
* performance of their applications, and which factors data providers should consider before creating
|
||
* data products.
|
||
*
|
||
* In our case study, we used a 4-dimensional array of 32-bit big-endian floating point numbers stored in
|
||
* the HDF5 dataset <code>/All_Data/CrIS-SDR_All/ES_ImaginaryLW</code> in both files. The data array is
|
||
* extensible and has the current dimension sizes 60x30x9x717. When compressed with \b gzip compression
|
||
* with level 6, the compression ratio is 1.076<sup>2</sup>. We used HDF5 command line tools
|
||
* \ref sec_cltools_h5dump and \ref sec_cltools_h5ls
|
||
* and the HDF Java-based browser HDFView to find various properties of the dataset that would help us to
|
||
* understand performance problems and propose solutions. If the reader decides to follow the discussion
|
||
* using a “hands on” approach, the examples below illustrate how to use \ref sec_cltools_h5dump and
|
||
* \ref sec_cltools_h5ls to get the
|
||
* characteristics of the <code>/All_Data/CrIS-SDR_All/ES_ImaginaryLW</code> dataset.
|
||
* \li <sup>2</sup>:The ratio itself is not a subject of this paper, but the fact that the dataset was
|
||
* compressed is. It is one of the factors that affected the performance. While the total compression
|
||
* ratio for the file is 1.3, one should be careful about applying the same compression to all datasets
|
||
* in a file. For some datasets, compression will not significantly reduce storage space while requiring
|
||
* extra I/O time for decompression as this example shows.
|
||
*
|
||
* The \ref sec_cltools_h5dump command line below will yield the results shown in Figure 4 below:
|
||
* \code
|
||
* % h5dump -H -d /All_Data/CrIS-SDR_All/ES_ImaginaryLW File_with_compression.h5
|
||
*
|
||
* HDF5 "gz6_SCRIS_npp_d20140522_t0754579_e0802557_b13293__noaa_pop.h5" {
|
||
* DATASET "/All_Data/CrIS-SDR_All/ES_ImaginaryLW" {
|
||
* DATATYPE H5T_IEEE_F32BE
|
||
* DATASPACE SIMPLE { ( 60, 30, 9, 717 ) / ( H5S_UNLIMITED,
|
||
* H5S_UNLIMITED, H5S_UNLIMITED, H5S_UNLIMITED ) }
|
||
* STORAGE_LAYOUT {
|
||
* CHUNKED ( 4, 30, 9, 717 )
|
||
* SIZE 43162046 (1.076:1 COMPRESSION)
|
||
* }
|
||
* FILTERS {
|
||
* COMPRESSION DEFLATE { LEVEL 6 }
|
||
* }
|
||
* FILLVALUE {
|
||
* FILL_TIME H5D_FILL_TIME_IFSET
|
||
* VALUE -999.3
|
||
* }
|
||
* ALLOCATION_TIME {
|
||
* H5D_ALLOC_TIME_INCR
|
||
* }
|
||
* }
|
||
* }
|
||
* \endcode
|
||
* <em>Figure 4: Output of the h5dump command that shows properties of the dataset /All_Data/CrIS-SDR_All/ES_ImaginaryLW</em>
|
||
*
|
||
* The \ref sec_cltools_h5ls command line below will yield the results shown in Figure 5 below:
|
||
* \code
|
||
* % h5ls -lrv gz6_SCRIS_npp_d20140522_t0754579_e0802557_b13293__noaa_pop.h5
|
||
*
|
||
* /All_Data/CrIS-SDR_All/ES_ImaginaryLW Dataset {60/Inf, 30/Inf, 9/Inf,
|
||
* 717/Inf}
|
||
* Location: 1:60464
|
||
* Links: 1
|
||
* Chunks: {4, 30, 9, 717} 3097440 bytes
|
||
* Storage: 46461600 logical bytes, 43162046 allocated bytes, 107.64%
|
||
* utilization
|
||
* Filter-0: deflate-1 OPT {6}
|
||
* Type: IEEE 32-bit big-endian float
|
||
* \endcode
|
||
* <em>Figure 5: Output of the h5ls command that shows properties of the dataset /All_Data/CrIS-SDR_All/ES_ImaginaryLW</em>
|
||
*
|
||
* In HDFView, right click on the dataset to choose “Show Properties” option from the drop-down menu.
|
||
* The properties will appear in the new window as shown in Figure 6.
|
||
* <table>
|
||
* <caption>HDFView window with information about the dataset</caption>
|
||
* <tr><td>
|
||
* \image html improve_perf-compress_fig_6.png "Figure 6"
|
||
* </td></tr>
|
||
* </table>
|
||
*
|
||
* Our application read the dataset along the fastest changing dimension, 717 elements at a time from the
|
||
* dataset in both files. In the 2-dimensional case, this would correspond to reading an array by “row”.
|
||
* There were 16,200 reads to get all of the data. What we found was a several orders of magnitude drop
|
||
* in the performance when data was read from the compressed dataset as shown in Table 1.
|
||
* <table>
|
||
* <caption>Table 1: Reading by 1x1x717 hyperslab (or “rows”) from original and compressed datasets.
|
||
* Performance drops more than 3000 times.</caption>
|
||
* <tr><th>File Name</th><td>File.h5</td><td>File_with_compression.h5 (gzip level 6)</td></tr>
|
||
* <tr><th>Read Time</th><td>0.1 seconds</td><td>0.37 seconds</td></tr>
|
||
* </table>
|
||
*
|
||
* We experimented with the HDF5 parameters such cache size and chunk size and modified our
|
||
* application to use different access patterns. The details of the experiments and achieved results will be
|
||
* discussed in the \ref sec_improve_compressed_perf_tune section. Here we provide the results just to show
|
||
* the difference in the read performance the change in the parameters made.
|
||
*
|
||
* The table below, Table 2, shows the result of reading data as in the example above with the difference
|
||
* that the application used a chunked cache size of 3MB instead of the default 1MB. Reading performance
|
||
* from the compressed dataset was only 4 times slower than for reading the uncompressed data.
|
||
* <table>
|
||
* <caption>Table 2: Reading by 1x1x717 hyperslab (or “rows”) from original and compressed datasets. Changing
|
||
* the chunk cache size from 1MB to 3MB improved application performance by a factor of 1000.</caption>
|
||
* <tr><th>File Name</th><td>File.h5</td><td>File_with_compression.h5 (gzip level 6)</td></tr>
|
||
* <tr><th>Read Time</th><td>0.1 seconds</td><td>0.37 seconds</td></tr>
|
||
* </table>
|
||
*
|
||
* We also experimented with a different access pattern to read data from both files. Instead of reading
|
||
* 717 elements at a time, we read a contiguous HDF5 hyperslab with dimensions 4x30x9x717. The reader
|
||
* who knows about HDF5 chunking will immediately recognize that we read one chunk at a time, a total
|
||
* 15 of them. With this change, reading from the non-compressed dataset was only 10 times better than
|
||
* reading from the compressed dataset; see Table 3 below and compare with the results in Table 1.
|
||
* <table>
|
||
* <caption>Table 3: Reading by 4x30x9x717 hyperslabs from original and compressed datasets. Performance for
|
||
* compressed dataset is several orders of magnitude better than the result in Table 1 and comparable
|
||
* to the result in Table 2.</caption>
|
||
* <tr><th>File Name</th><td>File.h5</td><td>File_with_compression.h5 (gzip level 6)</td></tr>
|
||
* <tr><th>Read Time</th><td>0.04 seconds</td><td>0.36 seconds</td></tr>
|
||
* </table>
|
||
*
|
||
* In our last experiment, we repacked both files with \ref sec_cltools_h5repack to use a chunk size of 1x30x9x717, 4
|
||
* times smaller than the original chunks, and read the file by using the original access pattern of
|
||
* 1x1x1x717 hyperslab (by “row”). The result is shown below in Table 4. Once again, we got much better
|
||
* performance than shown in Table 1, even when considering the time to repack the file with \ref sec_cltools_h5repack.
|
||
* <table>
|
||
* <caption>Table 4: Reading by 1x1x1x717 hyperslab (by “row”) from non-compressed and compressed datasets;
|
||
* a smaller chunk size of 1x30x9x717 was used to store data in both files. Performance for the
|
||
* compressed dataset is comparable to the result in Table 2 and Table 3.</caption>
|
||
* <tr><th>File Name</th><td>File.h5</td><td>File_with_compression-small-chunk.h5 (gzip level 6)</td></tr>
|
||
* <tr><th>Read Time</th><td>0.08 seconds</td><td>0.36 seconds</td></tr>
|
||
* <tr><th>Repack Time</th><td>3 seconds</td><td>12 seconds</td></tr>
|
||
* </table>
|
||
* \li Note that the read and repack times in the tables above are approximate values.
|
||
*
|
||
* \section sec_improve_compressed_perf_chunk Chunking and Compression in HDF5
|
||
* In this section we will give a brief overview of the chunking and compression features needed to follow
|
||
* the approach presented later in the “Tuning for Performance” section on page 15. For more information
|
||
* on HDF5 chunking, see the \ref hdf5_chunking document.
|
||
*
|
||
* \subsection subsec_improve_compressed_perf_chunk_chunk Chunking in HDF5
|
||
* Data of HDF5 dataset can be stored in several different ways in HDF5 file. See the
|
||
* \ref subsubsec_dataset_program_transfer
|
||
* section in the \ref sec_dataset chapter in the \ref UG for more information.
|
||
*
|
||
* The default storage layout of HDF5 files is contiguous storage: data of a multidimensional array is
|
||
* serialized (or flattened) along the fastest changing dimension and is stored as a contiguous block in the
|
||
* file. This storage mechanism is recommended if the size of a dataset is known and the storage size for
|
||
* the dataset is acceptable to the user: in other words, no data compression is desired. The contiguous
|
||
* storage is efficient for I/O if a whole HDF5 dataset is accessed or if a contiguous subset (as stored in the
|
||
* file) of an HDF5 dataset is accessed. The figure below shows an example with a row of a 2-dimensional
|
||
* array stored in an HDF5 dataset by a C application. In this case, the HDF5 Library seeks to the start
|
||
* position in the file and writes/reads the required number of bytes.
|
||
* <table>
|
||
* <caption>Elements of the rows of the 6x9 two-dimensional array are stored contiguously in the file while elements of the columns are not</caption>
|
||
* <tr><td>
|
||
* \image html improve_perf-compress_fig_7.png "Figure 7"
|
||
* </td></tr>
|
||
* </table>
|
||
*
|
||
* If we change the access pattern to accessing the dataset by columns instead of by rows, the contiguous
|
||
* layout may not work well. The column’s elements are not stored contiguously in the file (see Figure 8).
|
||
* Accessing a column will require several seeks to find the data in the file and multiple reads/writes of one
|
||
* element at a time. Seeks and small size I/O operations may affect performance especially for large datasets.
|
||
* Obviously, contiguous storage is not as favorable for a column access pattern as it is for a row
|
||
* access pattern, and other storage options may be more beneficial.
|
||
* <table>
|
||
* <caption>Elements of the column are not stored contiguously in the file</caption>
|
||
* <tr><td>
|
||
* \image html improve_perf-compress_fig_8.png "Figure 8"
|
||
* </td></tr>
|
||
* </table>
|
||
*
|
||
* An alternative is chunked storage (a chunked storage layout). When chunked storage is used, a
|
||
* multidimensional array is logically divided into equally sized chunks. For example, Figure 9 below shows
|
||
* the 6x9 array divided into 6 3x3 chunks. Chunked storage layout and chunk sizes (number of elements in
|
||
* a chunk along each dataset dimension) are specified at dataset creation time and cannot be changed
|
||
* without rewriting the dataset. Chunked storage is \b required if data will be added to an HDF5 dataset and
|
||
* <b>the maximum size of the dataset is unknown</b> at creation time (see Figure 3). Chunked storage is also
|
||
* \b required if data will be stored \b compressed.
|
||
*
|
||
* The logical chunk is stored as a contiguous block in the file (compare with the contiguous storage when
|
||
* the whole data array is stored contiguously in the file). When compression is used, it is applied to each
|
||
* chunk separately. During the I/O operation each chunk is accessed as a whole when the HDF5 Library
|
||
* reads or writes data elements stored in the chunk. For example, two chunks will be read (and
|
||
* uncompressed if needed) when accessing the 2<sup>nd</sup> column as shown in Figure 9.
|
||
*
|
||
* <b>The chunk size is an important factor in achieving good I/O and storage performance</b>.
|
||
*
|
||
* If the chunk size is too small, I/O performance degrades due to small reads/writes when a chunk is
|
||
* accessed. Storing a large number of small chunks increases the size of the internal HDF5 data structures
|
||
* needed to track the positions and sizes of chunks in the file, creating excessive storage overhead.
|
||
*
|
||
* On the other hand, if the chunk size is too big and compression is used, I/O performance may degrade
|
||
* with unsuitable combinations of access patterns and chunk cache sizes or on systems that do not have
|
||
* enough memory to compress or to uncompress chunks. For instance, an application that reads data by
|
||
* row from a chunk too large to fit in the configured cache will cause decompression of the entire chunk
|
||
* for each row that is read, resulting in a great deal of unnecessarily repeated disk reads and
|
||
* decompression processing.
|
||
*
|
||
* As was mentioned above, the storage layout cannot be changed after the dataset has been created. If
|
||
* desired, one can use the \ref sec_cltools_h5repack tool to modify the storage layout of a copy of a dataset; for
|
||
* example, the tool can be used to change the size of the chunk, to remove compression and store the
|
||
* dataset using contiguous storage, or to apply a different compression method. If data is read from the
|
||
* file many times, it may be much more efficient to rewrite the file using \ref sec_cltools_h5repack with the more
|
||
* appropriate storage parameters for reading, than to read data from the original file with an unfavorable
|
||
* compression and chunking arrangement.
|
||
* <table>
|
||
* <caption>Each chunk is stored separately in the HDF5 file. Two chunks will be read by HDF5 to access the 2nd column of the array</caption>
|
||
* <tr><td>
|
||
* \image html improve_perf-compress_fig_9.png "Figure 9"
|
||
* </td></tr>
|
||
* </table>
|
||
*
|
||
* <b>Another important aspect of HDF5 chunking is the chunk cache</b>.
|
||
*
|
||
* HDF5 does not cache raw data unless chunked storage is used. When data is accessed for a chunked
|
||
* dataset, the chunks that contain the requested data are brought to the cache one by one and stay in
|
||
* cache until they are evicted. If a chunk is cached, then reading or writing data stored in the chunk does
|
||
* not require disk accesses. In other words, chunk caching helps when the same chunk is accessed
|
||
* multiple times during I/O operations.
|
||
*
|
||
* The HDF5 Library provides the #H5Pset_cache and #H5Pset_chunk_cache functions to control the
|
||
* size of the chunk cache and the chunk eviction policy to specify the appropriate cache parameters for a
|
||
* particular access pattern.
|
||
*
|
||
* As will be shown in the \ref sec_improve_compressed_perf_tune section, chunked storage and chunk cache
|
||
* parameters affect I/O performance and should be chosen with care depending on the I/O access
|
||
* pattern.
|
||
*
|
||
* \subsection subsec_improve_compressed_perf_chunk_comp Compression in HDF5
|
||
* As it was mentioned in the previous sections, in HDF5 data can be stored compressed. The HDF5 Library
|
||
* comes with the built-in compression methods:
|
||
* \snippet{doc} H5Zmodule.h PreDefFilters
|
||
* One can also build in a custom filter, \ref subsec_dataset_filters, or use \ref subsubsec_dataset_filters_dyn.
|
||
*
|
||
* The compression method is chosen at a dataset creation time and cannot be changed later. As with the
|
||
* chunked layout, one can use \ref sec_cltools_h5repack to rewrite the dataset in a copy of the dataset using a different
|
||
* compression method or to remove compression completely.
|
||
*
|
||
* HDF5 tools such \ref sec_cltools_h5dump and \ref sec_cltools_h5ls can be used to check the efficiency of the compression. For
|
||
* example, both \ref sec_cltools_h5dump and \ref sec_cltools_h5ls show the compression ratio for a dataset. The compression ratio is
|
||
* defined as a ratio of the original data size to the size of compressed data. For example, the ratio for the
|
||
* dataset <code>/All_Data/CrIS-SDR_All/ES_ImaginaryLW</code> is 1.07 (see Figure 4) meaning that there was
|
||
* not much benefit in applying compression to save space in the file. For more information, see the \ref CompTS
|
||
* technical note for a discussion of compression efficiency.
|
||
*
|
||
* The HDF5 Library applies compression encoding or decoding when the chunk is moved between the
|
||
* chunk cache and the file. Since compression encoding and decoding takes CPU time, it affects HDF5
|
||
* write and read performance. This is especially true when data is read or written many times from the
|
||
* same chunk and the chunk is not cached between the accesses; this means the chunk has to be brought
|
||
* from disk every time it is accessed.
|
||
*
|
||
* In the next section we will see the effect of compression on the I/O performance.
|
||
*
|
||
* \section sec_improve_compressed_perf_tune Tuning for Performance
|
||
* In this section we will discuss several strategies one can apply to get better I/O performance. We will
|
||
* explain in detail how a particular strategy works and when it should be applied. While the examples
|
||
* below focus on reading only, the same approach will work for writing too.
|
||
*
|
||
* The strategies for improving performance require modifications to the reading application or to the
|
||
* HDF5 file itself. The reader should choose the strategies that are appropriate for a particular use case.
|
||
*
|
||
* \subsection subsec_improve_compressed_perf_tune_cache Adjust Chunk Cache Size
|
||
* The HDF5 Library automatically creates a chunk cache for each opened chunked dataset. The first
|
||
* strategy is to check whether the current chunk cache settings work properly with the application access
|
||
* pattern and reset the chunk cache parameters as appropriate.
|
||
*
|
||
* The HDF5 Library provides two functions, #H5Pset_cache and #H5Pset_chunk_cache, to control
|
||
* chunk cache settings. #H5Pset_cache controls the chunk cache setting for ALL datasets in the file, and
|
||
* #H5Pset_chunk_cache controls the chunk cache settings per dataset. To find out the default or current
|
||
* settings, use the #H5Pget_cache or #H5Pget_chunk_cache functions and then reset appropriate
|
||
* parameters if necessary. See the \ref subsubsec_improve_compressed_perf_tune_cache_how section for more
|
||
* information.
|
||
*
|
||
* The default size of the cache is 1MB. The size can be modified by setting the \b nbytes parameter in
|
||
* #H5Pset_cache and #H5Pset_chunk_cache. Several chunks can be held in the cache if their total size
|
||
* in bytes is less or equal to 1MB.
|
||
*
|
||
* To look up a chunk in cache, the HDF5 Library uses an array of pointers to the chunks (hash table). The
|
||
* array has \b nslots elements (or slots in the hash table) with a default value of 511. One can use the
|
||
* \b nslots parameter in #H5Pset_cache and #H5Pset_chunk_cache to change the default size of the
|
||
* hash table.
|
||
*
|
||
* Each chunk has an associated hash value that is calculated as follows. All chunks of the dataset have an
|
||
* index (\b cindex) in a linear array of chunks. For example, chunks in Figure 9 will have indices from 0 to 5,
|
||
* with the upper left chunk having index 0, the middle one in the top row having index 1, and the lower
|
||
* right chunk having index 5. The hash value is calculated as the remainder of dividing \b cindex by \b nslots
|
||
* (known as a modulo operation <b>cindex mod nslots</b>). The hash table can contain only one chunk with
|
||
* the same hash value. This fact is important to remember to avoid situations when the needed chunks
|
||
* have the same hash value. For example, let’s assume \b nslots is 3. Then in Figure 9 the chunks with the
|
||
* indices 0 and 3 (in other words, the chunks that contain the first three columns) have the same hash
|
||
* values and cannot be in the chunk cache simultaneously even though their total sizes are less than 1MB.
|
||
*
|
||
* Now, we can analyze what happens when data is read by “rows” (contiguous 717 elements) from the
|
||
* <code>/All_Data/CrIS-SDR_All/ES_ImaginaryLW</code> dataset and the default chunk cache settings are
|
||
* used. The number of slots \b nslots in the hash table is not a concern since the default value is 511 and
|
||
* we have only 15 chunks. Now let’s analyze how the chunk cache size affects the performance.
|
||
*
|
||
* Each row is stored in one of the 15 chunks that comprise the dataset. Each chunk has 4x30x9 or 1,080
|
||
* “rows”. To read the first row of the chunk, the whole chunk is read, uncompressed and the row is copied
|
||
* to the application buffer by the HDF5 Library. Since the size of the uncompressed chunk is 2.95 MB, the
|
||
* cache cannot hold the chunk. When the second row is read, the process repeats until all rows from the
|
||
* same chunk are read. Thus, the chunk will be read and uncompressed 1,080 times. When we increase
|
||
* the cache size to 3MB, the chunk stays in the cache and all rows can be copied to the application buffer
|
||
* without the HDF5 Library fetching data from disk and uncompressing the chunk every time the chunk is
|
||
* accessed.
|
||
*
|
||
* Since all 15 chunks have to be read, the HDF5 Library will be touching the disk 16,200 times when a 1MB
|
||
* size cache is used compared with 15 times when a 3MB cache is used. The first column in Table 5 below
|
||
* shows that it took 345 seconds to read a compressed dataset when using the default cache size of 1MB
|
||
* while it took only 0.37 seconds to read the dataset when using the chunk cache size of 3MB. We see
|
||
* several orders of magnitude performance improvements when we increase chunk cache size to 3MB.
|
||
* <table>
|
||
* <caption>Table 5: Performance improved when the chunk cache size was adjusted to 3MB by several orders of
|
||
* magnitude.</caption>
|
||
* <tr><th>File Name</th><td>File_with_compression.h5</td><td>File_with_compression.h5</td></tr>
|
||
* <tr><th>Cache Size</th><td>1MB (default)</td><td>3MB</td></tr>
|
||
* <tr><th>Read Time</th><td>345 seconds</td><td>0.37 seconds</td></tr>
|
||
* </table>
|
||
*
|
||
* As shown in Table 6 below, the reading performance with the 3MB cache size is comparable to the
|
||
* reading performance of the data stored without compression applied. Please notice that the chunk
|
||
* cache size did not affect the reading performance for the uncompressed data.
|
||
* <table>
|
||
* <caption>Table 6: With the chunk cache size adjusted to 3MB, performance is comparable with the
|
||
* performance of reading data that was stored without compression.</caption>
|
||
* <tr><th>File Name</th><td>File_with_compression.h5</td><td>File.h5</td></tr>
|
||
* <tr><th>Cache Size</th><td>3MB</td><td>1MB or 3MB</td></tr>
|
||
* <tr><th>Read Time</th><td>0.37 seconds</td><td>0.1 seconds</td></tr>
|
||
* </table>
|
||
* \li Note that the read times in the tables above are approximate values.
|
||
*
|
||
* \subsubsection subsubsec_improve_compressed_perf_tune_cache_how How to Adjust the Chunk Cache Size
|
||
* As was mentioned above, an application can adjust the chunk cache size by calling either
|
||
* #H5Pset_cache or #H5Pset_chunk_cache functions. #H5Pset_cache sets the chunk cache size for all
|
||
* chunked datasets in a file, and #H5Pset_chunk_cache sets the chunk cache size for a particular
|
||
* dataset.
|
||
*
|
||
* The programming model for using both functions is the following:
|
||
* \li Use #H5Pget_cache or #H5Pget_chunk_cache to retrieve the default parameters set by the
|
||
* library or by a previous call to the function.
|
||
* \li Use #H5Pset_cache or #H5Pset_chunk_cache to modify a subset of the parameters.
|
||
*
|
||
* Below are the code snippets that show the usage.
|
||
*
|
||
* The first example below shows how to change the cache size for all datasets in the file using
|
||
* #H5Pset_cache. Since the function sets a global setting for the file, it uses a file access property list
|
||
* identifier to modify the cache size. #H5Pget_cache is called first to retrieve default cache settings that
|
||
* will be modified by #H5Pset_cache. In the example below, every chunked dataset will have a cache size
|
||
* of 3MB. To overwrite this setting for a particular dataset one can use #H5Pset_chunk_cache as shown
|
||
* in the second example.
|
||
* <table>
|
||
* <tr><th>Code Example 1: Using H5Pset_cache to change the cache size for all datasets.</th></tr>
|
||
* </table>
|
||
* \code
|
||
* hid_t fapl; // File access property identifier
|
||
* int nelemts; // Dummy parameter in API, no longer used
|
||
* size_t nslots; // Number of slots in the hash table
|
||
* size_t nbytes; // Size of chunk cache in bytes
|
||
* double w0; // Chunk preemption policy
|
||
* ……
|
||
* fapl = H5Pcreate (H5P_FILE_ACCESS);
|
||
* // Retrieve default cache parameters
|
||
* H5Pget_cache(fapl, &nelemts, &nslots, &nbytes, &w0)
|
||
* // Set cache size to 3MBs and instruct the cache to discard the fully read chunk
|
||
* nbytes = 3 * 1024 * 1024;
|
||
* w0 = 1.
|
||
* H5Pset_cache(fapl, nelemts, nslots, nbytes, w0);
|
||
* fid = H5Fopen (file, H5F_ACC_RDONLY, fapl);
|
||
* H5Dopen2 (fid, “/All_Data/CrIS-SDR_All/ES_ImaginaryLW”, H5P_DEAFULT);
|
||
* ……
|
||
* \endcode
|
||
*
|
||
* The second example, see below, shows how to set at dataset creation time the chunk cache size for the
|
||
* <code>/All_Data/CrIS-SDR_All/ES_ImaginaryLW</code> dataset. The cache sizes for other datasets will not be
|
||
* modified.
|
||
* <table>
|
||
* <tr><th>Code Example 2 : Using H5Pset_chunk_cache to change one dataset.</th></tr>
|
||
* </table>
|
||
* \code
|
||
* hid_t dapl; // File access property identifier
|
||
* size_t nslots; // Number of slots in the hash table
|
||
* size_t nbytes; // Size of chunk cache in bytes
|
||
* double w0; // Chunk preemption policy
|
||
* ……
|
||
* dapl = H5Pcreate (H5P_DATASET_ACCESS);
|
||
* // Retrieve default cache parameters
|
||
* H5Pget_chunk_cache(dapl, &nslots, &nbytes, &w0)
|
||
* // Set cache size to 3MBs and instruct the cache to discard the fully read chunk
|
||
* nbytes = 3 * 1024 * 1024;
|
||
* w0 = 1.
|
||
* H5Pset_chunk_cache(dapl, nslots, nbytes, w0);
|
||
* H5Dopen2 (fid, “/All_Data/CrIS-SDR_All/ES_ImaginaryLW”, dapl);
|
||
* ……
|
||
* \endcode
|
||
* As we will see in the next section, care needs to be taken when working with chunked datasets and
|
||
* setting chunk cache sizes: an application’s memory footprint can be significantly affected.
|
||
*
|
||
* \subsubsection subsubsec_improve_compressed_perf_tune_cache_mem Chunk Cache Size and Application Memory
|
||
* A chunk cache is allocated for a dataset when the first I/O operation is performed. The chunk cache is
|
||
* discarded after the dataset is closed. If an application performs I/O on several datasets, memory
|
||
* consumed by an application increases by the total size of all chunk caches. One can also see an increase
|
||
* in the metadata cache size.
|
||
*
|
||
* If memory consumption is a concern, it is recommended that I/O be done on a few datasets at a time
|
||
* and to close the few datasets after I/O operation has been completed. As we will see in the next
|
||
* sections, there are access patterns that cannot take advantage of a chunk cache at all. If this is the case,
|
||
* the application can disable a chunk cache completely and thus reduce the memory footprint. To disable
|
||
* a chunk cache, use 0 for the value of the \b nbytes parameter in the calls to #H5Pset_cache or
|
||
* #H5Pset_chunk_cache.
|
||
*
|
||
* \subsection subsec_improve_compressed_perf_tune_access Change the Access Pattern
|
||
* When changing the chunk cache size is not an option (for example, there is no access to the program
|
||
* source code), one can consider a reading strategy that will minimize the effect of the chunk cache size.
|
||
* The strategy is to read as much data as possible in each read operation.
|
||
*
|
||
* As we mentioned before, the HDF5 Library performs I/O on the whole chunk. The chunk is read,
|
||
* uncompressed, and the requested data is copied to the application buffer. If in one read call the
|
||
* application requests all data in a chunk, then obviously chunk caching (and chunk cache size) is
|
||
* irrelevant since there is no need to access the same chunk again.
|
||
*
|
||
* In our case, suppose the application reads the selection that corresponds to the whole chunk. In other
|
||
* words, if a hyperslab with dimensions 4x30x9x717 is used instead of a hyperslab with dimensions
|
||
* 1x1x1x717, then the HDF5 Library would perform only 15 reading and decoding operations instead of
|
||
* 16,200. The significant improvement in performance is shown in Table 7 below. We see a similar I/O
|
||
* performance improvement as in the case when we increased the chunk cache size to 3MB (see Table 5).
|
||
*
|
||
* <table>
|
||
* <caption>Table 7: Leaving the chunk cache size unchanged and changing the access pattern to read more data
|
||
* improves performance by several orders of magnitude.</caption>
|
||
* <tr><th>File Name</th><td>File_with_compression.h5</td><td>File_with_compression.h5</td></tr>
|
||
* <tr><th>Access Pattern</th><td>1x1x1x717</td><td>4x30x9x717</td></tr>
|
||
* <tr><th>Read Time</th><td>345 seconds</td><td>0.36 seconds</td></tr>
|
||
* </table>
|
||
* \li Note that the read times in the table above are approximate values.
|
||
*
|
||
* \subsection subsec_improve_compressed_perf_tune_size Change the Chunk Size
|
||
* Data producers should consider that users who cannot modify applications to increase the chunk cache
|
||
* size or to change the access pattern will not encounter the performance problem described in the
|
||
* \ref subsubsec_improve_compressed_perf_tune_cache_how section if chunks in the file are smaller than 1MB (1x30x9x717 by
|
||
* 4 bytes) because the whole chunk will fit into the chunk cache of the default size. Therefore if data in
|
||
* the HDF5 files is intended for reading by unknown user applications or on systems that might be
|
||
* different from the system where it was written, it is a good idea to consider a chunk size less than 1MB.
|
||
* In this case the applications that use default HDF5 settings will not be penalized.
|
||
*
|
||
* As shown in the \ref sec_improve_compressed_perf_case section, Table 4, the performance of reading by row (717
|
||
* elements) when the chunk size is 1x30x9x717 (total size in bytes is approximately 0.74MB) is
|
||
* comparable to the performance of reading non-compressed data and is similar to the performance for
|
||
* reading compressed data when using a bigger cache size (Table 2) or bigger amount of data (Table 3).
|
||
* The above statement is summarized in the \ref sec_improve_compressed_perf_rec section.
|
||
*
|
||
* For users who encounter datasets with large chunk sizes and with applications that cannot be easily
|
||
* modified: since the chunk size is set at the dataset creation time and cannot be changed later, the only
|
||
* option is to recreate the dataset by using the \ref sec_cltools_h5repack tool to change the storage layout properties.
|
||
* The command below will change the chunk size of the <code>/All_Data/CrIS-SDR_All/ES_ImaginaryLW</code>
|
||
* dataset from 4x30x9x717 to 1x30x9x717 making chunk size in bytes 0.74MB instead of the original
|
||
* 2.96MBs size.
|
||
* \code
|
||
* % h5repack -l /All_Data/CrIS-SDR_All/ES_ImaginaryLW:CHUNK=1x30x9x717
|
||
* gz6_SCRIS_npp_d20140522_t0754579_e0802557_b13293__noaa_pop.h5 new.h5
|
||
* \endcode
|
||
*
|
||
* \section sec_improve_compressed_perf_rec Recommendations
|
||
* This section summarizes the discussion and recommendations for working with files that use the HDF5
|
||
* chunking and compression feature.
|
||
*
|
||
* When compression is enabled for an HDF5 dataset, the library must always read an entire chunk for
|
||
* each call to #H5Dread unless the chunk is already in the cache. To avoid trashing the cache, make sure
|
||
* that the chunk cache size is big enough to hold the whole chunk or that the application reads the whole
|
||
* chunk in one read operation bypassing the chunk cache.
|
||
*
|
||
* When experiencing I/O performance problems with compressed data, find the size of the chunk and try
|
||
* the strategy that is most applicable to your use case:
|
||
* \li Increase the size of the chunk cache to hold the whole chunk.
|
||
* \li Increase the amount of the selected data to read (making selection to be the whole chunk will
|
||
* guarantee bypassing the chunk cache).
|
||
* \li Decrease the chunk size by using \ref sec_cltools_h5repack tool to fit into the default size chunk cache.
|
||
*
|
||
* The results of all three strategies provide similar performance and are summarized in Table 8 below.
|
||
* <table>
|
||
* <caption>Table 8: By varying different parameters (highlighted) one can achieve good I/O performance for
|
||
* reading compressed data.</caption>
|
||
* <tr><th>File Name</th>
|
||
* <td>File_with_compression.h5</td>
|
||
* <td>File_with_compression.h5</td>
|
||
* <td>File_with_compression.h5</td>
|
||
* <td>File_with_compression-small-chunk.h5</td></tr>
|
||
* <tr><th>Cache Size</th>
|
||
* <td>1MB</td>
|
||
* <td>3MB</td>
|
||
* <td>1MB</td>
|
||
* <td>1MB</td></tr>
|
||
* <tr><th>Chunk Size</th>
|
||
* <td>4x30x9x717</td>
|
||
* <td>4x30x9x717</td>
|
||
* <td>4x30x9x717</td>
|
||
* <td>1x30x9x717</td></tr>
|
||
* <tr><th>Access Pattern (Hyperslab Size)</th>
|
||
* <td>1x1x1x717</td>
|
||
* <td>1x1x1x717</td>
|
||
* <td>4x30x9x717</td>
|
||
* <td>1x1x1x717</td></tr>
|
||
* <tr><th>Read Time</th>
|
||
* <td>345 seconds</td>
|
||
* <td>0.37 seconds</td>
|
||
* <td>0.36 seconds</td>
|
||
* <td>0.36 seconds</td></tr>
|
||
* <tr><th>Repack Time</th>
|
||
* <td>NA</td>
|
||
* <td>NA</td>
|
||
* <td>NA</td>
|
||
* <td>12 seconds</td></tr>
|
||
* </table>
|
||
* \li Note that the read and repack times in the table above are approximate values.
|
||
*
|
||
* Please notice that when compression is disabled, the library’s behavior depends on the cache size
|
||
* relative to the chunk size. If the chunk fits the cache, the library reads entire chunk for each call to
|
||
* #H5Dread unless it is in cache already. If the chunk doesn’t fit the cache, the library reads only the data
|
||
* that is selected directly from the file. There will be more read operations, especially if the read plane
|
||
* does not include the fastest changing dimension.
|
||
*
|
||
* One can use \ref sec_cltools_h5repack tool to remove compression by using the following command:
|
||
* \code
|
||
* % h5repack -f /All_Data/CrIS-SDR_All/ES_ImaginaryLW:NONE
|
||
* gz6_SCRIS_npp_d20140522_t0754579_e0802557_b13293__noaa_pop.h5 new.h5
|
||
* \endcode
|
||
*
|
||
* The CCP tool described in the introduction is intended to facilitate optimization of the parameters
|
||
* chosen when creating files and investigation of possible solutions when performance problems are
|
||
* encountered.
|
||
*
|
||
* <hr>
|
||
* Navigate back: \ref index "Main" / \ref TN
|
||
*
|
||
*/
|