mirror of
https://gitlab.kitware.com/vtk/vtk-m
synced 2024-09-16 17:22:55 +00:00
Add StableSortIndices worklet.
This worklet produces an index array that permutes a key array into a stable sorted order.
This commit is contained in:
parent
8e94e2ae52
commit
d6b2896ad9
@ -33,6 +33,8 @@
|
||||
#include <vtkm/cont/internal/DeviceAdapterError.h>
|
||||
#include <vtkm/cont/testing/Testing.h>
|
||||
|
||||
#include <vtkm/worklet/StableSortIndices.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <random>
|
||||
@ -60,11 +62,15 @@ enum BenchmarkName
|
||||
SCAN_EXCLUSIVE = 1 << 6,
|
||||
SORT = 1 << 7,
|
||||
SORT_BY_KEY = 1 << 8,
|
||||
UNIQUE = 1 << 9,
|
||||
UPPER_BOUNDS = 1 << 10,
|
||||
STABLE_SORT_INDICES = 1 << 9,
|
||||
STABLE_SORT_INDICES_UNIQUE = 1 << 10,
|
||||
UNIQUE = 1 << 11,
|
||||
UPPER_BOUNDS = 1 << 12,
|
||||
ALL = COPY | COPY_IF | LOWER_BOUNDS | REDUCE | REDUCE_BY_KEY | SCAN_INCLUSIVE | SCAN_EXCLUSIVE |
|
||||
SORT |
|
||||
SORT_BY_KEY |
|
||||
STABLE_SORT_INDICES |
|
||||
STABLE_SORT_INDICES_UNIQUE |
|
||||
UNIQUE |
|
||||
UPPER_BOUNDS
|
||||
};
|
||||
@ -380,6 +386,7 @@ private:
|
||||
VTKM_MAKE_BENCHMARK(ReduceByKey20, BenchReduceByKey, 20);
|
||||
VTKM_MAKE_BENCHMARK(ReduceByKey25, BenchReduceByKey, 25);
|
||||
VTKM_MAKE_BENCHMARK(ReduceByKey30, BenchReduceByKey, 30);
|
||||
VTKM_MAKE_BENCHMARK(ReduceByKey100, BenchReduceByKey, 100);
|
||||
|
||||
template <typename Value>
|
||||
struct BenchScanInclusive
|
||||
@ -526,6 +533,94 @@ private:
|
||||
VTKM_MAKE_BENCHMARK(SortByKey20, BenchSortByKey, 20);
|
||||
VTKM_MAKE_BENCHMARK(SortByKey25, BenchSortByKey, 25);
|
||||
VTKM_MAKE_BENCHMARK(SortByKey30, BenchSortByKey, 30);
|
||||
VTKM_MAKE_BENCHMARK(SortByKey100, BenchSortByKey, 100);
|
||||
|
||||
template <typename Value>
|
||||
struct BenchStableSortIndices
|
||||
{
|
||||
using SSI = vtkm::worklet::StableSortIndices<DeviceAdapterTag>;
|
||||
using ValueArrayHandle = vtkm::cont::ArrayHandle<Value, StorageTag>;
|
||||
|
||||
ValueArrayHandle ValueHandle;
|
||||
std::mt19937 Rng;
|
||||
|
||||
VTKM_CONT
|
||||
BenchStableSortIndices()
|
||||
{
|
||||
this->ValueHandle.Allocate(ARRAY_SIZE);
|
||||
auto portal = this->ValueHandle.GetPortalControl();
|
||||
for (vtkm::Id i = 0; i < portal.GetNumberOfValues(); ++i)
|
||||
{
|
||||
portal.Set(vtkm::Id(i), TestValue(vtkm::Id(Rng()), Value()));
|
||||
}
|
||||
}
|
||||
|
||||
VTKM_CONT
|
||||
vtkm::Float64 operator()()
|
||||
{
|
||||
vtkm::cont::ArrayHandle<vtkm::Id> indices;
|
||||
Algorithm::Copy(vtkm::cont::ArrayHandleIndex(ARRAY_SIZE), indices);
|
||||
|
||||
Timer timer;
|
||||
SSI::Sort(ValueHandle, indices);
|
||||
return timer.GetElapsedTime();
|
||||
}
|
||||
|
||||
VTKM_CONT
|
||||
std::string Description() const
|
||||
{
|
||||
std::stringstream description;
|
||||
description << "StableSortIndices::Sort on " << ARRAY_SIZE << " random values";
|
||||
return description.str();
|
||||
}
|
||||
};
|
||||
VTKM_MAKE_BENCHMARK(StableSortIndices, BenchStableSortIndices);
|
||||
|
||||
template <typename Value>
|
||||
struct BenchStableSortIndicesUnique
|
||||
{
|
||||
using SSI = vtkm::worklet::StableSortIndices<DeviceAdapterTag>;
|
||||
using IndexArrayHandle = typename SSI::IndexArrayType;
|
||||
using ValueArrayHandle = vtkm::cont::ArrayHandle<Value, StorageTag>;
|
||||
|
||||
const vtkm::Id N_VALID;
|
||||
ValueArrayHandle ValueHandle;
|
||||
|
||||
VTKM_CONT
|
||||
BenchStableSortIndicesUnique(vtkm::Id percent_valid)
|
||||
: N_VALID((ARRAY_SIZE * percent_valid) / 100)
|
||||
{
|
||||
Algorithm::Schedule(
|
||||
FillModuloTestValueKernel<Value>(
|
||||
N_VALID, this->ValueHandle.PrepareForOutput(ARRAY_SIZE, DeviceAdapterTag())),
|
||||
ARRAY_SIZE);
|
||||
}
|
||||
|
||||
VTKM_CONT
|
||||
vtkm::Float64 operator()()
|
||||
{
|
||||
IndexArrayHandle indices = SSI::Sort(this->ValueHandle);
|
||||
Timer timer;
|
||||
SSI::Unique(this->ValueHandle, indices);
|
||||
return timer.GetElapsedTime();
|
||||
}
|
||||
|
||||
VTKM_CONT
|
||||
std::string Description() const
|
||||
{
|
||||
std::stringstream description;
|
||||
description << "StableSortIndices::Unique on " << ARRAY_SIZE << " values with "
|
||||
<< this->N_VALID << " valid values";
|
||||
return description.str();
|
||||
}
|
||||
};
|
||||
VTKM_MAKE_BENCHMARK(StableSortIndicesUnique5, BenchStableSortIndicesUnique, 5);
|
||||
VTKM_MAKE_BENCHMARK(StableSortIndicesUnique10, BenchStableSortIndicesUnique, 10);
|
||||
VTKM_MAKE_BENCHMARK(StableSortIndicesUnique15, BenchStableSortIndicesUnique, 15);
|
||||
VTKM_MAKE_BENCHMARK(StableSortIndicesUnique20, BenchStableSortIndicesUnique, 20);
|
||||
VTKM_MAKE_BENCHMARK(StableSortIndicesUnique25, BenchStableSortIndicesUnique, 25);
|
||||
VTKM_MAKE_BENCHMARK(StableSortIndicesUnique30, BenchStableSortIndicesUnique, 30);
|
||||
VTKM_MAKE_BENCHMARK(StableSortIndicesUnique100, BenchStableSortIndicesUnique, 100);
|
||||
|
||||
template <typename Value>
|
||||
struct BenchUnique
|
||||
@ -674,6 +769,7 @@ public:
|
||||
VTKM_RUN_BENCHMARK(ReduceByKey20, ValueTypes());
|
||||
VTKM_RUN_BENCHMARK(ReduceByKey25, ValueTypes());
|
||||
VTKM_RUN_BENCHMARK(ReduceByKey30, ValueTypes());
|
||||
VTKM_RUN_BENCHMARK(ReduceByKey100, ValueTypes());
|
||||
}
|
||||
|
||||
if (benchmarks & SCAN_INCLUSIVE)
|
||||
@ -703,6 +799,25 @@ public:
|
||||
VTKM_RUN_BENCHMARK(SortByKey20, ValueTypes());
|
||||
VTKM_RUN_BENCHMARK(SortByKey25, ValueTypes());
|
||||
VTKM_RUN_BENCHMARK(SortByKey30, ValueTypes());
|
||||
VTKM_RUN_BENCHMARK(SortByKey100, ValueTypes());
|
||||
}
|
||||
|
||||
if (benchmarks & STABLE_SORT_INDICES)
|
||||
{
|
||||
std::cout << "\n" << DIVIDER << "\nBenchmarking StableSortIndices::Sort\n";
|
||||
VTKM_RUN_BENCHMARK(StableSortIndices, ValueTypes());
|
||||
}
|
||||
|
||||
if (benchmarks & STABLE_SORT_INDICES_UNIQUE)
|
||||
{
|
||||
std::cout << "\n" << DIVIDER << "\nBenchmarking StableSortIndices::Unique\n";
|
||||
VTKM_RUN_BENCHMARK(StableSortIndicesUnique5, ValueTypes());
|
||||
VTKM_RUN_BENCHMARK(StableSortIndicesUnique10, ValueTypes());
|
||||
VTKM_RUN_BENCHMARK(StableSortIndicesUnique15, ValueTypes());
|
||||
VTKM_RUN_BENCHMARK(StableSortIndicesUnique20, ValueTypes());
|
||||
VTKM_RUN_BENCHMARK(StableSortIndicesUnique25, ValueTypes());
|
||||
VTKM_RUN_BENCHMARK(StableSortIndicesUnique30, ValueTypes());
|
||||
VTKM_RUN_BENCHMARK(StableSortIndicesUnique100, ValueTypes());
|
||||
}
|
||||
|
||||
if (benchmarks & UNIQUE)
|
||||
@ -783,6 +898,14 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
benchmarks |= vtkm::benchmarking::SORT_BY_KEY;
|
||||
}
|
||||
else if (arg == "stablesortindices")
|
||||
{
|
||||
benchmarks |= vtkm::benchmarking::STABLE_SORT_INDICES;
|
||||
}
|
||||
else if (arg == "stablesortindicesunique")
|
||||
{
|
||||
benchmarks |= vtkm::benchmarking::STABLE_SORT_INDICES_UNIQUE;
|
||||
}
|
||||
else if (arg == "unique")
|
||||
{
|
||||
benchmarks |= vtkm::benchmarking::UNIQUE;
|
||||
|
@ -57,6 +57,7 @@ set(headers
|
||||
ScatterIdentity.h
|
||||
ScatterPermutation.h
|
||||
ScatterUniform.h
|
||||
StableSortIndices.h
|
||||
StreamLineUniformGrid.h
|
||||
SurfaceNormals.h
|
||||
Tetrahedralize.h
|
||||
|
169
vtkm/worklet/StableSortIndices.h
Normal file
169
vtkm/worklet/StableSortIndices.h
Normal file
@ -0,0 +1,169 @@
|
||||
//============================================================================
|
||||
// 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_worklet_SortAndUniqueIndices_h
|
||||
#define vtk_m_worklet_SortAndUniqueIndices_h
|
||||
|
||||
#include <vtkm/cont/ArrayHandle.h>
|
||||
#include <vtkm/cont/ArrayHandleIndex.h>
|
||||
#include <vtkm/cont/DeviceAdapterAlgorithm.h>
|
||||
|
||||
namespace vtkm
|
||||
{
|
||||
namespace worklet
|
||||
{
|
||||
|
||||
/// Produces an ArrayHandle<vtkm::Id> index array that stable-sorts and
|
||||
/// optionally uniquifies an input array.
|
||||
template <typename DeviceAdapter>
|
||||
struct StableSortIndices
|
||||
{
|
||||
using IndexArrayType = vtkm::cont::ArrayHandle<vtkm::Id>;
|
||||
|
||||
// Allows Sort to be called on an array that indexes into KeyPortal.
|
||||
// If the values compare equal, the indices are compared to stabilize the
|
||||
// result.
|
||||
template <typename KeyPortalType>
|
||||
struct IndirectSortPredicate
|
||||
{
|
||||
using KeyType = typename KeyPortalType::ValueType;
|
||||
|
||||
const KeyPortalType KeyPortal;
|
||||
|
||||
VTKM_CONT
|
||||
IndirectSortPredicate(const KeyPortalType& keyPortal)
|
||||
: KeyPortal(keyPortal)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename IndexType>
|
||||
VTKM_EXEC bool operator()(const IndexType& a, const IndexType& b) const
|
||||
{
|
||||
// If the values compare equal, compare the indices as well so we get
|
||||
// consistent outputs.
|
||||
const KeyType valueA = this->KeyPortal.Get(a);
|
||||
const KeyType valueB = this->KeyPortal.Get(b);
|
||||
if (valueA < valueB)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (valueB < valueA)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Allows Unique to be called on an array that indexes into KeyPortal.
|
||||
template <typename KeyPortalType>
|
||||
struct IndirectUniquePredicate
|
||||
{
|
||||
const KeyPortalType KeyPortal;
|
||||
|
||||
VTKM_CONT
|
||||
IndirectUniquePredicate(const KeyPortalType& keyPortal)
|
||||
: KeyPortal(keyPortal)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename IndexType>
|
||||
VTKM_EXEC bool operator()(const IndexType& a, const IndexType& b) const
|
||||
{
|
||||
return this->KeyPortal.Get(a) == this->KeyPortal.Get(b);
|
||||
}
|
||||
};
|
||||
|
||||
/// Permutes the @a indices array so that it will map @a keys into a stable
|
||||
/// sorted order. The @a keys array is not modified.
|
||||
///
|
||||
/// @param keys The ArrayHandle containing data to be sorted.
|
||||
/// @param indices The ArrayHandle<vtkm::Id> containing the permutation indices.
|
||||
///
|
||||
/// @note @a indices is expected to contain the values (0, numKeys] in
|
||||
/// increasing order. If the values in @a indices are not sequential, the sort
|
||||
/// will succeed and be consistently reproducible, but the result is not
|
||||
/// guaranteed to be stable with respect to the original ordering of @a keys.
|
||||
template <typename KeyType, typename Storage>
|
||||
VTKM_CONT static void Sort(const vtkm::cont::ArrayHandle<KeyType, Storage>& keys,
|
||||
IndexArrayType& indices)
|
||||
{
|
||||
using Algo = vtkm::cont::DeviceAdapterAlgorithm<DeviceAdapter>;
|
||||
using KeyArrayType = vtkm::cont::ArrayHandle<KeyType, Storage>;
|
||||
using KeyPortalType =
|
||||
typename KeyArrayType::template ExecutionTypes<DeviceAdapter>::PortalConst;
|
||||
using SortPredicate = IndirectSortPredicate<KeyPortalType>;
|
||||
|
||||
VTKM_ASSERT(keys.GetNumberOfValues() == indices.GetNumberOfValues());
|
||||
|
||||
KeyPortalType keyPortal = keys.PrepareForInput(DeviceAdapter());
|
||||
Algo::Sort(indices, SortPredicate(keyPortal));
|
||||
}
|
||||
|
||||
/// Returns an index array that maps the @a keys array into a stable sorted
|
||||
/// ordering. The @a keys array is not modified.
|
||||
///
|
||||
/// This is a convenience overload that generates the index array.
|
||||
template <typename KeyType, typename Storage>
|
||||
VTKM_CONT static IndexArrayType Sort(const vtkm::cont::ArrayHandle<KeyType, Storage>& keys)
|
||||
{
|
||||
using Algo = vtkm::cont::DeviceAdapterAlgorithm<DeviceAdapter>;
|
||||
using KeyArrayType = vtkm::cont::ArrayHandle<KeyType, Storage>;
|
||||
using KeyPortalType =
|
||||
typename KeyArrayType::template ExecutionTypes<DeviceAdapter>::PortalConst;
|
||||
using SortPredicate = IndirectSortPredicate<KeyPortalType>;
|
||||
|
||||
// Generate the initial index array
|
||||
IndexArrayType indices;
|
||||
{
|
||||
vtkm::cont::ArrayHandleIndex indicesSrc(keys.GetNumberOfValues());
|
||||
Algo::Copy(indicesSrc, indices);
|
||||
}
|
||||
|
||||
KeyPortalType keyPortal = keys.PrepareForInput(DeviceAdapter());
|
||||
Algo::Sort(indices, SortPredicate(keyPortal));
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
/// Reduces the array returned by @a Sort so that the mapped @a keys are
|
||||
/// unique. The @a indices array will be modified in-place and the @a keys
|
||||
/// array is not modified.
|
||||
///
|
||||
template <typename KeyType, typename Storage>
|
||||
VTKM_CONT static void Unique(const vtkm::cont::ArrayHandle<KeyType, Storage>& keys,
|
||||
IndexArrayType& indices)
|
||||
{
|
||||
using Algo = vtkm::cont::DeviceAdapterAlgorithm<DeviceAdapter>;
|
||||
using KeyArrayType = vtkm::cont::ArrayHandle<KeyType, Storage>;
|
||||
using KeyPortalType =
|
||||
typename KeyArrayType::template ExecutionTypes<DeviceAdapter>::PortalConst;
|
||||
using UniquePredicate = IndirectUniquePredicate<KeyPortalType>;
|
||||
|
||||
KeyPortalType keyPortal = keys.PrepareForInput(DeviceAdapter());
|
||||
Algo::Unique(indices, UniquePredicate(keyPortal));
|
||||
}
|
||||
};
|
||||
}
|
||||
} // end namespace vtkm::worklet
|
||||
|
||||
#endif // vtk_m_worklet_SortAndUniqueIndices_h
|
Loading…
Reference in New Issue
Block a user