vtk-m/vtkm/cont/internal/ArrayHandleBasicImpl.hxx
Robert Maynard a6eecbe9ac ExecutionArrayInterface now can hint at how allocated memory will be used.
Certain backends desire the ability to mark allocations as being used for
reading versus writing to improve performance.
2017-11-02 10:12:57 -04:00

484 lines
16 KiB
C++

//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//
// Copyright 2017 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
// Copyright 2017 UT-Battelle, LLC.
// Copyright 2017 Los Alamos National Security.
//
// Under the terms of Contract DE-NA0003525 with NTESS,
// the U.S. Government retains certain rights in this software.
//
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
// Laboratory (LANL), the U.S. Government retains certain rights in
// this software.
//============================================================================
#ifndef vtk_m_cont_internal_ArrayHandleBasicImpl_hxx
#define vtk_m_cont_internal_ArrayHandleBasicImpl_hxx
#include <vtkm/cont/internal/ArrayHandleBasicImpl.h>
namespace vtkm
{
namespace cont
{
template <typename T>
ArrayHandle<T, StorageTagBasic>::ArrayHandle()
: Internals(new InternalStruct)
{
}
template <typename T>
ArrayHandle<T, StorageTagBasic>::ArrayHandle(const Thisclass& src)
: Internals(src.Internals)
{
}
template <typename T>
ArrayHandle<T, StorageTagBasic>::ArrayHandle(const Thisclass&& src)
: Internals(std::move(src.Internals))
{
}
template <typename T>
ArrayHandle<T, StorageTagBasic>::ArrayHandle(const StorageType& storage)
: Internals(new InternalStruct(storage))
{
}
template <typename T>
ArrayHandle<T, StorageTagBasic>::ArrayHandle(const std::shared_ptr<InternalStruct>& i)
: Internals(i)
{
}
template <typename T>
ArrayHandle<T, StorageTagBasic>::~ArrayHandle()
{
}
template <typename T>
ArrayHandle<T, StorageTagBasic>& ArrayHandle<T, StorageTagBasic>::operator=(const Thisclass& src)
{
this->Internals = src.Internals;
return *this;
}
template <typename T>
ArrayHandle<T, StorageTagBasic>& ArrayHandle<T, StorageTagBasic>::operator=(Thisclass&& src)
{
this->Internals = std::move(src.Internals);
return *this;
}
template <typename T>
bool ArrayHandle<T, StorageTagBasic>::operator==(const Thisclass& rhs) const
{
return this->Internals == rhs.Internals;
}
template <typename T>
bool ArrayHandle<T, StorageTagBasic>::operator!=(const Thisclass& rhs) const
{
return this->Internals != rhs.Internals;
}
template <typename T>
template <typename VT, typename ST>
VTKM_CONT bool ArrayHandle<T, StorageTagBasic>::operator==(const ArrayHandle<VT, ST>&) const
{
return false; // different valuetype and/or storage
}
template <typename T>
template <typename VT, typename ST>
VTKM_CONT bool ArrayHandle<T, StorageTagBasic>::operator!=(const ArrayHandle<VT, ST>&) const
{
return true; // different valuetype and/or storage
}
template <typename T>
typename ArrayHandle<T, StorageTagBasic>::StorageType& ArrayHandle<T, StorageTagBasic>::GetStorage()
{
this->SyncControlArray();
if (this->Internals->ControlArrayValid)
{
return this->Internals->ControlArray;
}
else
{
throw vtkm::cont::ErrorInternal(
"ArrayHandle::SyncControlArray did not make control array valid.");
}
}
template <typename T>
const typename ArrayHandle<T, StorageTagBasic>::StorageType&
ArrayHandle<T, StorageTagBasic>::GetStorage() const
{
this->SyncControlArray();
if (this->Internals->ControlArrayValid)
{
return this->Internals->ControlArray;
}
else
{
throw vtkm::cont::ErrorInternal(
"ArrayHandle::SyncControlArray did not make control array valid.");
}
}
template <typename T>
typename ArrayHandle<T, StorageTagBasic>::PortalControl
ArrayHandle<T, StorageTagBasic>::GetPortalControl()
{
this->SyncControlArray();
if (this->Internals->ControlArrayValid)
{
// If the user writes into the iterator we return, then the execution
// array will become invalid. Play it safe and release the execution
// resources. (Use the const version to preserve the execution array.)
this->ReleaseResourcesExecutionInternal();
return this->Internals->ControlArray.GetPortal();
}
else
{
throw vtkm::cont::ErrorInternal(
"ArrayHandle::SyncControlArray did not make control array valid.");
}
}
template <typename T>
typename ArrayHandle<T, StorageTagBasic>::PortalConstControl
ArrayHandle<T, StorageTagBasic>::GetPortalConstControl() const
{
this->SyncControlArray();
if (this->Internals->ControlArrayValid)
{
return this->Internals->ControlArray.GetPortalConst();
}
else
{
throw vtkm::cont::ErrorInternal(
"ArrayHandle::SyncControlArray did not make control array valid.");
}
}
template <typename T>
vtkm::Id ArrayHandle<T, StorageTagBasic>::GetNumberOfValues() const
{
if (this->Internals->ControlArrayValid)
{
return this->Internals->ControlArray.GetNumberOfValues();
}
else if (this->Internals->ExecutionArrayValid)
{
return static_cast<vtkm::Id>(this->Internals->ExecutionArrayEnd -
this->Internals->ExecutionArray);
}
else
{
return 0;
}
}
template <typename T>
void ArrayHandle<T, StorageTagBasic>::Allocate(vtkm::Id numberOfValues)
{
this->ReleaseResourcesExecutionInternal();
this->Internals->ControlArray.Allocate(numberOfValues);
this->Internals->ControlArrayValid = true;
}
template <typename T>
void ArrayHandle<T, StorageTagBasic>::Shrink(vtkm::Id numberOfValues)
{
VTKM_ASSERT(numberOfValues >= 0);
if (numberOfValues > 0)
{
vtkm::Id originalNumberOfValues = this->GetNumberOfValues();
if (numberOfValues < originalNumberOfValues)
{
if (this->Internals->ControlArrayValid)
{
this->Internals->ControlArray.Shrink(numberOfValues);
}
if (this->Internals->ExecutionArrayValid)
{
this->Internals->ExecutionArrayEnd = this->Internals->ExecutionArray + numberOfValues;
}
}
else if (numberOfValues == originalNumberOfValues)
{
// Nothing to do.
}
else // numberOfValues > originalNumberOfValues
{
throw vtkm::cont::ErrorBadValue("ArrayHandle::Shrink cannot be used to grow array.");
}
VTKM_ASSERT(this->GetNumberOfValues() == numberOfValues);
}
else // numberOfValues == 0
{
// If we are shrinking to 0, there is nothing to save and we might as well
// free up memory. Plus, some storage classes expect that data will be
// deallocated when the size goes to zero.
this->Allocate(0);
}
}
template <typename T>
void ArrayHandle<T, StorageTagBasic>::ReleaseResourcesExecution()
{
// Save any data in the execution environment by making sure it is synced
// with the control environment.
this->SyncControlArray();
this->ReleaseResourcesExecutionInternal();
}
template <typename T>
void ArrayHandle<T, StorageTagBasic>::ReleaseResources()
{
this->ReleaseResourcesExecutionInternal();
if (this->Internals->ControlArrayValid)
{
this->Internals->ControlArray.ReleaseResources();
this->Internals->ControlArrayValid = false;
}
}
template <typename T>
template <typename DeviceAdapterTag>
typename ArrayHandle<T, StorageTagBasic>::template ExecutionTypes<DeviceAdapterTag>::PortalConst
ArrayHandle<T, StorageTagBasic>::PrepareForInput(DeviceAdapterTag device) const
{
VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapterTag);
InternalStruct* priv = const_cast<InternalStruct*>(this->Internals.get());
this->PrepareForDevice(device);
const vtkm::UInt64 numBytes = static_cast<vtkm::UInt64>(sizeof(ValueType)) *
static_cast<vtkm::UInt64>(this->GetStorage().GetNumberOfValues());
if (!this->Internals->ExecutionArrayValid)
{
// Initialize an empty array if needed:
if (!this->Internals->ControlArrayValid)
{
this->Internals->ControlArray.Allocate(0);
this->Internals->ControlArrayValid = true;
}
internal::TypelessExecutionArray execArray(
reinterpret_cast<void*&>(priv->ExecutionArray),
reinterpret_cast<void*&>(priv->ExecutionArrayEnd),
reinterpret_cast<void*&>(priv->ExecutionArrayCapacity),
this->Internals->ControlArray.GetBasePointer(),
this->Internals->ControlArray.GetCapacityPointer());
priv->ExecutionInterface->Allocate(execArray, numBytes);
priv->ExecutionInterface->CopyFromControl(
priv->ControlArray.GetArray(), priv->ExecutionArray, numBytes);
this->Internals->ExecutionArrayValid = true;
}
this->Internals->ExecutionInterface->UsingForRead(
priv->ControlArray.GetArray(), priv->ExecutionArray, numBytes);
return PortalFactory<DeviceAdapterTag>::CreatePortalConst(this->Internals->ExecutionArray,
this->Internals->ExecutionArrayEnd);
}
template <typename T>
template <typename DeviceAdapterTag>
typename ArrayHandle<T, StorageTagBasic>::template ExecutionTypes<DeviceAdapterTag>::Portal
ArrayHandle<T, StorageTagBasic>::PrepareForOutput(vtkm::Id numVals, DeviceAdapterTag device)
{
VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapterTag);
InternalStruct* priv = const_cast<InternalStruct*>(this->Internals.get());
this->PrepareForDevice(device);
// Invalidate control arrays since we expect the execution data to be
// overwritten. Don't free control resources in case they're shared with
// the execution environment.
this->Internals->ControlArrayValid = false;
internal::TypelessExecutionArray execArray(reinterpret_cast<void*&>(priv->ExecutionArray),
reinterpret_cast<void*&>(priv->ExecutionArrayEnd),
reinterpret_cast<void*&>(priv->ExecutionArrayCapacity),
this->Internals->ControlArray.GetBasePointer(),
this->Internals->ControlArray.GetCapacityPointer());
const vtkm::UInt64 numBytes =
static_cast<vtkm::UInt64>(sizeof(ValueType)) * static_cast<vtkm::UInt64>(numVals);
this->Internals->ExecutionInterface->Allocate(execArray, numBytes);
this->Internals->ExecutionInterface->UsingForWrite(
priv->ControlArray.GetArray(), priv->ExecutionArray, numBytes);
this->Internals->ExecutionArrayValid = true;
return PortalFactory<DeviceAdapterTag>::CreatePortal(this->Internals->ExecutionArray,
this->Internals->ExecutionArrayEnd);
}
template <typename T>
template <typename DeviceAdapterTag>
typename ArrayHandle<T, StorageTagBasic>::template ExecutionTypes<DeviceAdapterTag>::Portal
ArrayHandle<T, StorageTagBasic>::PrepareForInPlace(DeviceAdapterTag device)
{
VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapterTag);
InternalStruct* priv = const_cast<InternalStruct*>(this->Internals.get());
this->PrepareForDevice(device);
const vtkm::UInt64 numBytes = static_cast<vtkm::UInt64>(sizeof(ValueType)) *
static_cast<vtkm::UInt64>(this->GetStorage().GetNumberOfValues());
if (!this->Internals->ExecutionArrayValid)
{
// Initialize an empty array if needed:
if (!this->Internals->ControlArrayValid)
{
this->Internals->ControlArray.Allocate(0);
this->Internals->ControlArrayValid = true;
}
internal::TypelessExecutionArray execArray(
reinterpret_cast<void*&>(this->Internals->ExecutionArray),
reinterpret_cast<void*&>(this->Internals->ExecutionArrayEnd),
reinterpret_cast<void*&>(this->Internals->ExecutionArrayCapacity),
this->Internals->ControlArray.GetBasePointer(),
this->Internals->ControlArray.GetCapacityPointer());
priv->ExecutionInterface->Allocate(execArray, numBytes);
priv->ExecutionInterface->CopyFromControl(
priv->ControlArray.GetArray(), priv->ExecutionArray, numBytes);
this->Internals->ExecutionArrayValid = true;
}
priv->ExecutionInterface->UsingForReadWrite(
priv->ControlArray.GetArray(), priv->ExecutionArray, numBytes);
// Invalidate the control array, since we expect the values to be modified:
this->Internals->ControlArrayValid = false;
return PortalFactory<DeviceAdapterTag>::CreatePortal(this->Internals->ExecutionArray,
this->Internals->ExecutionArrayEnd);
}
template <typename T>
template <typename DeviceAdapterTag>
void ArrayHandle<T, StorageTagBasic>::PrepareForDevice(DeviceAdapterTag) const
{
DeviceAdapterId devId = DeviceAdapterTraits<DeviceAdapterTag>::GetId();
InternalStruct* priv = const_cast<InternalStruct*>(this->Internals.get());
// Check if the current device matches the last one and sync through
// the control environment if the device changes.
if (this->Internals->ExecutionInterface)
{
if (this->Internals->ExecutionInterface->GetDeviceId() == devId)
{
// All set, nothing to do.
return;
}
else
{
// Update the device allocator:
this->SyncControlArray();
internal::TypelessExecutionArray execArray(
reinterpret_cast<void*&>(priv->ExecutionArray),
reinterpret_cast<void*&>(priv->ExecutionArrayEnd),
reinterpret_cast<void*&>(priv->ExecutionArrayCapacity),
this->Internals->ControlArray.GetBasePointer(),
this->Internals->ControlArray.GetCapacityPointer());
priv->ExecutionInterface->Free(execArray);
delete priv->ExecutionInterface;
priv->ExecutionInterface = nullptr;
priv->ExecutionArrayValid = false;
}
}
VTKM_ASSERT(priv->ExecutionInterface == nullptr);
VTKM_ASSERT(!priv->ExecutionArrayValid);
priv->ExecutionInterface =
new internal::ExecutionArrayInterfaceBasic<DeviceAdapterTag>(this->Internals->ControlArray);
}
template <typename T>
DeviceAdapterId ArrayHandle<T, StorageTagBasic>::GetDeviceAdapterId() const
{
return this->Internals->ExecutionArrayValid ? this->Internals->ExecutionInterface->GetDeviceId()
: VTKM_DEVICE_ADAPTER_UNDEFINED;
}
template <typename T>
void ArrayHandle<T, StorageTagBasic>::SyncControlArray() const
{
if (!this->Internals->ControlArrayValid)
{
// Need to change some state that does not change the logical state from
// an external point of view.
InternalStruct* priv = const_cast<InternalStruct*>(this->Internals.get());
if (this->Internals->ExecutionArrayValid)
{
const vtkm::Id numValues =
static_cast<vtkm::Id>(this->Internals->ExecutionArrayEnd - this->Internals->ExecutionArray);
const vtkm::UInt64 numBytes =
static_cast<vtkm::UInt64>(sizeof(ValueType)) * static_cast<vtkm::UInt64>(numValues);
priv->ControlArray.Allocate(numValues);
priv->ExecutionInterface->CopyToControl(
priv->ExecutionArray, priv->ControlArray.GetArray(), numBytes);
priv->ControlArrayValid = true;
}
else
{
// This array is in the null state (there is nothing allocated), but
// the calling function wants to do something with the array. Put this
// class into a valid state by allocating an array of size 0.
priv->ControlArray.Allocate(0);
priv->ControlArrayValid = true;
}
}
}
template <typename T>
void ArrayHandle<T, StorageTagBasic>::ReleaseResourcesExecutionInternal()
{
if (this->Internals->ExecutionArrayValid)
{
internal::TypelessExecutionArray execArray(
reinterpret_cast<void*&>(this->Internals->ExecutionArray),
reinterpret_cast<void*&>(this->Internals->ExecutionArrayEnd),
reinterpret_cast<void*&>(this->Internals->ExecutionArrayCapacity),
this->Internals->ControlArray.GetBasePointer(),
this->Internals->ControlArray.GetCapacityPointer());
this->Internals->ExecutionInterface->Free(execArray);
this->Internals->ExecutionArrayValid = false;
}
}
}
} // end namespace vtkm::cont
#endif // not vtk_m_cont_internal_ArrayHandleBasicImpl_hxx