ceres::internal::FixedArray -> absl::FixedArray

Ceres Solver was using an old forked version of FixedArray,
now that we are using absl, we can use the official version
that ships with it.

Change-Id: Ic88d7f6e8a49b928d611f7cbb04172452b322b01
This commit is contained in:
Sameer Agarwal
2024-07-23 12:02:33 -07:00
parent c181b6dbc4
commit ebb66e655f
20 changed files with 57 additions and 1361 deletions
@@ -34,9 +34,9 @@
#include <memory>
#include <type_traits>
#include "absl/container/fixed_array.h"
#include "ceres/first_order_function.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/fixed_array.h"
#include "ceres/jet.h"
#include "ceres/types.h"
@@ -131,7 +131,7 @@ class AutoDiffFirstOrderFunction final : public FirstOrderFunction {
}
using JetT = Jet<double, kNumParameters>;
internal::FixedArray<JetT, (256 * 7) / sizeof(JetT)> x(kNumParameters);
absl::FixedArray<JetT, (256 * 7) / sizeof(JetT)> x(kNumParameters);
for (int i = 0; i < kNumParameters; ++i) {
x[i].a = parameters[i];
x[i].v.setZero();
@@ -38,9 +38,9 @@
#include <type_traits>
#include <vector>
#include "absl/container/fixed_array.h"
#include "absl/log/check.h"
#include "ceres/dynamic_cost_function.h"
#include "ceres/internal/fixed_array.h"
#include "ceres/jet.h"
#include "ceres/types.h"
@@ -149,14 +149,13 @@ class DynamicAutoDiffCostFunction final : public DynamicCostFunction {
// Allocate scratch space for the strided evaluation.
using JetT = Jet<double, Stride>;
internal::FixedArray<JetT, (256 * 7) / sizeof(JetT)> input_jets(
num_parameters);
internal::FixedArray<JetT, (256 * 7) / sizeof(JetT)> output_jets(
absl::FixedArray<JetT, (256 * 7) / sizeof(JetT)> input_jets(num_parameters);
absl::FixedArray<JetT, (256 * 7) / sizeof(JetT)> output_jets(
num_residuals());
// Make the parameter pack that is sent to the functor (reused).
internal::FixedArray<Jet<double, Stride>*> jet_parameters(
num_parameter_blocks, nullptr);
absl::FixedArray<Jet<double, Stride>*> jet_parameters(num_parameter_blocks,
nullptr);
int num_active_parameters = 0;
// To handle constant parameters between non-constant parameter blocks, the
@@ -36,11 +36,11 @@
#include <numeric>
#include <vector>
#include "absl/container/fixed_array.h"
#include "absl/log/check.h"
#include "ceres/dynamic_cost_function.h"
#include "ceres/internal/disable_warnings.h"
#include "ceres/internal/export.h"
#include "ceres/internal/fixed_array.h"
namespace ceres {
@@ -130,11 +130,11 @@ class CERES_EXPORT DynamicCostFunctionToFunctor {
const int num_parameters = std::accumulate(
parameter_block_sizes.begin(), parameter_block_sizes.end(), 0);
internal::FixedArray<double> parameters(num_parameters);
internal::FixedArray<double*> parameter_blocks(num_parameter_blocks);
internal::FixedArray<double> jacobians(num_residuals * num_parameters);
internal::FixedArray<double*> jacobian_blocks(num_parameter_blocks);
internal::FixedArray<double> residuals(num_residuals);
absl::FixedArray<double> parameters(num_parameters);
absl::FixedArray<double*> parameter_blocks(num_parameter_blocks);
absl::FixedArray<double> jacobians(num_residuals * num_parameters);
absl::FixedArray<double*> jacobian_blocks(num_parameter_blocks);
absl::FixedArray<double> residuals(num_residuals);
// Build a set of arrays to get the residuals and jacobians from
// the CostFunction wrapped by this functor.
-1
View File
@@ -43,7 +43,6 @@
#include "ceres/internal/disable_warnings.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/export.h"
#include "ceres/internal/fixed_array.h"
#include "ceres/manifold.h"
namespace ceres {
+5 -4
View File
@@ -35,7 +35,8 @@
#include <array>
#include <vector>
#include "ceres/internal/fixed_array.h"
#include "absl/container/fixed_array.h"
#include "absl/log/check.h"
#include "ceres/types.h"
namespace ceres::internal {
@@ -47,7 +48,7 @@ namespace ceres::internal {
// Three different containers are selected in different scenarios:
//
// num_elements == DYNAMIC:
// -> ceres::internal::FixedArray<T, max_stack_size>(size)
// -> absl::FixedArray<T, max_stack_size>(size)
// num_elements != DYNAMIC && num_elements <= max_stack_size
// -> std::array<T,num_elements>
@@ -71,9 +72,9 @@ struct ArraySelector<T,
max_num_elements_on_stack,
true,
fits_on_stack>
: ceres::internal::FixedArray<T, max_num_elements_on_stack> {
: absl::FixedArray<T, max_num_elements_on_stack> {
explicit ArraySelector(int s)
: ceres::internal::FixedArray<T, max_num_elements_on_stack>(s) {}
: absl::FixedArray<T, max_num_elements_on_stack>(s) {}
};
template <typename T, int num_elements, int max_num_elements_on_stack>
-467
View File
@@ -1,467 +0,0 @@
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// -----------------------------------------------------------------------------
// File: fixed_array.h
// -----------------------------------------------------------------------------
//
// A `FixedArray<T>` represents a non-resizable array of `T` where the length of
// the array can be determined at run-time. It is a good replacement for
// non-standard and deprecated uses of `alloca()` and variable length arrays
// within the GCC extension. (See
// https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html).
//
// `FixedArray` allocates small arrays inline, keeping performance fast by
// avoiding heap operations. It also helps reduce the chances of
// accidentally overflowing your stack if large input is passed to
// your function.
#ifndef CERES_PUBLIC_INTERNAL_FIXED_ARRAY_H_
#define CERES_PUBLIC_INTERNAL_FIXED_ARRAY_H_
#include <Eigen/Core> // For Eigen::aligned_allocator
#include <algorithm>
#include <cstddef>
#include <initializer_list>
#include <iterator>
#include <limits>
#include <memory>
#include <tuple>
#include <type_traits>
#include "absl/log/check.h"
#include "ceres/internal/memory.h"
namespace ceres::internal {
constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
// The default fixed array allocator.
//
// As one can not easily detect if a struct contains or inherits from a fixed
// size Eigen type, to be safe the Eigen::aligned_allocator is used by default.
// But trivial types can never contain Eigen types, so std::allocator is used to
// safe some heap memory.
template <typename T>
using FixedArrayDefaultAllocator =
typename std::conditional<std::is_trivial<T>::value,
std::allocator<T>,
Eigen::aligned_allocator<T>>::type;
// -----------------------------------------------------------------------------
// FixedArray
// -----------------------------------------------------------------------------
//
// A `FixedArray` provides a run-time fixed-size array, allocating a small array
// inline for efficiency.
//
// Most users should not specify an `inline_elements` argument and let
// `FixedArray` automatically determine the number of elements
// to store inline based on `sizeof(T)`. If `inline_elements` is specified, the
// `FixedArray` implementation will use inline storage for arrays with a
// length <= `inline_elements`.
//
// Note that a `FixedArray` constructed with a `size_type` argument will
// default-initialize its values by leaving trivially constructible types
// uninitialized (e.g. int, int[4], double), and others default-constructed.
// This matches the behavior of c-style arrays and `std::array`, but not
// `std::vector`.
//
// Note that `FixedArray` does not provide a public allocator; if it requires a
// heap allocation, it will do so with global `::operator new[]()` and
// `::operator delete[]()`, even if T provides class-scope overrides for these
// operators.
template <typename T,
size_t N = kFixedArrayUseDefault,
typename A = FixedArrayDefaultAllocator<T>>
class FixedArray {
static_assert(!std::is_array<T>::value || std::extent<T>::value > 0,
"Arrays with unknown bounds cannot be used with FixedArray.");
static constexpr size_t kInlineBytesDefault = 256;
using AllocatorTraits = std::allocator_traits<A>;
// std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17,
// but this seems to be mostly pedantic.
template <typename Iterator>
using EnableIfForwardIterator = typename std::enable_if<std::is_convertible<
typename std::iterator_traits<Iterator>::iterator_category,
std::forward_iterator_tag>::value>::type;
static constexpr bool DefaultConstructorIsNonTrivial() {
return !std::is_trivially_default_constructible<StorageElement>::value;
}
public:
using allocator_type = typename AllocatorTraits::allocator_type;
using value_type = typename AllocatorTraits::value_type;
using pointer = typename AllocatorTraits::pointer;
using const_pointer = typename AllocatorTraits::const_pointer;
using reference = value_type&;
using const_reference = const value_type&;
using size_type = typename AllocatorTraits::size_type;
using difference_type = typename AllocatorTraits::difference_type;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static constexpr size_type inline_elements =
(N == kFixedArrayUseDefault ? kInlineBytesDefault / sizeof(value_type)
: static_cast<size_type>(N));
FixedArray(const FixedArray& other,
const allocator_type& a = allocator_type())
: FixedArray(other.begin(), other.end(), a) {}
FixedArray(FixedArray&& other, const allocator_type& a = allocator_type())
: FixedArray(std::make_move_iterator(other.begin()),
std::make_move_iterator(other.end()),
a) {}
// Creates an array object that can store `n` elements.
// Note that trivially constructible elements will be uninitialized.
explicit FixedArray(size_type n, const allocator_type& a = allocator_type())
: storage_(n, a) {
if (DefaultConstructorIsNonTrivial()) {
ConstructRange(storage_.alloc(), storage_.begin(), storage_.end());
}
}
// Creates an array initialized with `n` copies of `val`.
FixedArray(size_type n,
const value_type& val,
const allocator_type& a = allocator_type())
: storage_(n, a) {
ConstructRange(storage_.alloc(), storage_.begin(), storage_.end(), val);
}
// Creates an array initialized with the size and contents of `init_list`.
FixedArray(std::initializer_list<value_type> init_list,
const allocator_type& a = allocator_type())
: FixedArray(init_list.begin(), init_list.end(), a) {}
// Creates an array initialized with the elements from the input
// range. The array's size will always be `std::distance(first, last)`.
// REQUIRES: Iterator must be a forward_iterator or better.
template <typename Iterator, EnableIfForwardIterator<Iterator>* = nullptr>
FixedArray(Iterator first,
Iterator last,
const allocator_type& a = allocator_type())
: storage_(std::distance(first, last), a) {
CopyRange(storage_.alloc(), storage_.begin(), first, last);
}
~FixedArray() noexcept {
for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) {
AllocatorTraits::destroy(storage_.alloc(), cur);
}
}
// Assignments are deleted because they break the invariant that the size of a
// `FixedArray` never changes.
void operator=(FixedArray&&) = delete;
void operator=(const FixedArray&) = delete;
// FixedArray::size()
//
// Returns the length of the fixed array.
size_type size() const { return storage_.size(); }
// FixedArray::max_size()
//
// Returns the largest possible value of `std::distance(begin(), end())` for a
// `FixedArray<T>`. This is equivalent to the most possible addressable bytes
// over the number of bytes taken by T.
constexpr size_type max_size() const {
return (std::numeric_limits<difference_type>::max)() / sizeof(value_type);
}
// FixedArray::empty()
//
// Returns whether or not the fixed array is empty.
bool empty() const { return size() == 0; }
// FixedArray::memsize()
//
// Returns the memory size of the fixed array in bytes.
size_t memsize() const { return size() * sizeof(value_type); }
// FixedArray::data()
//
// Returns a const T* pointer to elements of the `FixedArray`. This pointer
// can be used to access (but not modify) the contained elements.
const_pointer data() const { return AsValueType(storage_.begin()); }
// Overload of FixedArray::data() to return a T* pointer to elements of the
// fixed array. This pointer can be used to access and modify the contained
// elements.
pointer data() { return AsValueType(storage_.begin()); }
// FixedArray::operator[]
//
// Returns a reference the ith element of the fixed array.
// REQUIRES: 0 <= i < size()
reference operator[](size_type i) {
DCHECK_LT(i, size());
return data()[i];
}
// Overload of FixedArray::operator()[] to return a const reference to the
// ith element of the fixed array.
// REQUIRES: 0 <= i < size()
const_reference operator[](size_type i) const {
DCHECK_LT(i, size());
return data()[i];
}
// FixedArray::front()
//
// Returns a reference to the first element of the fixed array.
reference front() { return *begin(); }
// Overload of FixedArray::front() to return a reference to the first element
// of a fixed array of const values.
const_reference front() const { return *begin(); }
// FixedArray::back()
//
// Returns a reference to the last element of the fixed array.
reference back() { return *(end() - 1); }
// Overload of FixedArray::back() to return a reference to the last element
// of a fixed array of const values.
const_reference back() const { return *(end() - 1); }
// FixedArray::begin()
//
// Returns an iterator to the beginning of the fixed array.
iterator begin() { return data(); }
// Overload of FixedArray::begin() to return a const iterator to the
// beginning of the fixed array.
const_iterator begin() const { return data(); }
// FixedArray::cbegin()
//
// Returns a const iterator to the beginning of the fixed array.
const_iterator cbegin() const { return begin(); }
// FixedArray::end()
//
// Returns an iterator to the end of the fixed array.
iterator end() { return data() + size(); }
// Overload of FixedArray::end() to return a const iterator to the end of the
// fixed array.
const_iterator end() const { return data() + size(); }
// FixedArray::cend()
//
// Returns a const iterator to the end of the fixed array.
const_iterator cend() const { return end(); }
// FixedArray::rbegin()
//
// Returns a reverse iterator from the end of the fixed array.
reverse_iterator rbegin() { return reverse_iterator(end()); }
// Overload of FixedArray::rbegin() to return a const reverse iterator from
// the end of the fixed array.
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
// FixedArray::crbegin()
//
// Returns a const reverse iterator from the end of the fixed array.
const_reverse_iterator crbegin() const { return rbegin(); }
// FixedArray::rend()
//
// Returns a reverse iterator from the beginning of the fixed array.
reverse_iterator rend() { return reverse_iterator(begin()); }
// Overload of FixedArray::rend() for returning a const reverse iterator
// from the beginning of the fixed array.
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
// FixedArray::crend()
//
// Returns a reverse iterator from the beginning of the fixed array.
const_reverse_iterator crend() const { return rend(); }
// FixedArray::fill()
//
// Assigns the given `value` to all elements in the fixed array.
void fill(const value_type& val) { std::fill(begin(), end(), val); }
// Relational operators. Equality operators are elementwise using
// `operator==`, while order operators order FixedArrays lexicographically.
friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) {
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}
friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) {
return !(lhs == rhs);
}
friend bool operator<(const FixedArray& lhs, const FixedArray& rhs) {
return std::lexicographical_compare(
lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}
friend bool operator>(const FixedArray& lhs, const FixedArray& rhs) {
return rhs < lhs;
}
friend bool operator<=(const FixedArray& lhs, const FixedArray& rhs) {
return !(rhs < lhs);
}
friend bool operator>=(const FixedArray& lhs, const FixedArray& rhs) {
return !(lhs < rhs);
}
private:
// StorageElement
//
// For FixedArrays with a C-style-array value_type, StorageElement is a POD
// wrapper struct called StorageElementWrapper that holds the value_type
// instance inside. This is needed for construction and destruction of the
// entire array regardless of how many dimensions it has. For all other cases,
// StorageElement is just an alias of value_type.
//
// Maintainer's Note: The simpler solution would be to simply wrap value_type
// in a struct whether it's an array or not. That causes some paranoid
// diagnostics to misfire, believing that 'data()' returns a pointer to a
// single element, rather than the packed array that it really is.
// e.g.:
//
// FixedArray<char> buf(1);
// sprintf(buf.data(), "foo");
//
// error: call to int __builtin___sprintf_chk(etc...)
// will always overflow destination buffer [-Werror]
//
template <typename OuterT,
typename InnerT = typename std::remove_extent<OuterT>::type,
size_t InnerN = std::extent<OuterT>::value>
struct StorageElementWrapper {
InnerT array[InnerN];
};
using StorageElement =
typename std::conditional<std::is_array<value_type>::value,
StorageElementWrapper<value_type>,
value_type>::type;
static pointer AsValueType(pointer ptr) { return ptr; }
static pointer AsValueType(StorageElementWrapper<value_type>* ptr) {
return std::addressof(ptr->array);
}
static_assert(sizeof(StorageElement) == sizeof(value_type));
static_assert(alignof(StorageElement) == alignof(value_type));
class NonEmptyInlinedStorage {
public:
StorageElement* data() { return reinterpret_cast<StorageElement*>(buff_); }
void AnnotateConstruct(size_type) {}
void AnnotateDestruct(size_type) {}
// #ifdef ADDRESS_SANITIZER
// void* RedzoneBegin() { return &redzone_begin_; }
// void* RedzoneEnd() { return &redzone_end_ + 1; }
// #endif // ADDRESS_SANITIZER
private:
// ADDRESS_SANITIZER_REDZONE(redzone_begin_);
alignas(StorageElement) char buff_[sizeof(StorageElement[inline_elements])];
// ADDRESS_SANITIZER_REDZONE(redzone_end_);
};
class EmptyInlinedStorage {
public:
StorageElement* data() { return nullptr; }
void AnnotateConstruct(size_type) {}
void AnnotateDestruct(size_type) {}
};
using InlinedStorage =
typename std::conditional<inline_elements == 0,
EmptyInlinedStorage,
NonEmptyInlinedStorage>::type;
// Storage
//
// An instance of Storage manages the inline and out-of-line memory for
// instances of FixedArray. This guarantees that even when construction of
// individual elements fails in the FixedArray constructor body, the
// destructor for Storage will still be called and out-of-line memory will be
// properly deallocated.
//
class Storage : public InlinedStorage {
public:
Storage(size_type n, const allocator_type& a)
: size_alloc_(n, a), data_(InitializeData()) {}
~Storage() noexcept {
if (UsingInlinedStorage(size())) {
InlinedStorage::AnnotateDestruct(size());
} else {
AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size());
}
}
size_type size() const { return std::get<0>(size_alloc_); }
StorageElement* begin() const { return data_; }
StorageElement* end() const { return begin() + size(); }
allocator_type& alloc() { return std::get<1>(size_alloc_); }
private:
static bool UsingInlinedStorage(size_type n) {
return n <= inline_elements;
}
StorageElement* InitializeData() {
if (UsingInlinedStorage(size())) {
InlinedStorage::AnnotateConstruct(size());
return InlinedStorage::data();
} else {
return reinterpret_cast<StorageElement*>(
AllocatorTraits::allocate(alloc(), size()));
}
}
// Using std::tuple and not absl::CompressedTuple, as it has a lot of
// dependencies to other absl headers.
std::tuple<size_type, allocator_type> size_alloc_;
StorageElement* data_;
};
Storage storage_;
};
template <typename T, size_t N, typename A>
constexpr size_t FixedArray<T, N, A>::kInlineBytesDefault;
template <typename T, size_t N, typename A>
constexpr typename FixedArray<T, N, A>::size_type
FixedArray<T, N, A>::inline_elements;
} // namespace ceres::internal
#endif // CERES_PUBLIC_INTERNAL_FIXED_ARRAY_H_
+3 -3
View File
@@ -40,9 +40,9 @@
#include "Eigen/Dense"
#include "Eigen/StdVector"
#include "absl/container/fixed_array.h"
#include "absl/log/check.h"
#include "ceres/cost_function.h"
#include "ceres/internal/fixed_array.h"
#include "ceres/internal/variadic_evaluate.h"
#include "ceres/numeric_diff_options.h"
#include "ceres/types.h"
@@ -125,8 +125,8 @@ struct NumericDiff {
// For each parameter in the parameter block, use finite differences to
// compute the derivative for that parameter.
FixedArray<double> temp_residual_array(num_residuals_internal);
FixedArray<double> residual_array(num_residuals_internal);
absl::FixedArray<double> temp_residual_array(num_residuals_internal);
absl::FixedArray<double> residual_array(num_residuals_internal);
Map<ResidualVector> residuals(residual_array.data(),
num_residuals_internal);
+1 -1
View File
@@ -228,7 +228,7 @@ class NumericDiffCostFunction final
bool Evaluate(double const* const* parameters,
double* residuals,
double** jacobians) const override {
using internal::FixedArray;
using absl::FixedArray;
using internal::NumericDiff;
using ParameterDims =
@@ -36,10 +36,10 @@
#include <type_traits>
#include <utility>
#include "absl/container/fixed_array.h"
#include "absl/log/check.h"
#include "ceres/first_order_function.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/fixed_array.h"
#include "ceres/internal/numeric_diff.h"
#include "ceres/internal/parameter_dims.h"
#include "ceres/internal/variadic_evaluate.h"
@@ -197,7 +197,7 @@ class NumericDiffFirstOrderFunction final : public FirstOrderFunction {
}
// Create a copy of the parameters which will get mutated.
internal::FixedArray<double, 32> parameters_copy(num_parameters_);
absl::FixedArray<double> parameters_copy(num_parameters_);
std::copy_n(parameters, num_parameters_, parameters_copy.data());
double* parameters_ptr = parameters_copy.data();
constexpr int kNumResiduals = 1;
+13 -15
View File
@@ -42,8 +42,8 @@
#include <type_traits>
#include <utility>
#include "absl/container/fixed_array.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/fixed_array.h"
#include "ceres/internal/port.h"
#include "ceres/manifold.h"
@@ -117,7 +117,7 @@ class ProductManifold final : public Manifold {
bool PlusJacobian(const double* x, double* jacobian_ptr) const override {
MatrixRef jacobian(jacobian_ptr, AmbientSize(), TangentSize());
jacobian.setZero();
internal::FixedArray<double> buffer(buffer_size_);
absl::FixedArray<double> buffer(buffer_size_);
return PlusJacobianImpl(
x, jacobian, buffer, std::make_index_sequence<kNumManifolds>{});
@@ -126,7 +126,7 @@ class ProductManifold final : public Manifold {
bool MinusJacobian(const double* x, double* jacobian_ptr) const override {
MatrixRef jacobian(jacobian_ptr, TangentSize(), AmbientSize());
jacobian.setZero();
internal::FixedArray<double> buffer(buffer_size_);
absl::FixedArray<double> buffer(buffer_size_);
return MinusJacobianImpl(
x, jacobian, buffer, std::make_index_sequence<kNumManifolds>{});
@@ -199,7 +199,7 @@ class ProductManifold final : public Manifold {
template <std::size_t Index0, std::size_t... Indices>
bool PlusJacobianImpl(const double* x,
MatrixRef& jacobian,
internal::FixedArray<double>& buffer,
absl::FixedArray<double>& buffer,
std::index_sequence<Index0, Indices...>) const {
if (!Dereference(std::get<Index0>(manifolds_))
.PlusJacobian(x + ambient_offsets_[Index0], buffer.data())) {
@@ -217,18 +217,17 @@ class ProductManifold final : public Manifold {
x, jacobian, buffer, std::index_sequence<Indices...>{});
}
static constexpr bool PlusJacobianImpl(
const double* /*x*/,
MatrixRef& /*jacobian*/,
internal::FixedArray<double>& /*buffer*/,
std::index_sequence<>) noexcept {
static constexpr bool PlusJacobianImpl(const double* /*x*/,
MatrixRef& /*jacobian*/,
absl::FixedArray<double>& /*buffer*/,
std::index_sequence<>) noexcept {
return true;
}
template <std::size_t Index0, std::size_t... Indices>
bool MinusJacobianImpl(const double* x,
MatrixRef& jacobian,
internal::FixedArray<double>& buffer,
absl::FixedArray<double>& buffer,
std::index_sequence<Index0, Indices...>) const {
if (!Dereference(std::get<Index0>(manifolds_))
.MinusJacobian(x + ambient_offsets_[Index0], buffer.data())) {
@@ -246,11 +245,10 @@ class ProductManifold final : public Manifold {
x, jacobian, buffer, std::index_sequence<Indices...>{});
}
static constexpr bool MinusJacobianImpl(
const double* /*x*/,
MatrixRef& /*jacobian*/,
internal::FixedArray<double>& /*buffer*/,
std::index_sequence<>) noexcept {
static constexpr bool MinusJacobianImpl(const double* /*x*/,
MatrixRef& /*jacobian*/,
absl::FixedArray<double>& /*buffer*/,
std::index_sequence<>) noexcept {
return true;
}
+1 -1
View File
@@ -39,6 +39,7 @@ set(Threads_DEPENDENCY "find_dependency (Threads)" PARENT_SCOPE)
list(APPEND CERES_LIBRARY_PUBLIC_DEPENDENCIES absl::log)
list(APPEND CERES_LIBRARY_PUBLIC_DEPENDENCIES absl::check)
list(APPEND CERES_LIBRARY_PUBLIC_DEPENDENCIES absl::fixed_array)
# Source files that contain public symbols and live in the ceres namespaces.
# Such symbols are expected to be marked with CERES_EXPORT and the files below
@@ -450,7 +451,6 @@ if (BUILD_TESTING)
ceres_test(dynamic_sparsity)
ceres_test(evaluation_callback)
ceres_test(evaluator)
ceres_test(fixed_array)
ceres_test(gradient_checker)
ceres_test(gradient_checking_cost_function)
ceres_test(gradient_problem)
+3 -3
View File
@@ -35,7 +35,7 @@
#include <type_traits>
#include <vector>
#include "ceres/internal/fixed_array.h"
#include "absl/container/fixed_array.h"
#include "ceres/types.h"
#include "gtest/gtest.h"
@@ -47,12 +47,12 @@ namespace ceres::internal {
TEST(ArraySelector, FixedArray) {
ArraySelector<int, DYNAMIC, 20> array1(10);
static_assert(
std::is_base_of<internal::FixedArray<int, 20>, decltype(array1)>::value);
std::is_base_of<absl::FixedArray<int, 20>, decltype(array1)>::value);
EXPECT_EQ(array1.size(), 10);
ArraySelector<int, DYNAMIC, 10> array2(20);
static_assert(
std::is_base_of<internal::FixedArray<int, 10>, decltype(array2)>::value);
std::is_base_of<absl::FixedArray<int, 10>, decltype(array2)>::value);
EXPECT_EQ(array2.size(), 20);
}
+2 -1
View File
@@ -34,6 +34,7 @@
#include <iterator>
#include <random>
#include "absl/container/fixed_array.h"
#include "gtest/gtest.h"
namespace ceres::internal {
@@ -666,7 +667,7 @@ TEST(AutoDiff, AlignedAllocationTest) {
y += 1;
using JetT = Jet<double, 2>;
FixedArray<JetT, (256 * 7) / sizeof(JetT)> x(3);
absl::FixedArray<JetT, (256 * 7) / sizeof(JetT)> x(3);
// Need this to makes sure that x does not get optimized out.
x[0] = x[0] + JetT(1.0);
+3 -3
View File
@@ -33,8 +33,8 @@
#include <numeric>
#include "absl/container/fixed_array.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/fixed_array.h"
#include "ceres/parallel_for.h"
#include "ceres/parallel_vector_ops.h"
@@ -47,7 +47,7 @@ template <typename Derived>
inline double Norm(const Eigen::DenseBase<Derived>& x,
ContextImpl* context,
int num_threads) {
FixedArray<double> norms(num_threads, 0.);
absl::FixedArray<double> norms(num_threads, 0.);
ParallelFor(
context,
0,
@@ -77,7 +77,7 @@ inline double Dot(const VectorLikeX& x,
const VectorLikeY& y,
ContextImpl* context,
int num_threads) {
FixedArray<double> dots(num_threads, 0.);
absl::FixedArray<double> dots(num_threads, 0.);
ParallelFor(
context,
0,
-835
View File
@@ -1,835 +0,0 @@
// Copyright 2017 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ceres/internal/fixed_array.h"
#include <cstdio>
#include <cstring>
#include <list>
#include <memory>
#include <numeric>
#include <scoped_allocator>
#include <stdexcept>
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using ::testing::ElementsAreArray;
namespace {
// CERES_INTERNAL_ARRAYSIZE()
//
// Returns the number of elements in an array as a compile-time constant, which
// can be used in defining new arrays. If you use this macro on a pointer by
// mistake, you will get a compile-time error.
#define CERES_INTERNAL_ARRAYSIZE(array) (sizeof(ArraySizeHelper(array)))
// Note: this internal template function declaration is used by
// CERES_INTERNAL_ARRAYSIZE. The function doesn't need a definition, as we only
// use its type.
template <typename T, size_t N>
auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N];
// Helper routine to determine if a ceres::internal::FixedArray used stack
// allocation.
template <typename ArrayType>
static bool IsOnStack(const ArrayType& a) {
return a.size() <= ArrayType::inline_elements;
}
class ConstructionTester {
public:
ConstructionTester() : self_ptr_(this) { constructions++; }
~ConstructionTester() {
assert(self_ptr_ == this);
self_ptr_ = nullptr;
destructions++;
}
// These are incremented as elements are constructed and destructed so we can
// be sure all elements are properly cleaned up.
static int constructions;
static int destructions;
void CheckConstructed() { assert(self_ptr_ == this); }
void set(int value) { value_ = value; }
int get() { return value_; }
private:
// self_ptr_ should always point to 'this' -- that's how we can be sure the
// constructor has been called.
ConstructionTester* self_ptr_;
int value_{0};
};
int ConstructionTester::constructions = 0;
int ConstructionTester::destructions = 0;
// ThreeInts will initialize its three ints to the value stored in
// ThreeInts::counter. The constructor increments counter so that each object
// in an array of ThreeInts will have different values.
class ThreeInts {
public:
ThreeInts() {
x_ = counter;
y_ = counter;
z_ = counter;
++counter;
}
static int counter;
int x_, y_, z_;
};
int ThreeInts::counter = 0;
TEST(FixedArrayTest, CopyCtor) {
ceres::internal::FixedArray<int, 10> on_stack(5);
std::iota(on_stack.begin(), on_stack.end(), 0);
ceres::internal::FixedArray<int, 10> stack_copy = on_stack;
EXPECT_THAT(stack_copy, ElementsAreArray(on_stack));
EXPECT_TRUE(IsOnStack(stack_copy));
ceres::internal::FixedArray<int, 10> allocated(15);
std::iota(allocated.begin(), allocated.end(), 0);
ceres::internal::FixedArray<int, 10> alloced_copy = allocated;
EXPECT_THAT(alloced_copy, ElementsAreArray(allocated));
EXPECT_FALSE(IsOnStack(alloced_copy));
}
TEST(FixedArrayTest, MoveCtor) {
ceres::internal::FixedArray<std::unique_ptr<int>, 10> on_stack(5);
for (int i = 0; i < 5; ++i) {
on_stack[i] = std::make_unique<int>(i);
}
ceres::internal::FixedArray<std::unique_ptr<int>, 10> stack_copy =
std::move(on_stack);
for (int i = 0; i < 5; ++i) EXPECT_EQ(*(stack_copy[i]), i);
EXPECT_EQ(stack_copy.size(), on_stack.size());
ceres::internal::FixedArray<std::unique_ptr<int>, 10> allocated(15);
for (int i = 0; i < 15; ++i) {
allocated[i] = std::make_unique<int>(i);
}
ceres::internal::FixedArray<std::unique_ptr<int>, 10> alloced_copy =
std::move(allocated);
for (int i = 0; i < 15; ++i) EXPECT_EQ(*(alloced_copy[i]), i);
EXPECT_EQ(allocated.size(), alloced_copy.size());
}
TEST(FixedArrayTest, SmallObjects) {
// Small object arrays
{
// Short arrays should be on the stack
ceres::internal::FixedArray<int> array(4);
EXPECT_TRUE(IsOnStack(array));
}
{
// Large arrays should be on the heap
ceres::internal::FixedArray<int> array(1048576);
EXPECT_FALSE(IsOnStack(array));
}
{
// Arrays of <= default size should be on the stack
ceres::internal::FixedArray<int, 100> array(100);
EXPECT_TRUE(IsOnStack(array));
}
{
// Arrays of > default size should be on the heap
ceres::internal::FixedArray<int, 100> array(101);
EXPECT_FALSE(IsOnStack(array));
}
{
// Arrays with different size elements should use approximately
// same amount of stack space
ceres::internal::FixedArray<int> array1(0);
ceres::internal::FixedArray<char> array2(0);
EXPECT_LE(sizeof(array1), sizeof(array2) + 100);
EXPECT_LE(sizeof(array2), sizeof(array1) + 100);
}
{
// Ensure that vectors are properly constructed inside a fixed array.
ceres::internal::FixedArray<std::vector<int>> array(2);
EXPECT_EQ(0, array[0].size());
EXPECT_EQ(0, array[1].size());
}
{
// Regardless of ceres::internal::FixedArray implementation, check that a
// type with a low alignment requirement and a non power-of-two size is
// initialized correctly.
ThreeInts::counter = 1;
ceres::internal::FixedArray<ThreeInts> array(2);
EXPECT_EQ(1, array[0].x_);
EXPECT_EQ(1, array[0].y_);
EXPECT_EQ(1, array[0].z_);
EXPECT_EQ(2, array[1].x_);
EXPECT_EQ(2, array[1].y_);
EXPECT_EQ(2, array[1].z_);
}
}
TEST(FixedArrayRelationalsTest, EqualArrays) {
for (int i = 0; i < 10; ++i) {
ceres::internal::FixedArray<int, 5> a1(i);
std::iota(a1.begin(), a1.end(), 0);
ceres::internal::FixedArray<int, 5> a2(a1.begin(), a1.end());
EXPECT_TRUE(a1 == a2);
EXPECT_FALSE(a1 != a2);
EXPECT_TRUE(a2 == a1);
EXPECT_FALSE(a2 != a1);
EXPECT_FALSE(a1 < a2);
EXPECT_FALSE(a1 > a2);
EXPECT_FALSE(a2 < a1);
EXPECT_FALSE(a2 > a1);
EXPECT_TRUE(a1 <= a2);
EXPECT_TRUE(a1 >= a2);
EXPECT_TRUE(a2 <= a1);
EXPECT_TRUE(a2 >= a1);
}
}
TEST(FixedArrayRelationalsTest, UnequalArrays) {
for (int i = 1; i < 10; ++i) {
ceres::internal::FixedArray<int, 5> a1(i);
std::iota(a1.begin(), a1.end(), 0);
ceres::internal::FixedArray<int, 5> a2(a1.begin(), a1.end());
--a2[i / 2];
EXPECT_FALSE(a1 == a2);
EXPECT_TRUE(a1 != a2);
EXPECT_FALSE(a2 == a1);
EXPECT_TRUE(a2 != a1);
EXPECT_FALSE(a1 < a2);
EXPECT_TRUE(a1 > a2);
EXPECT_TRUE(a2 < a1);
EXPECT_FALSE(a2 > a1);
EXPECT_FALSE(a1 <= a2);
EXPECT_TRUE(a1 >= a2);
EXPECT_TRUE(a2 <= a1);
EXPECT_FALSE(a2 >= a1);
}
}
template <int stack_elements>
static void TestArray(int n) {
SCOPED_TRACE(n);
SCOPED_TRACE(stack_elements);
ConstructionTester::constructions = 0;
ConstructionTester::destructions = 0;
{
ceres::internal::FixedArray<ConstructionTester, stack_elements> array(n);
EXPECT_THAT(array.size(), n);
EXPECT_THAT(array.memsize(), sizeof(ConstructionTester) * n);
EXPECT_THAT(array.begin() + n, array.end());
// Check that all elements were constructed
for (int i = 0; i < n; i++) {
array[i].CheckConstructed();
}
// Check that no other elements were constructed
EXPECT_THAT(ConstructionTester::constructions, n);
// Test operator[]
for (int i = 0; i < n; i++) {
array[i].set(i);
}
for (int i = 0; i < n; i++) {
EXPECT_THAT(array[i].get(), i);
EXPECT_THAT(array.data()[i].get(), i);
}
// Test data()
for (int i = 0; i < n; i++) {
array.data()[i].set(i + 1);
}
for (int i = 0; i < n; i++) {
EXPECT_THAT(array[i].get(), i + 1);
EXPECT_THAT(array.data()[i].get(), i + 1);
}
} // Close scope containing 'array'.
// Check that all constructed elements were destructed.
EXPECT_EQ(ConstructionTester::constructions,
ConstructionTester::destructions);
}
template <int elements_per_inner_array, int inline_elements>
static void TestArrayOfArrays(int n) {
SCOPED_TRACE(n);
SCOPED_TRACE(inline_elements);
SCOPED_TRACE(elements_per_inner_array);
ConstructionTester::constructions = 0;
ConstructionTester::destructions = 0;
{
using InnerArray = ConstructionTester[elements_per_inner_array];
// Heap-allocate the FixedArray to avoid blowing the stack frame.
auto array_ptr = std::unique_ptr<
ceres::internal::FixedArray<InnerArray, inline_elements>>(
new ceres::internal::FixedArray<InnerArray, inline_elements>(n));
auto& array = *array_ptr;
ASSERT_EQ(array.size(), n);
ASSERT_EQ(array.memsize(),
sizeof(ConstructionTester) * elements_per_inner_array * n);
ASSERT_EQ(array.begin() + n, array.end());
// Check that all elements were constructed
for (int i = 0; i < n; i++) {
for (int j = 0; j < elements_per_inner_array; j++) {
(array[i])[j].CheckConstructed();
}
}
// Check that no other elements were constructed
ASSERT_EQ(ConstructionTester::constructions, n * elements_per_inner_array);
// Test operator[]
for (int i = 0; i < n; i++) {
for (int j = 0; j < elements_per_inner_array; j++) {
(array[i])[j].set(i * elements_per_inner_array + j);
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < elements_per_inner_array; j++) {
ASSERT_EQ((array[i])[j].get(), i * elements_per_inner_array + j);
ASSERT_EQ((array.data()[i])[j].get(), i * elements_per_inner_array + j);
}
}
// Test data()
for (int i = 0; i < n; i++) {
for (int j = 0; j < elements_per_inner_array; j++) {
(array.data()[i])[j].set((i + 1) * elements_per_inner_array + j);
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < elements_per_inner_array; j++) {
ASSERT_EQ((array[i])[j].get(), (i + 1) * elements_per_inner_array + j);
ASSERT_EQ((array.data()[i])[j].get(),
(i + 1) * elements_per_inner_array + j);
}
}
} // Close scope containing 'array'.
// Check that all constructed elements were destructed.
EXPECT_EQ(ConstructionTester::constructions,
ConstructionTester::destructions);
}
TEST(IteratorConstructorTest, NonInline) {
int const kInput[] = {2, 3, 5, 7, 11, 13, 17};
ceres::internal::FixedArray<int, CERES_INTERNAL_ARRAYSIZE(kInput) - 1> const
fixed(kInput, kInput + CERES_INTERNAL_ARRAYSIZE(kInput));
ASSERT_EQ(CERES_INTERNAL_ARRAYSIZE(kInput), fixed.size());
for (size_t i = 0; i < CERES_INTERNAL_ARRAYSIZE(kInput); ++i) {
ASSERT_EQ(kInput[i], fixed[i]);
}
}
TEST(IteratorConstructorTest, Inline) {
int const kInput[] = {2, 3, 5, 7, 11, 13, 17};
ceres::internal::FixedArray<int, CERES_INTERNAL_ARRAYSIZE(kInput)> const
fixed(kInput, kInput + CERES_INTERNAL_ARRAYSIZE(kInput));
ASSERT_EQ(CERES_INTERNAL_ARRAYSIZE(kInput), fixed.size());
for (size_t i = 0; i < CERES_INTERNAL_ARRAYSIZE(kInput); ++i) {
ASSERT_EQ(kInput[i], fixed[i]);
}
}
TEST(IteratorConstructorTest, NonPod) {
char const* kInput[] = {
"red", "orange", "yellow", "green", "blue", "indigo", "violet"};
ceres::internal::FixedArray<std::string> const fixed(
kInput, kInput + CERES_INTERNAL_ARRAYSIZE(kInput));
ASSERT_EQ(CERES_INTERNAL_ARRAYSIZE(kInput), fixed.size());
for (size_t i = 0; i < CERES_INTERNAL_ARRAYSIZE(kInput); ++i) {
ASSERT_EQ(kInput[i], fixed[i]);
}
}
TEST(IteratorConstructorTest, FromEmptyVector) {
std::vector<int> const empty;
ceres::internal::FixedArray<int> const fixed(empty.begin(), empty.end());
EXPECT_EQ(0, fixed.size());
EXPECT_EQ(empty.size(), fixed.size());
}
TEST(IteratorConstructorTest, FromNonEmptyVector) {
int const kInput[] = {2, 3, 5, 7, 11, 13, 17};
std::vector<int> const items(kInput,
kInput + CERES_INTERNAL_ARRAYSIZE(kInput));
ceres::internal::FixedArray<int> const fixed(items.begin(), items.end());
ASSERT_EQ(items.size(), fixed.size());
for (size_t i = 0; i < items.size(); ++i) {
ASSERT_EQ(items[i], fixed[i]);
}
}
TEST(IteratorConstructorTest, FromBidirectionalIteratorRange) {
int const kInput[] = {2, 3, 5, 7, 11, 13, 17};
std::list<int> const items(kInput, kInput + CERES_INTERNAL_ARRAYSIZE(kInput));
ceres::internal::FixedArray<int> const fixed(items.begin(), items.end());
EXPECT_THAT(fixed, testing::ElementsAreArray(kInput));
}
TEST(InitListConstructorTest, InitListConstruction) {
ceres::internal::FixedArray<int> fixed = {1, 2, 3};
EXPECT_THAT(fixed, testing::ElementsAreArray({1, 2, 3}));
}
TEST(FillConstructorTest, NonEmptyArrays) {
ceres::internal::FixedArray<int> stack_array(4, 1);
EXPECT_THAT(stack_array, testing::ElementsAreArray({1, 1, 1, 1}));
ceres::internal::FixedArray<int, 0> heap_array(4, 1);
EXPECT_THAT(stack_array, testing::ElementsAreArray({1, 1, 1, 1}));
}
TEST(FillConstructorTest, EmptyArray) {
ceres::internal::FixedArray<int> empty_fill(0, 1);
ceres::internal::FixedArray<int> empty_size(0);
EXPECT_EQ(empty_fill, empty_size);
}
TEST(FillConstructorTest, NotTriviallyCopyable) {
std::string str = "abcd";
ceres::internal::FixedArray<std::string> strings = {str, str, str, str};
ceres::internal::FixedArray<std::string> array(4, str);
EXPECT_EQ(array, strings);
}
TEST(FillConstructorTest, Disambiguation) {
ceres::internal::FixedArray<size_t> a(1, 2);
EXPECT_THAT(a, testing::ElementsAre(2));
}
TEST(FixedArrayTest, ManySizedArrays) {
std::vector<int> sizes;
for (int i = 1; i < 100; i++) sizes.push_back(i);
for (int i = 100; i <= 1000; i += 100) sizes.push_back(i);
for (int n : sizes) {
TestArray<0>(n);
TestArray<1>(n);
TestArray<64>(n);
TestArray<1000>(n);
}
}
TEST(FixedArrayTest, ManySizedArraysOfArraysOf1) {
for (int n = 1; n < 1000; n++) {
ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 0>(n)));
ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 1>(n)));
ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 64>(n)));
ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 1000>(n)));
}
}
TEST(FixedArrayTest, ManySizedArraysOfArraysOf2) {
for (int n = 1; n < 1000; n++) {
TestArrayOfArrays<2, 0>(n);
TestArrayOfArrays<2, 1>(n);
TestArrayOfArrays<2, 64>(n);
TestArrayOfArrays<2, 1000>(n);
}
}
// If value_type is put inside of a struct container,
// we might evoke this error in a hardened build unless data() is carefully
// written, so check on that.
// error: call to int __builtin___sprintf_chk(etc...)
// will always overflow destination buffer [-Werror]
TEST(FixedArrayTest, AvoidParanoidDiagnostics) {
ceres::internal::FixedArray<char, 32> buf(32);
snprintf(buf.data(), 32, "foo");
}
TEST(FixedArrayTest, TooBigInlinedSpace) {
struct TooBig {
char c[1 << 20];
}; // too big for even one on the stack
// Simulate the data members of ceres::internal::FixedArray, a pointer and a
// size_t.
struct Data {
std::tuple<size_t, std::allocator<double>> size_alloc_;
TooBig* p;
};
// Make sure TooBig objects are not inlined for 0 or default size.
static_assert(
sizeof(ceres::internal::FixedArray<TooBig, 0>) == sizeof(Data),
"0-sized ceres::internal::FixedArray should have same size as Data.");
static_assert(
alignof(ceres::internal::FixedArray<TooBig, 0>) == alignof(Data),
"0-sized ceres::internal::FixedArray should have same alignment as "
"Data.");
static_assert(sizeof(ceres::internal::FixedArray<TooBig>) == sizeof(Data),
"default-sized ceres::internal::FixedArray should have same "
"size as Data");
static_assert(alignof(ceres::internal::FixedArray<TooBig>) == alignof(Data),
"default-sized ceres::internal::FixedArray should have same "
"alignment as Data.");
}
// PickyDelete EXPECTs its class-scope deallocation funcs are unused.
struct PickyDelete {
void operator delete(void* p) {
EXPECT_TRUE(false) << __FUNCTION__;
::operator delete(p);
}
void operator delete[](void* p) {
EXPECT_TRUE(false) << __FUNCTION__;
::operator delete[](p);
}
};
TEST(FixedArrayTest, UsesGlobalAlloc) {
ceres::internal::FixedArray<PickyDelete, 0> a(5);
}
TEST(FixedArrayTest, Data) {
static const int kInput[] = {2, 3, 5, 7, 11, 13, 17};
ceres::internal::FixedArray<int> fa(std::begin(kInput), std::end(kInput));
EXPECT_EQ(fa.data(), &*fa.begin());
EXPECT_EQ(fa.data(), &fa[0]);
const ceres::internal::FixedArray<int>& cfa = fa;
EXPECT_EQ(cfa.data(), &*cfa.begin());
EXPECT_EQ(cfa.data(), &cfa[0]);
}
TEST(FixedArrayTest, Empty) {
ceres::internal::FixedArray<int> empty(0);
ceres::internal::FixedArray<int> inline_filled(1);
ceres::internal::FixedArray<int, 0> heap_filled(1);
EXPECT_TRUE(empty.empty());
EXPECT_FALSE(inline_filled.empty());
EXPECT_FALSE(heap_filled.empty());
}
TEST(FixedArrayTest, FrontAndBack) {
ceres::internal::FixedArray<int, 3 * sizeof(int)> inlined = {1, 2, 3};
EXPECT_EQ(inlined.front(), 1);
EXPECT_EQ(inlined.back(), 3);
ceres::internal::FixedArray<int, 0> allocated = {1, 2, 3};
EXPECT_EQ(allocated.front(), 1);
EXPECT_EQ(allocated.back(), 3);
ceres::internal::FixedArray<int> one_element = {1};
EXPECT_EQ(one_element.front(), one_element.back());
}
TEST(FixedArrayTest, ReverseIteratorInlined) {
ceres::internal::FixedArray<int, 5 * sizeof(int)> a = {0, 1, 2, 3, 4};
int counter = 5;
for (ceres::internal::FixedArray<int>::reverse_iterator iter = a.rbegin();
iter != a.rend();
++iter) {
counter--;
EXPECT_EQ(counter, *iter);
}
EXPECT_EQ(counter, 0);
counter = 5;
for (ceres::internal::FixedArray<int>::const_reverse_iterator iter =
a.rbegin();
iter != a.rend();
++iter) {
counter--;
EXPECT_EQ(counter, *iter);
}
EXPECT_EQ(counter, 0);
counter = 5;
for (auto iter = a.crbegin(); iter != a.crend(); ++iter) {
counter--;
EXPECT_EQ(counter, *iter);
}
EXPECT_EQ(counter, 0);
}
TEST(FixedArrayTest, ReverseIteratorAllocated) {
ceres::internal::FixedArray<int, 0> a = {0, 1, 2, 3, 4};
int counter = 5;
for (ceres::internal::FixedArray<int>::reverse_iterator iter = a.rbegin();
iter != a.rend();
++iter) {
counter--;
EXPECT_EQ(counter, *iter);
}
EXPECT_EQ(counter, 0);
counter = 5;
for (ceres::internal::FixedArray<int>::const_reverse_iterator iter =
a.rbegin();
iter != a.rend();
++iter) {
counter--;
EXPECT_EQ(counter, *iter);
}
EXPECT_EQ(counter, 0);
counter = 5;
for (auto iter = a.crbegin(); iter != a.crend(); ++iter) {
counter--;
EXPECT_EQ(counter, *iter);
}
EXPECT_EQ(counter, 0);
}
TEST(FixedArrayTest, Fill) {
ceres::internal::FixedArray<int, 5 * sizeof(int)> inlined(5);
int fill_val = 42;
inlined.fill(fill_val);
for (int i : inlined) EXPECT_EQ(i, fill_val);
ceres::internal::FixedArray<int, 0> allocated(5);
allocated.fill(fill_val);
for (int i : allocated) EXPECT_EQ(i, fill_val);
// It doesn't do anything, just make sure this compiles.
ceres::internal::FixedArray<int> empty(0);
empty.fill(fill_val);
}
// TODO(johnsoncj): Investigate InlinedStorage default initialization in GCC 4.x
#ifndef __GNUC__
TEST(FixedArrayTest, DefaultCtorDoesNotValueInit) {
using T = char;
constexpr auto capacity = 10;
using FixedArrType = ceres::internal::FixedArray<T, capacity>;
using FixedArrBuffType =
typename std::aligned_storage<sizeof(FixedArrType),
alignof(FixedArrType)>::type;
constexpr auto scrubbed_bits = 0x95;
constexpr auto length = capacity / 2;
FixedArrBuffType buff;
std::memset(std::addressof(buff), scrubbed_bits, sizeof(FixedArrBuffType));
FixedArrType* arr =
::new (static_cast<void*>(std::addressof(buff))) FixedArrType(length);
EXPECT_THAT(*arr, testing::Each(scrubbed_bits));
arr->~FixedArrType();
}
#endif // __GNUC__
// This is a stateful allocator, but the state lives outside of the
// allocator (in whatever test is using the allocator). This is odd
// but helps in tests where the allocator is propagated into nested
// containers - that chain of allocators uses the same state and is
// thus easier to query for aggregate allocation information.
template <typename T>
class CountingAllocator : public std::allocator<T> {
public:
using Alloc = std::allocator<T>;
using size_type = typename Alloc::size_type;
CountingAllocator() = default;
explicit CountingAllocator(int64_t* b) : bytes_used_(b) {}
CountingAllocator(int64_t* b, int64_t* a)
: bytes_used_(b), instance_count_(a) {}
template <typename U>
explicit CountingAllocator(const CountingAllocator<U>& x)
: Alloc(x),
bytes_used_(x.bytes_used_),
instance_count_(x.instance_count_) {}
T* allocate(size_type n) {
assert(bytes_used_ != nullptr);
*bytes_used_ += n * sizeof(T);
return Alloc::allocate(n);
}
void deallocate(T* p, size_type n) {
Alloc::deallocate(p, n);
assert(bytes_used_ != nullptr);
*bytes_used_ -= n * sizeof(T);
}
int64_t* bytes_used_{nullptr};
int64_t* instance_count_{nullptr};
};
TEST(AllocatorSupportTest, CountInlineAllocations) {
constexpr size_t inlined_size = 4;
using Alloc = CountingAllocator<int>;
using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>;
int64_t allocated = 0;
int64_t active_instances = 0;
{
const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
Alloc alloc(&allocated, &active_instances);
AllocFxdArr arr(ia, ia + inlined_size, alloc);
static_cast<void>(arr);
}
EXPECT_EQ(allocated, 0);
EXPECT_EQ(active_instances, 0);
}
TEST(AllocatorSupportTest, CountOutoflineAllocations) {
constexpr size_t inlined_size = 4;
using Alloc = CountingAllocator<int>;
using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>;
int64_t allocated = 0;
int64_t active_instances = 0;
{
const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
Alloc alloc(&allocated, &active_instances);
AllocFxdArr arr(ia, ia + CERES_INTERNAL_ARRAYSIZE(ia), alloc);
EXPECT_EQ(allocated, arr.size() * sizeof(int));
static_cast<void>(arr);
}
EXPECT_EQ(active_instances, 0);
}
TEST(AllocatorSupportTest, CountCopyInlineAllocations) {
constexpr size_t inlined_size = 4;
using Alloc = CountingAllocator<int>;
using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>;
int64_t allocated1 = 0;
int64_t allocated2 = 0;
int64_t active_instances = 0;
Alloc alloc(&allocated1, &active_instances);
Alloc alloc2(&allocated2, &active_instances);
{
int initial_value = 1;
AllocFxdArr arr1(inlined_size / 2, initial_value, alloc);
EXPECT_EQ(allocated1, 0);
AllocFxdArr arr2(arr1, alloc2);
EXPECT_EQ(allocated2, 0);
static_cast<void>(arr1);
static_cast<void>(arr2);
}
EXPECT_EQ(active_instances, 0);
}
TEST(AllocatorSupportTest, CountCopyOutoflineAllocations) {
constexpr size_t inlined_size = 4;
using Alloc = CountingAllocator<int>;
using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>;
int64_t allocated1 = 0;
int64_t allocated2 = 0;
int64_t active_instances = 0;
Alloc alloc(&allocated1, &active_instances);
Alloc alloc2(&allocated2, &active_instances);
{
int initial_value = 1;
AllocFxdArr arr1(inlined_size * 2, initial_value, alloc);
EXPECT_EQ(allocated1, arr1.size() * sizeof(int));
AllocFxdArr arr2(arr1, alloc2);
EXPECT_EQ(allocated2, inlined_size * 2 * sizeof(int));
static_cast<void>(arr1);
static_cast<void>(arr2);
}
EXPECT_EQ(active_instances, 0);
}
TEST(AllocatorSupportTest, SizeValAllocConstructor) {
using testing::AllOf;
using testing::Each;
using testing::SizeIs;
constexpr size_t inlined_size = 4;
using Alloc = CountingAllocator<int>;
using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>;
{
auto len = inlined_size / 2;
auto val = 0;
int64_t allocated = 0;
AllocFxdArr arr(len, val, Alloc(&allocated));
EXPECT_EQ(allocated, 0);
EXPECT_THAT(arr, AllOf(SizeIs(len), Each(0)));
}
{
auto len = inlined_size * 2;
auto val = 0;
int64_t allocated = 0;
AllocFxdArr arr(len, val, Alloc(&allocated));
EXPECT_EQ(allocated, len * sizeof(int));
EXPECT_THAT(arr, AllOf(SizeIs(len), Each(0)));
}
}
struct EigenStruct {
Eigen::Vector4d data;
};
static_assert(
std::is_same<ceres::internal::FixedArrayDefaultAllocator<double>,
std::allocator<double>>::value,
"Double is a trivial type, so std::allocator should be used here.");
static_assert(
std::is_same<ceres::internal::FixedArrayDefaultAllocator<double*>,
std::allocator<double*>>::value,
"A pointer is a trivial type, so std::allocator should be used here.");
static_assert(
std::is_same<ceres::internal::FixedArrayDefaultAllocator<Eigen::Matrix4d>,
Eigen::aligned_allocator<Eigen::Matrix4d>>::value,
"An Eigen::Matrix4d needs the Eigen::aligned_allocator for proper "
"alignment.");
static_assert(
std::is_same<ceres::internal::FixedArrayDefaultAllocator<EigenStruct>,
Eigen::aligned_allocator<EigenStruct>>::value,
"A struct containing fixed size Eigen types needs Eigen::aligned_allocator "
"for proper alignment.");
} // namespace
+2 -1
View File
@@ -37,6 +37,7 @@
#include <utility>
#include <vector>
#include "absl/container/fixed_array.h"
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "ceres/cost_function.h"
@@ -206,7 +207,7 @@ TEST(GradientChecker, SmokeTest) {
parameter_sizes[2] = 4;
// Make a random set of blocks.
FixedArray<double*> parameters(num_parameters);
absl::FixedArray<double*> parameters(num_parameters);
std::mt19937 prng;
std::uniform_real_distribution<double> distribution(-1.0, 1.0);
auto randu = [&prng, &distribution] { return distribution(prng); };
-1
View File
@@ -5,7 +5,6 @@
#include "absl/log/check.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/fixed_array.h"
namespace ceres {
namespace {
+2 -2
View File
@@ -41,6 +41,7 @@
#include <utility>
#include <vector>
#include "absl/container/fixed_array.h"
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "ceres/casts.h"
@@ -52,7 +53,6 @@
#include "ceres/evaluation_callback.h"
#include "ceres/evaluator.h"
#include "ceres/internal/export.h"
#include "ceres/internal/fixed_array.h"
#include "ceres/loss_function.h"
#include "ceres/manifold.h"
#include "ceres/map_util.h"
@@ -776,7 +776,7 @@ bool ProblemImpl::EvaluateResidualBlock(ResidualBlock* residual_block,
}
double dummy_cost = 0.0;
FixedArray<double, 32> scratch(
absl::FixedArray<double> scratch(
residual_block->NumScratchDoublesForEvaluate());
return residual_block->Evaluate(apply_loss_function,
cost ? cost : &dummy_cost,
+3 -3
View File
@@ -35,12 +35,12 @@
#include <cstddef>
#include <vector>
#include "absl/container/fixed_array.h"
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "ceres/corrector.h"
#include "ceres/cost_function.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/fixed_array.h"
#include "ceres/loss_function.h"
#include "ceres/manifold.h"
#include "ceres/parameter_block.h"
@@ -77,13 +77,13 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function,
// Collect the parameters from their blocks. This will rarely allocate, since
// residuals taking more than 8 parameter block arguments are rare.
FixedArray<const double*, 8> parameters(num_parameter_blocks);
absl::FixedArray<const double*> parameters(num_parameter_blocks);
for (int i = 0; i < num_parameter_blocks; ++i) {
parameters[i] = parameter_blocks_[i]->state();
}
// Put pointers into the scratch space into global_jacobians as appropriate.
FixedArray<double*, 8> global_jacobians(num_parameter_blocks);
absl::FixedArray<double*> global_jacobians(num_parameter_blocks);
if (jacobians != nullptr) {
for (int i = 0; i < num_parameter_blocks; ++i) {
const ParameterBlock* parameter_block = parameter_blocks_[i];
+4 -4
View File
@@ -54,12 +54,12 @@
#include <map>
#include "Eigen/Dense"
#include "absl/container/fixed_array.h"
#include "absl/log/check.h"
#include "ceres/block_random_access_matrix.h"
#include "ceres/block_sparse_matrix.h"
#include "ceres/block_structure.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/fixed_array.h"
#include "ceres/invert_psd_matrix.h"
#include "ceres/map_util.h"
#include "ceres/parallel_for.h"
@@ -249,7 +249,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::Eliminate(
ete.setZero();
}
FixedArray<double, 8> g(e_block_size);
absl::FixedArray<double> g(e_block_size);
typename EigenTypes<kEBlockSize>::VectorRef gref(g.data(),
e_block_size);
gref.setZero();
@@ -283,7 +283,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::Eliminate(
// rhs = F'b - F'E(E'E)^(-1) E'b
if (rhs) {
FixedArray<double, 8> inverse_ete_g(e_block_size);
absl::FixedArray<double> inverse_ete_g(e_block_size);
MatrixVectorMultiply<kEBlockSize, kEBlockSize, 0>(
inverse_ete.data(),
e_block_size,
@@ -336,7 +336,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::BackSubstitute(
const Cell& e_cell = row.cells.front();
DCHECK_EQ(e_block_id, e_cell.block_id);
FixedArray<double, 8> sj(row.block.size);
absl::FixedArray<double> sj(row.block.size);
typename EigenTypes<kRowBlockSize>::VectorRef(sj.data(), row.block.size) =
typename EigenTypes<kRowBlockSize>::ConstVectorRef(