Files
ortools-clone/ortools/base/constant_divisor.h
Corentin Le Molgat b4b226801b update include guards
2025-11-05 11:54:02 +01:00

195 lines
5.9 KiB
C++

// Copyright 2010-2025 Google LLC
// 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
//
// http://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.
#ifndef ORTOOLS_BASE_CONSTANT_DIVISOR_H_
#define ORTOOLS_BASE_CONSTANT_DIVISOR_H_
// Provides faster division in situations where the same divisor is used
// repeatedly but is not known at compile time. For example, a hash table might
// not be sized until the model is loaded, but once loaded it is not resized for
// the life of the model. As this is not a compile time constant, the compiler
// can not optimize away the division.
//
// However the cost of precomputing coefficients when the hash table is sized
// can be dwarfed by the cycles saved avoiding hardware division on every
// lookup. See benchmark section below to estimate the breakeven point. This
// reduces the CPU penalty of non-power of two sized hash tables, bloom filters,
// etc.
//
//
// The following template and specializations are defined in this file:
// - ConstantDivisor<T>
// - ConstantDivisor<uint8_t>
// - ConstantDivisor<uint16_t>
// - ConstantDivisor<uint32_t> (Only supports denominators > 1)
// - ConstantDivisor<uint64_t> (Only supports denominators > 1)
//
// Fast div/mod implementation based on
// "Faster Remainder by Direct Computation: Applications to Compilers and
// Software Libraries" Daniel Lemire, Owen Kaser, Nathan Kurz arXiv:1902.01961
//
// Usage:
// uint64_t n; // Not known at compile time!
// ConstantDivisor<uint64_t> divisor(n);
// uint64_t m = ...;
// EXPECT_EQ(m / n, divisor.div(m));
// EXPECT_EQ(m % n, divisor.mod(m));
//
#include <cstdint>
#include "absl/numeric/int128.h"
namespace util {
namespace math {
template <typename T>
class ConstantDivisor {
public:
typedef T value_type;
explicit ConstantDivisor(value_type denominator)
: denominator_(denominator) {}
value_type div(value_type n) const { return n / denominator_; }
value_type mod(value_type n) const { return n % denominator_; }
friend value_type operator/(value_type a, const ConstantDivisor& b) {
return b.div(a);
}
friend value_type operator%(value_type a, const ConstantDivisor& b) {
return b.mod(a);
}
private:
value_type denominator_;
};
namespace internal {
// Common code for all specializations.
template <typename T, typename MagicT, typename Impl>
class ConstantDivisorBase {
public:
using value_type = T;
explicit ConstantDivisorBase(MagicT magic, value_type denominator)
: magic_(magic), denominator_(denominator) {}
value_type mod(value_type numerator) const {
return numerator -
static_cast<const Impl*>(this)->div(numerator) * denominator_;
}
friend value_type operator/(value_type a, const Impl& b) { return b.div(a); }
friend value_type operator%(value_type a, const Impl& b) { return b.mod(a); }
value_type denominator() const { return denominator_; }
protected:
using MagicValueType = MagicT;
static_assert(sizeof(MagicT) >= 2 * sizeof(value_type));
MagicT magic_;
private:
value_type denominator_;
};
} // namespace internal
// Division and modulus using uint64_t numerators and denominators.
template <>
class ConstantDivisor<uint64_t>
: public internal::ConstantDivisorBase<uint64_t, absl::uint128,
ConstantDivisor<uint64_t>> {
public:
// REQUIRES: denominator > 1
explicit ConstantDivisor(value_type denominator);
value_type div(value_type numerator) const {
return MultiplyHi(magic_, numerator);
}
private:
static uint64_t MultiplyHi(absl::uint128 a, uint64_t b) {
absl::uint128 lo(absl::Uint128Low64(a));
absl::uint128 hi(absl::Uint128High64(a));
absl::uint128 bottom = (lo * b) >> 64;
absl::uint128 top = (hi * b);
return absl::Uint128High64(bottom + top);
}
};
// Division and modulus using uint32_t numerators and denominators.
template <>
class ConstantDivisor<uint32_t>
: public internal::ConstantDivisorBase<uint32_t, uint64_t,
ConstantDivisor<uint32_t>> {
public:
using value_type = uint32_t;
// REQUIRES: denominator > 1
explicit ConstantDivisor(value_type denominator);
value_type div(value_type numerator) const {
return absl::Uint128High64(absl::uint128(numerator) * magic_);
}
};
// Division and modulus using uint16_t numerators and denominators.
template <>
class ConstantDivisor<uint16_t>
: public internal::ConstantDivisorBase<uint16_t, uint64_t,
ConstantDivisor<uint16_t>> {
public:
using value_type = uint16_t;
explicit ConstantDivisor(value_type denominator);
value_type div(value_type numerator) const {
return static_cast<uint16_t>(
(magic_ * static_cast<MagicValueType>(numerator)) >> kShift);
}
private:
// Any value in [32;48] works here.
static constexpr MagicValueType kShift = 32;
};
// Division and modulus using uint8_t numerators and denominators.
template <>
class ConstantDivisor<uint8_t>
: public internal::ConstantDivisorBase<uint8_t, uint32_t,
ConstantDivisor<uint8_t>> {
public:
using value_type = uint8_t;
explicit ConstantDivisor(value_type denominator);
value_type div(value_type numerator) const {
return static_cast<uint8_t>(
(magic_ * static_cast<MagicValueType>(numerator)) >> kShift);
}
private:
// Any value in [16;24] works here.
static constexpr MagicValueType kShift = 16;
};
} // namespace math
} // namespace util
#endif // ORTOOLS_BASE_CONSTANT_DIVISOR_H_