/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2012,2013, by the GROMACS development team, led by
- * David van der Spoel, Berk Hess, Erik Lindahl, and including many
- * others, as listed in the AUTHORS file in the top-level source
- * directory and at http://www.gromacs.org.
+ * Copyright (c) 2012,2013,2014, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
*
* GROMACS is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* \author Teemu Murtola <teemu.murtola@gmail.com>
* \ingroup module_analysisdata
*/
+#include "gmxpre.h"
+
#include "datastorage.h"
#include <algorithm>
#include "gromacs/analysisdata/abstractdata.h"
#include "gromacs/analysisdata/dataframe.h"
+#include "gromacs/analysisdata/datamodulemanager.h"
#include "gromacs/analysisdata/paralleloptions.h"
#include "gromacs/utility/exceptions.h"
#include "gromacs/utility/gmxassert.h"
/********************************************************************
- * AnalysisDataStorage::Impl declaration
+ * AnalysisDataStorageImpl declaration
*/
namespace internal
{
+
//! Smart pointer type for managing a storage frame builder.
typedef gmx_unique_ptr<AnalysisDataStorageFrame>::type
AnalysisDataFrameBuilderPointer;
-} // namespace internal
/*! \internal \brief
* Private implementation class for AnalysisDataStorage.
*
* \ingroup module_analysisdata
*/
-class AnalysisDataStorage::Impl
+class AnalysisDataStorageImpl
{
public:
- //! Short-hand for the internal frame data type.
- typedef internal::AnalysisDataStorageFrameData FrameData;
//! Smart pointer type for managing a stored frame.
- typedef gmx_unique_ptr<FrameData>::type FramePointer;
- //! Short-hand for a smart pointer type to a storage frame builder.
- typedef internal::AnalysisDataFrameBuilderPointer FrameBuilderPointer;
+ typedef gmx_unique_ptr<AnalysisDataStorageFrameData>::type FramePointer;
//! Shorthand for a list of data frames that are currently stored.
typedef std::vector<FramePointer> FrameList;
//! Shorthand for a list of currently unused storage frame builders.
- typedef std::vector<FrameBuilderPointer> FrameBuilderList;
+ typedef std::vector<AnalysisDataFrameBuilderPointer> FrameBuilderList;
- Impl();
+ AnalysisDataStorageImpl();
- //! Returns the number of columns in the attached data.
- int columnCount() const;
//! Returns whether the storage is set to use multipoint data.
bool isMultipoint() const;
/*! \brief
}
//! Returns the index of the oldest frame that may be currently stored.
int firstStoredIndex() const;
+ //! Returns the index of the first frame that is not fully notified.
+ int firstUnnotifiedIndex() const { return firstUnnotifiedIndex_; }
/*! \brief
* Computes index into \a frames_ for accessing frame \p index.
*
*
* \throws std::bad_alloc if out of memory.
*/
- FrameBuilderPointer getFrameBuilder();
+ AnalysisDataFrameBuilderPointer getFrameBuilder();
/*! \brief
* Returns whether notifications should be immediately fired.
return isMultipoint() && storageLimit_ == 0 && pendingLimit_ == 1;
}
/*! \brief
- * Calls notification method in \a data_.
+ * Returns whether data needs to be stored at all.
+ *
+ * This is used to optimize multipoint handling for parallel cases
+ * (where shouldNotifyImmediately() returns false),
+ * where it is not necessary to store even a single frame.
*
- * \throws unspecified Any exception thrown by
- * AbstractAnalysisData::notifyPointsAdd().
+ * \todo
+ * This could be extended to non-multipoint data as well.
+ *
+ * Does not throw.
*/
- void notifyPointSet(const AnalysisDataPointSetRef &points);
+ bool needStorage() const
+ {
+ return storageLimit_ > 0 || (pendingLimit_ > 1 && modules_->hasSerialModules());
+ }
/*! \brief
* Calls notification methods for new frames.
*
void finishFrame(int index);
- //! Data object to use for notification calls.
- AbstractAnalysisData *data_;
+ //! Parent data object to access data dimensionality etc.
+ const AbstractAnalysisData *data_;
+ //! Manager to use for notification calls.
+ AnalysisDataModuleManager *modules_;
/*! \brief
* Number of past frames that need to be stored.
*
*
* Should always be at least one.
*
+ * \todo
+ * Get rid of this alltogether, as it is no longer used much.
+ *
* \see AnalysisDataStorage::startFrame()
*/
int pendingLimit_;
FrameList frames_;
//! Location of oldest frame in \a frames_.
size_t firstFrameLocation_;
+ //! Index of the first frame that is not fully notified.
+ int firstUnnotifiedIndex_;
/*! \brief
* Currently unused frame builders.
*
* AnalysisDataStorageFrameImpl declaration
*/
-namespace internal
-{
-
-/*! \internal \brief
+/*! \internal
+ * \brief
* Internal representation for a single stored frame.
*
* It is implemented such that the frame header is always valid, i.e.,
* \param storageImpl Storage object this frame belongs to.
* \param[in] index Zero-based index for the frame.
*/
- AnalysisDataStorageFrameData(AnalysisDataStorage::Impl *storageImpl,
- int index);
+ AnalysisDataStorageFrameData(AnalysisDataStorageImpl *storageImpl,
+ int index);
//! Whether the frame has been started with startFrame().
bool isStarted() const { return status_ >= eStarted; }
void markNotified() { status_ = eNotified; }
//! Returns the storage implementation object.
- AnalysisDataStorage::Impl &storageImpl() const { return storageImpl_; }
+ AnalysisDataStorageImpl &storageImpl() const { return storageImpl_; }
//! Returns the underlying data object (for data dimensionalities etc.).
const AbstractAnalysisData &baseData() const { return *storageImpl().data_; }
/*! \brief
* Adds a new point set to this frame.
*/
- void addPointSet(int firstColumn, ValueIterator begin, ValueIterator end);
+ void addPointSet(int dataSetIndex, int firstColumn,
+ ValueIterator begin, ValueIterator end);
/*! \brief
* Finalizes the frame during AnalysisDataStorage::finishFrame().
*
private:
//! Storage object that contains this frame.
- AnalysisDataStorage::Impl &storageImpl_;
+ AnalysisDataStorageImpl &storageImpl_;
//! Header for the frame.
AnalysisDataFrameHeader header_;
//! Values for the frame.
GMX_DISALLOW_COPY_AND_ASSIGN(AnalysisDataStorageFrameData);
};
-} // namespace internal
-
/********************************************************************
- * AnalysisDataStorage::Impl implementation
+ * AnalysisDataStorageImpl implementation
*/
-AnalysisDataStorage::Impl::Impl()
- : data_(NULL),
- storageLimit_(0), pendingLimit_(1), firstFrameLocation_(0), nextIndex_(0)
+AnalysisDataStorageImpl::AnalysisDataStorageImpl()
+ : data_(NULL), modules_(NULL),
+ storageLimit_(0), pendingLimit_(1),
+ firstFrameLocation_(0), firstUnnotifiedIndex_(0), nextIndex_(0)
{
}
-int
-AnalysisDataStorage::Impl::columnCount() const
-{
- GMX_ASSERT(data_ != NULL, "columnCount() called too early");
- return data_->columnCount();
-}
-
-
bool
-AnalysisDataStorage::Impl::isMultipoint() const
+AnalysisDataStorageImpl::isMultipoint() const
{
GMX_ASSERT(data_ != NULL, "isMultipoint() called too early");
return data_->isMultipoint();
int
-AnalysisDataStorage::Impl::firstStoredIndex() const
+AnalysisDataStorageImpl::firstStoredIndex() const
{
return frames_[firstFrameLocation_]->frameIndex();
}
int
-AnalysisDataStorage::Impl::computeStorageLocation(int index) const
+AnalysisDataStorageImpl::computeStorageLocation(int index) const
{
if (index < firstStoredIndex() || index >= nextIndex_)
{
size_t
-AnalysisDataStorage::Impl::endStorageLocation() const
+AnalysisDataStorageImpl::endStorageLocation() const
{
if (storeAll())
{
void
-AnalysisDataStorage::Impl::extendBuffer(size_t newSize)
+AnalysisDataStorageImpl::extendBuffer(size_t newSize)
{
frames_.reserve(newSize);
while (frames_.size() < newSize)
{
- frames_.push_back(FramePointer(new FrameData(this, nextIndex_)));
+ frames_.push_back(FramePointer(new AnalysisDataStorageFrameData(this, nextIndex_)));
++nextIndex_;
}
// The unused frame should not be included in the count.
void
-AnalysisDataStorage::Impl::rotateBuffer()
+AnalysisDataStorageImpl::rotateBuffer()
{
GMX_ASSERT(!storeAll(),
"No need to rotate internal buffer if everything is stored");
}
-internal::AnalysisDataFrameBuilderPointer
-AnalysisDataStorage::Impl::getFrameBuilder()
+AnalysisDataFrameBuilderPointer
+AnalysisDataStorageImpl::getFrameBuilder()
{
if (builders_.empty())
{
- return FrameBuilderPointer(new AnalysisDataStorageFrame(columnCount()));
+ return AnalysisDataFrameBuilderPointer(new AnalysisDataStorageFrame(*data_));
}
- FrameBuilderPointer builder(move(builders_.back()));
+ AnalysisDataFrameBuilderPointer builder(move(builders_.back()));
builders_.pop_back();
return move(builder);
}
void
-AnalysisDataStorage::Impl::notifyPointSet(const AnalysisDataPointSetRef &points)
-{
- data_->notifyPointsAdd(points);
-}
-
-
-void
-AnalysisDataStorage::Impl::notifyNextFrames(size_t firstLocation)
+AnalysisDataStorageImpl::notifyNextFrames(size_t firstLocation)
{
- if (firstLocation != firstFrameLocation_)
+ if (frames_[firstLocation]->frameIndex() != firstUnnotifiedIndex_)
{
- // firstLocation can only be zero here if !storeAll() because
- // firstFrameLocation_ is always zero for storeAll()
- int prevIndex =
- (firstLocation == 0 ? frames_.size() - 1 : firstLocation - 1);
- if (!frames_[prevIndex]->isNotified())
- {
- return;
- }
+ return;
}
size_t i = firstLocation;
size_t end = endStorageLocation();
while (i != end)
{
- Impl::FrameData &storedFrame = *frames_[i];
+ AnalysisDataStorageFrameData &storedFrame = *frames_[i];
if (!storedFrame.isFinished())
{
break;
}
if (!storedFrame.isNotified())
{
- data_->notifyFrameStart(storedFrame.header());
+ // Increment before the notifications to make the frame available
+ // in the module callbacks.
+ ++firstUnnotifiedIndex_;
+ modules_->notifyFrameStart(storedFrame.header());
for (int j = 0; j < storedFrame.pointSetCount(); ++j)
{
- data_->notifyPointsAdd(storedFrame.pointSet(j));
+ modules_->notifyPointsAdd(storedFrame.pointSet(j));
}
- data_->notifyFrameFinish(storedFrame.header());
+ modules_->notifyFrameFinish(storedFrame.header());
storedFrame.markNotified();
if (storedFrame.frameIndex() >= storageLimit_)
{
void
-AnalysisDataStorage::Impl::finishFrame(int index)
+AnalysisDataStorageImpl::finishFrame(int index)
{
- int storageIndex = computeStorageLocation(index);
+ const int storageIndex = computeStorageLocation(index);
GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
- Impl::FrameData &storedFrame = *frames_[storageIndex];
+
+ AnalysisDataStorageFrameData &storedFrame = *frames_[storageIndex];
GMX_RELEASE_ASSERT(storedFrame.isStarted(),
"finishFrame() called for frame before startFrame()");
GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
GMX_RELEASE_ASSERT(storedFrame.frameIndex() == index,
"Inconsistent internal frame indexing");
builders_.push_back(storedFrame.finishFrame(isMultipoint()));
+ modules_->notifyParallelFrameFinish(storedFrame.header());
if (shouldNotifyImmediately())
{
- data_->notifyFrameFinish(storedFrame.header());
+ ++firstUnnotifiedIndex_;
+ modules_->notifyFrameFinish(storedFrame.header());
if (storedFrame.frameIndex() >= storageLimit_)
{
rotateBuffer();
* AnalysisDataStorageFrame implementation
*/
-namespace internal
-{
-
AnalysisDataStorageFrameData::AnalysisDataStorageFrameData(
- AnalysisDataStorage::Impl *storageImpl,
- int index)
+ AnalysisDataStorageImpl *storageImpl,
+ int index)
: storageImpl_(*storageImpl), header_(index, 0.0, 0.0), status_(eMissing)
{
GMX_RELEASE_ASSERT(storageImpl->data_ != NULL,
// so initialize it only once here.
if (!baseData().isMultipoint())
{
- int columnCount = baseData().columnCount();
- pointSets_.push_back(AnalysisDataPointSetInfo(0, columnCount, 0));
+ int offset = 0;
+ for (int i = 0; i < baseData().dataSetCount(); ++i)
+ {
+ int columnCount = baseData().columnCount(i);
+ pointSets_.push_back(
+ AnalysisDataPointSetInfo(offset, columnCount, i, 0));
+ offset += columnCount;
+ }
}
}
header_ = header;
builder_ = move(builder);
builder_->data_ = this;
+ builder_->selectDataSet(0);
}
void
-AnalysisDataStorageFrameData::addPointSet(int firstColumn,
+AnalysisDataStorageFrameData::addPointSet(int dataSetIndex, int firstColumn,
ValueIterator begin, ValueIterator end)
{
- const int valueCount = end - begin;
+ const int valueCount = end - begin;
+ AnalysisDataPointSetInfo pointSetInfo(0, valueCount,
+ dataSetIndex, firstColumn);
+ AnalysisDataPointSetRef pointSet(header(), pointSetInfo,
+ constArrayRefFromVector<AnalysisDataValue>(begin, end));
+ storageImpl().modules_->notifyParallelPointsAdd(pointSet);
if (storageImpl().shouldNotifyImmediately())
{
- AnalysisDataPointSetInfo pointSetInfo(0, valueCount, firstColumn);
- storageImpl().notifyPointSet(
- AnalysisDataPointSetRef(header(), pointSetInfo,
- AnalysisDataValuesRef(begin, end)));
+ storageImpl().modules_->notifyPointsAdd(pointSet);
}
- else
+ else if (storageImpl().needStorage())
{
pointSets_.push_back(
- AnalysisDataPointSetInfo(values_.size(), valueCount, firstColumn));
+ AnalysisDataPointSetInfo(values_.size(), valueCount,
+ dataSetIndex, firstColumn));
std::copy(begin, end, std::back_inserter(values_));
}
}
status_ = eFinished;
if (!bMultipoint)
{
- GMX_RELEASE_ASSERT(pointSets_.size() == 1U,
+ GMX_RELEASE_ASSERT(static_cast<int>(pointSets_.size()) == baseData().dataSetCount(),
"Point sets created for non-multipoint data");
values_ = builder_->values_;
builder_->clearValues();
+ for (int i = 0; i < pointSetCount(); ++i)
+ {
+ storageImpl().modules_->notifyParallelPointsAdd(pointSet(i));
+ }
}
else
{
"Invalid point set index");
return AnalysisDataPointSetRef(
header_, pointSets_[index],
- AnalysisDataValuesRef(values_.begin(), values_.end()));
+ constArrayRefFromVector<AnalysisDataValue>(values_.begin(), values_.end()));
}
} // namespace internal
* AnalysisDataStorageFrame
*/
-AnalysisDataStorageFrame::AnalysisDataStorageFrame(int columnCount)
- : data_(NULL), values_(columnCount), bPointSetInProgress_(false)
+AnalysisDataStorageFrame::AnalysisDataStorageFrame(
+ const AbstractAnalysisData &data)
+ : data_(NULL), currentDataSet_(0), currentOffset_(0),
+ columnCount_(data.columnCount(0)), bPointSetInProgress_(false)
{
+ int totalColumnCount = 0;
+ for (int i = 0; i < data.dataSetCount(); ++i)
+ {
+ totalColumnCount += data.columnCount(i);
+ }
+ values_.resize(totalColumnCount);
}
}
+void
+AnalysisDataStorageFrame::selectDataSet(int index)
+{
+ GMX_RELEASE_ASSERT(data_ != NULL, "Invalid frame accessed");
+ const AbstractAnalysisData &baseData = data_->baseData();
+ GMX_RELEASE_ASSERT(index >= 0 && index < baseData.dataSetCount(),
+ "Out of range data set index");
+ GMX_RELEASE_ASSERT(!baseData.isMultipoint() || !bPointSetInProgress_,
+ "Point sets in multipoint data cannot span data sets");
+ currentDataSet_ = index;
+ currentOffset_ = 0;
+ // TODO: Consider precalculating.
+ for (int i = 0; i < index; ++i)
+ {
+ currentOffset_ += baseData.columnCount(i);
+ }
+ columnCount_ = baseData.columnCount(index);
+}
+
+
void
AnalysisDataStorageFrame::finishPointSet()
{
"Should not be called for non-multipoint data");
if (bPointSetInProgress_)
{
- std::vector<AnalysisDataValue>::const_iterator begin = values_.begin();
- std::vector<AnalysisDataValue>::const_iterator end = values_.end();
+ std::vector<AnalysisDataValue>::const_iterator begin
+ = values_.begin() + currentOffset_;
+ std::vector<AnalysisDataValue>::const_iterator end
+ = begin + columnCount_;
+ int firstColumn = 0;
while (begin != end && !begin->isSet())
{
++begin;
+ ++firstColumn;
}
while (end != begin && !(end-1)->isSet())
{
--end;
}
- int firstColumn = (begin != end) ? begin - values_.begin() : 0;
- data_->addPointSet(firstColumn, begin, end);
+ if (begin == end)
+ {
+ firstColumn = 0;
+ }
+ data_->addPointSet(currentDataSet_, firstColumn, begin, end);
}
clearValues();
}
}
-void
-AnalysisDataStorage::setParallelOptions(const AnalysisDataParallelOptions &opt)
+int
+AnalysisDataStorage::frameCount() const
{
- impl_->pendingLimit_ = 2 * opt.parallelizationFactor() - 1;
+ return impl_->firstUnnotifiedIndex();
}
{
return AnalysisDataFrameRef();
}
- const Impl::FrameData &storedFrame = *impl_->frames_[storageIndex];
+ const internal::AnalysisDataStorageFrameData &storedFrame
+ = *impl_->frames_[storageIndex];
if (!storedFrame.isAvailable())
{
return AnalysisDataFrameRef();
void
-AnalysisDataStorage::startDataStorage(AbstractAnalysisData *data)
+AnalysisDataStorage::startDataStorage(AbstractAnalysisData *data,
+ AnalysisDataModuleManager *modules)
{
+ modules->notifyDataStart(data);
+ // Data needs to be set before calling extendBuffer()
+ impl_->data_ = data;
+ impl_->modules_ = modules;
+ if (!impl_->storeAll())
+ {
+ // 2 = pending limit (1) + 1
+ impl_->extendBuffer(impl_->storageLimit_ + 2);
+ }
+}
+
+
+void
+AnalysisDataStorage::startParallelDataStorage(
+ AbstractAnalysisData *data,
+ AnalysisDataModuleManager *modules,
+ const AnalysisDataParallelOptions &options)
+{
+ const int pendingLimit = options.parallelizationFactor();
+ impl_->pendingLimit_ = pendingLimit;
+ modules->notifyParallelDataStart(data, options);
// Data needs to be set before calling extendBuffer()
- impl_->data_ = data;
+ impl_->data_ = data;
+ impl_->modules_ = modules;
if (!impl_->storeAll())
{
- impl_->extendBuffer(impl_->storageLimit_ + impl_->pendingLimit_ + 1);
+ impl_->extendBuffer(impl_->storageLimit_ + pendingLimit + 1);
}
}
AnalysisDataStorage::startFrame(const AnalysisDataFrameHeader &header)
{
GMX_ASSERT(header.isValid(), "Invalid header");
- Impl::FrameData *storedFrame;
+ internal::AnalysisDataStorageFrameData *storedFrame;
if (impl_->storeAll())
{
size_t size = header.index() + 1;
GMX_RELEASE_ASSERT(storedFrame->frameIndex() == header.index(),
"Inconsistent internal frame indexing");
storedFrame->startFrame(header, impl_->getFrameBuilder());
+ impl_->modules_->notifyParallelFrameStart(header);
if (impl_->shouldNotifyImmediately())
{
- impl_->data_->notifyFrameStart(header);
+ impl_->modules_->notifyFrameStart(header);
}
return storedFrame->builder();
}
AnalysisDataStorageFrame &
AnalysisDataStorage::currentFrame(int index)
{
- int storageIndex = impl_->computeStorageLocation(index);
+ const int storageIndex = impl_->computeStorageLocation(index);
GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
- Impl::FrameData &storedFrame = *impl_->frames_[storageIndex];
+
+ internal::AnalysisDataStorageFrameData &storedFrame = *impl_->frames_[storageIndex];
GMX_RELEASE_ASSERT(storedFrame.isStarted(),
"currentFrame() called for frame before startFrame()");
GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
impl_->finishFrame(index);
}
+void
+AnalysisDataStorage::finishDataStorage()
+{
+ // TODO: Check that all frames have been finished etc.
+ impl_->builders_.clear();
+ impl_->modules_->notifyDataFinish();
+}
+
} // namespace gmx