/* * Copyright (c) 2016-2019 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall * not be construed as granting a license to any other intellectual * property including but not limited to intellectual property relating * to a hardware implementation of the functionality of the software * licensed hereunder. You may use the software subject to the license * terms below provided that you ensure that this notice is replicated * unmodified and in its entirety in all distributions of the software, * modified or unmodified, in source code or in binary form. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Andreas Sandberg */ #include "base/stats/hdf5.hh" #include "base/logging.hh" #include "base/stats/info.hh" /** * Check if all strings in a container are empty. */ template bool emptyStrings(const T &labels) { for (const auto &s : labels) { if (!s.empty()) return false; } return true; } namespace Stats { Hdf5::Hdf5(const std::string &file, unsigned chunking, bool desc, bool formulas) : fname(file), timeChunk(chunking), enableDescriptions(desc), enableFormula(formulas), dumpCount(0) { // Tell the library not to print exceptions by default. There are // cases where we rely on exceptions to determine if we need to // create a node or if we can just open it. H5::Exception::dontPrint(); } Hdf5::~Hdf5() { } void Hdf5::begin() { h5File = H5::H5File(fname, // Truncate the file if this is the first dump dumpCount > 0 ? H5F_ACC_RDWR : H5F_ACC_TRUNC); path.push(h5File.openGroup("/")); } void Hdf5::end() { assert(valid()); dumpCount++; } bool Hdf5::valid() const { return true; } void Hdf5::beginGroup(const char *name) { auto base = path.top(); // Try to open an existing stat group corresponding to the // name. Create it if it doesn't exist. H5::Group group; try { group = base.openGroup(name); } catch (H5::FileIException e) { group = base.createGroup(name); } catch (H5::GroupIException e) { group = base.createGroup(name); } path.push(group); } void Hdf5::endGroup() { assert(!path.empty()); path.pop(); } void Hdf5::visit(const ScalarInfo &info) { // Since this stat is a scalar, we need 1-dimensional value in the // stat file. The Hdf5::appendStat helper will populate the size // of the first dimension (time). hsize_t fdims[1] = { 0, }; double data[1] = { info.result(), }; appendStat(info, 1, fdims, data); } void Hdf5::visit(const VectorInfo &info) { appendVectorInfo(info); } void Hdf5::visit(const DistInfo &info) { warn_once("HDF5 stat files don't support distributions.\n"); } void Hdf5::visit(const VectorDistInfo &info) { warn_once("HDF5 stat files don't support vector distributions.\n"); } void Hdf5::visit(const Vector2dInfo &info) { // Request a 3-dimensional stat, the first dimension will be // populated by the Hdf5::appendStat() helper. The remaining two // dimensions correspond to the stat instance. hsize_t fdims[3] = { 0, info.x, info.y }; H5::DataSet data_set = appendStat(info, 3, fdims, info.cvec.data()); if (dumpCount == 0) { if (!info.subnames.empty() && !emptyStrings(info.subnames)) addMetaData(data_set, "subnames", info.subnames); if (!info.y_subnames.empty() && !emptyStrings(info.y_subnames)) addMetaData(data_set, "y_subnames", info.y_subnames); if (!info.subdescs.empty() && !emptyStrings(info.subdescs)) addMetaData(data_set, "subdescs", info.subdescs); } } void Hdf5::visit(const FormulaInfo &info) { if (!enableFormula) return; H5::DataSet data_set = appendVectorInfo(info); if (dumpCount == 0) addMetaData(data_set, "equation", info.str()); } void Hdf5::visit(const SparseHistInfo &info) { warn_once("HDF5 stat files don't support sparse histograms.\n"); } H5::DataSet Hdf5::appendVectorInfo(const VectorInfo &info) { const VResult &vr(info.result()); // Request a 2-dimensional stat, the first dimension will be // populated by the Hdf5::appendStat() helper. The remaining // dimension correspond to the stat instance. hsize_t fdims[2] = { 0, vr.size() }; H5::DataSet data_set = appendStat(info, 2, fdims, vr.data()); if (dumpCount == 0) { if (!info.subnames.empty() && !emptyStrings(info.subnames)) addMetaData(data_set, "subnames", info.subnames); if (!info.subdescs.empty() && !emptyStrings(info.subdescs)) addMetaData(data_set, "subdescs", info.subdescs); } return data_set; } H5::DataSet Hdf5::appendStat(const Info &info, int rank, hsize_t *dims, const double *data) { H5::Group group = path.top(); H5::DataSet data_set; H5::DataSpace fspace; dims[0] = dumpCount + 1; if (dumpCount > 0) { // Get the existing stat if we have already dumped this stat // before. data_set = group.openDataSet(info.name); data_set.extend(dims); fspace = data_set.getSpace(); } else { // We don't have the stat already, create it. H5::DSetCreatPropList props; // Setup max dimensions based on the requested file dimensions std::vector max_dims(rank); std::copy(dims, dims + rank, max_dims.begin()); max_dims[0] = H5S_UNLIMITED; // Setup chunking std::vector chunk_dims(rank); std::copy(dims, dims + rank, chunk_dims.begin()); chunk_dims[0] = timeChunk; props.setChunk(rank, chunk_dims.data()); // Enable compression props.setDeflate(1); fspace = H5::DataSpace(rank, dims, max_dims.data()); data_set = group.createDataSet(info.name, H5::PredType::NATIVE_DOUBLE, fspace, props); if (enableDescriptions && !info.desc.empty()) { addMetaData(data_set, "description", info.desc); } } // The first dimension is time which isn't included in data. dims[0] = 1; H5::DataSpace mspace(rank, dims); std::vector foffset(rank, 0); foffset[0] = dumpCount; fspace.selectHyperslab(H5S_SELECT_SET, dims, foffset.data()); data_set.write(data, H5::PredType::NATIVE_DOUBLE, mspace, fspace); return data_set; } void Hdf5::addMetaData(H5::DataSet &loc, const char *name, const std::vector &values) { H5::StrType type(H5::PredType::C_S1, H5T_VARIABLE); hsize_t dims[1] = { values.size(), }; H5::DataSpace space(1, dims); H5::Attribute attribute = loc.createAttribute(name, type, space); attribute.write(type, values.data()); } void Hdf5::addMetaData(H5::DataSet &loc, const char *name, const std::vector &values) { std::vector cstrs(values.size()); for (int i = 0; i < values.size(); ++i) cstrs[i] = values[i].c_str(); addMetaData(loc, name, cstrs); } void Hdf5::addMetaData(H5::DataSet &loc, const char *name, const std::string &value) { H5::StrType type(H5::PredType::C_S1, value.length() + 1); hsize_t dims[1] = { 1, }; H5::DataSpace space(1, dims); H5::Attribute attribute = loc.createAttribute(name, type, space); attribute.write(type, value.c_str()); } void Hdf5::addMetaData(H5::DataSet &loc, const char *name, double value) { hsize_t dims[1] = { 1, }; H5::DataSpace space(1, dims); H5::Attribute attribute = loc.createAttribute( name, H5::PredType::NATIVE_DOUBLE, space); attribute.write(H5::PredType::NATIVE_DOUBLE, &value); } std::unique_ptr initHDF5(const std::string &filename, unsigned chunking, bool desc, bool formulas) { return std::unique_ptr( new Hdf5(filename, chunking, desc, formulas)); } }; // namespace Stats