Commit aaf9e970 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

Compute fingerprint tables at constexpr time

Summary: [Folly] Compute fingerprint tables at constexpr time, v.s. as a separate build step which generates a source file.

Reviewed By: Orvid

Differential Revision: D10081043

fbshipit-source-id: f01f7587aafd29fc1c44515730b759325e1566d8
parent 1d10cf85
......@@ -119,8 +119,6 @@ list(REMOVE_ITEM files
${FOLLY_DIR}/python/GILAwareManualExecutor.cpp
)
list(REMOVE_ITEM hfiles
${FOLLY_DIR}/detail/SlowFingerprint.h
${FOLLY_DIR}/detail/FingerprintPolynomial.h
${FOLLY_DIR}/python/fibers.h
${FOLLY_DIR}/python/GILAwareManualExecutor.h
)
......@@ -194,9 +192,6 @@ auto_source_group(folly ${FOLLY_DIR} ${files} ${hfiles})
apply_folly_compile_options_to_target(folly_base)
# Add the generated files to the correct source group.
source_group("folly" FILES ${CMAKE_CURRENT_BINARY_DIR}/folly/folly-config.h)
source_group("folly\\build" FILES
${CMAKE_CURRENT_BINARY_DIR}/folly/build/FingerprintTables.cpp
)
# Generate pkg-config variables from folly_deps before we add our own
# build/install-time include directory generator expressions
......@@ -219,51 +214,10 @@ target_compile_definitions(folly_base
$<TARGET_PROPERTY:folly_deps,INTERFACE_COMPILE_DEFINITIONS>
)
# Now to generate the fingerprint tables
add_executable(GenerateFingerprintTables
${FOLLY_DIR}/build/GenerateFingerprintTables.cpp
$<TARGET_OBJECTS:folly_base>
)
target_link_libraries(GenerateFingerprintTables PRIVATE folly_deps)
apply_folly_compile_options_to_target(GenerateFingerprintTables)
set_property(TARGET GenerateFingerprintTables PROPERTY FOLDER "Build")
source_group("" FILES ${FOLLY_DIR}/build/GenerateFingerprintTables.cpp)
# Compile the fingerprint tables.
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/folly/build/FingerprintTables.cpp
COMMAND
${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/folly/build
COMMAND
GenerateFingerprintTables
--install_dir ${CMAKE_CURRENT_BINARY_DIR}/folly/build
DEPENDS GenerateFingerprintTables
COMMENT "Generating the fingerprint tables..."
)
add_library(folly_fingerprint STATIC
${CMAKE_CURRENT_BINARY_DIR}/folly/build/FingerprintTables.cpp
${FOLLY_DIR}/Fingerprint.h
${FOLLY_DIR}/detail/SlowFingerprint.h
${FOLLY_DIR}/detail/FingerprintPolynomial.h
$<TARGET_OBJECTS:folly_base>
)
target_link_libraries(folly_fingerprint PRIVATE folly_deps)
apply_folly_compile_options_to_target(folly_fingerprint)
# We want to generate a single library and target for folly, but we needed a
# two-stage compile for the fingerprint tables, so we create a phony source
# file that we modify whenever the base libraries change, causing folly to be
# re-linked, making things happy.
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/folly_dep.cpp
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/folly_dep.cpp
DEPENDS folly_base folly_fingerprint
)
add_library(folly
${CMAKE_CURRENT_BINARY_DIR}/folly_dep.cpp
$<TARGET_OBJECTS:folly_base>
)
apply_folly_compile_options_to_target(folly)
source_group("" FILES ${CMAKE_CURRENT_BINARY_DIR}/folly_dep.cpp)
target_link_libraries(folly PUBLIC folly_deps)
......@@ -274,9 +228,6 @@ install(TARGETS folly folly_deps
ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
auto_install_files(folly ${FOLLY_DIR}
${hfiles}
${FOLLY_DIR}/Fingerprint.h
${FOLLY_DIR}/detail/SlowFingerprint.h
${FOLLY_DIR}/detail/FingerprintPolynomial.h
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/folly/folly-config.h
......
/*
* Copyright 2018-present Facebook, Inc.
*
* 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.
*/
#include <folly/Fingerprint.h>
#include <folly/Utility.h>
#include <folly/detail/FingerprintPolynomial.h>
namespace folly {
namespace detail {
namespace {
// The polynomials below were generated by a separate program that requires the
// NTL (Number Theory Library) from http://www.shoup.net/ntl/
//
// Briefly: randomly generate a polynomial of degree D, test for
// irreducibility, repeat until you find an irreducible polynomial
// (roughly 1/D of all polynomials of degree D are irreducible, so
// this will succeed in D/2 tries on average; D is small (64..128) so
// this simple method works well)
//
// DO NOT REPLACE THE POLYNOMIALS USED, EVER, as that would change the value
// of every single fingerprint in existence.
template <size_t Deg>
struct FingerprintTablePoly;
template <>
struct FingerprintTablePoly<63> {
static constexpr uint64_t data[1] = {0xbf3736b51869e9b7};
};
template <>
struct FingerprintTablePoly<95> {
static constexpr uint64_t data[2] = {0x51555cb0aa8d39c3, 0xb679ec3700000000};
};
template <>
struct FingerprintTablePoly<127> {
static constexpr uint64_t data[2] = {0xc91bff9b8768b51b, 0x8c5d5853bd77b0d3};
};
template <typename D, size_t S0, size_t... I0>
constexpr auto copy_table(D const (&table)[S0], index_sequence<I0...>) {
using array = std::array<D, S0>;
return array{{table[I0]...}};
}
template <typename D, size_t S0>
constexpr auto copy_table(D const (&table)[S0]) {
return copy_table(table, make_index_sequence<S0>{});
}
template <typename D, size_t S0, size_t S1, size_t... I0>
constexpr auto copy_table(D const (&table)[S0][S1], index_sequence<I0...>) {
using array = std::array<std::array<D, S1>, S0>;
return array{{copy_table(table[I0])...}};
}
template <typename D, size_t S0, size_t S1>
constexpr auto copy_table(D const (&table)[S0][S1]) {
return copy_table(table, make_index_sequence<S0>{});
}
template <typename D, size_t S0, size_t S1, size_t S2, size_t... I0>
constexpr auto copy_table(D const (&table)[S0][S1][S2], index_sequence<I0...>) {
using array = std::array<std::array<std::array<D, S2>, S1>, S0>;
return array{{copy_table(table[I0])...}};
}
template <typename D, size_t S0, size_t S1, size_t S2>
constexpr auto copy_table(D const (&table)[S0][S1][S2]) {
return copy_table(table, make_index_sequence<S0>{});
}
template <size_t Deg>
constexpr poly_table<Deg> make_poly_table() {
FingerprintPolynomial<Deg> const poly(FingerprintTablePoly<Deg>::data);
uint64_t table[8][256][poly_size(Deg)] = {};
// table[i][q] is Q(X) * X^(k+8*i) mod P(X),
// where k is the number of bits in the fingerprint (and deg(P)) and
// Q(X) = q7*X^7 + q6*X^6 + ... + q1*X + q0 is a degree-7 polyonomial
// whose coefficients are the bits of q.
for (uint16_t x = 0; x < 256; x++) {
FingerprintPolynomial<Deg> t;
t.setHigh8Bits(uint8_t(x));
for (int i = 0; i < 8; i++) {
t.mulXkmod(8, poly);
for (size_t j = 0; j < poly_size(Deg); ++j) {
table[i][x][j] = t.get(j);
}
}
}
return copy_table(table);
}
} // namespace
template <>
FOLLY_STORAGE_CONSTEXPR const uint64_t
FingerprintTable<64>::poly[poly_size(64)] = {
FingerprintTablePoly<63>::data[0]};
template <>
FOLLY_STORAGE_CONSTEXPR const uint64_t
FingerprintTable<96>::poly[poly_size(96)] = {
FingerprintTablePoly<95>::data[0],
FingerprintTablePoly<95>::data[1]};
template <>
FOLLY_STORAGE_CONSTEXPR const uint64_t
FingerprintTable<128>::poly[poly_size(128)] = {
FingerprintTablePoly<127>::data[0],
FingerprintTablePoly<127>::data[1]};
template <>
FOLLY_STORAGE_CONSTEXPR const poly_table<64> FingerprintTable<64>::table =
make_poly_table<63>();
template <>
FOLLY_STORAGE_CONSTEXPR const poly_table<96> FingerprintTable<96>::table =
make_poly_table<95>();
template <>
FOLLY_STORAGE_CONSTEXPR const poly_table<128> FingerprintTable<128>::table =
make_poly_table<127>();
} // namespace detail
} // namespace folly
......@@ -28,8 +28,7 @@
* extended for fingerprints larger than 64 bits, and modified to use
* 64-bit instead of 32-bit integers for computation.
*
* The precomputed tables are in FingerprintTable.cpp, which is automatically
* generated by ComputeFingerprintTable.cpp.
* The precomputed tables are in Fingerprint.cpp.
*
* Benchmarked on 10/13/2009 on a 2.5GHz quad-core Xeon L5420,
* - Fingerprint<64>::update64() takes about 12ns
......@@ -44,6 +43,7 @@
#pragma once
#include <array>
#include <cstdint>
#include <folly/Range.h>
......@@ -52,16 +52,24 @@ namespace folly {
namespace detail {
constexpr size_t poly_size(size_t bits) {
return 1 + (bits - 1) / 64;
}
template <size_t Deg>
using poly_table =
std::array<std::array<std::array<uint64_t, poly_size(Deg)>, 256>, 8>;
template <int BITS>
struct FingerprintTable {
static const uint64_t poly[1 + (BITS - 1) / 64];
static const uint64_t table[8][256][1 + (BITS - 1) / 64];
static const uint64_t poly[poly_size(BITS)];
static const poly_table<BITS> table;
};
template <int BITS>
const uint64_t FingerprintTable<BITS>::poly[1 + (BITS - 1) / 64] = {};
const uint64_t FingerprintTable<BITS>::poly[poly_size(BITS)] = {};
template <int BITS>
const uint64_t FingerprintTable<BITS>::table[8][256][1 + (BITS - 1) / 64] = {};
const poly_table<BITS> FingerprintTable<BITS>::table = {};
#ifndef _MSC_VER
// MSVC 2015 can't handle these extern specialization declarations,
......@@ -70,9 +78,9 @@ const uint64_t FingerprintTable<BITS>::table[8][256][1 + (BITS - 1) / 64] = {};
#define FOLLY_DECLARE_FINGERPRINT_TABLES(BITS) \
template <> \
const uint64_t FingerprintTable<BITS>::poly[1 + (BITS - 1) / 64]; \
const uint64_t FingerprintTable<BITS>::poly[poly_size(BITS)]; \
template <> \
const uint64_t FingerprintTable<BITS>::table[8][256][1 + (BITS - 1) / 64]
const poly_table<BITS> FingerprintTable<BITS>::table
FOLLY_DECLARE_FINGERPRINT_TABLES(64);
FOLLY_DECLARE_FINGERPRINT_TABLES(96);
......@@ -142,8 +150,8 @@ class Fingerprint {
/**
* Return the number of uint64s needed to hold the fingerprint value.
*/
static int size() {
return 1 + (BITS - 1) / 64;
constexpr static int size() {
return detail::poly_size(BITS);
}
/**
......@@ -162,7 +170,7 @@ class Fingerprint {
private:
// XOR the fingerprint with a value from one of the tables.
void xortab(const uint64_t* tab) {
void xortab(std::array<uint64_t, detail::poly_size(BITS)> const& tab) {
for (int i = 0; i < size(); i++) {
fp_[i] ^= tab[i];
}
......@@ -175,7 +183,7 @@ class Fingerprint {
uint32_t shlor32(uint32_t v);
uint64_t shlor64(uint64_t v);
uint64_t fp_[1 + (BITS - 1) / 64];
uint64_t fp_[detail::poly_size(BITS)];
};
// Convenience functions
......
/*
* Copyright 2012-present Facebook, Inc.
*
* 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 __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS 1
#endif
#include <cinttypes>
#include <cstdio>
#include <string>
#include <glog/logging.h>
#include <folly/Portability.h>
#include <folly/portability/GFlags.h>
#include <folly/detail/FingerprintPolynomial.h>
using namespace folly;
using namespace folly::detail;
// The defaults were generated by a separate program that requires the
// NTL (Number Theory Library) from http://www.shoup.net/ntl/
//
// Briefly: randomly generate a polynomial of degree D, test for
// irreducibility, repeat until you find an irreducible polynomial
// (roughly 1/D of all polynomials of degree D are irreducible, so
// this will succeed in D/2 tries on average; D is small (64..128) so
// this simple method works well)
//
// DO NOT REPLACE THE POLYNOMIALS USED, EVER, as that would change the value
// of every single fingerprint in existence.
DEFINE_int64(
poly64,
0xbf3736b51869e9b7,
"Generate 64-bit tables using this polynomial");
DEFINE_int64(
poly96_m,
0x51555cb0aa8d39c3,
"Generate 96-bit tables using this polynomial "
"(most significant 64 bits)");
DEFINE_int32(
poly96_l,
0xb679ec37,
"Generate 96-bit tables using this polynomial "
"(least significant 32 bits)");
DEFINE_int64(
poly128_m,
0xc91bff9b8768b51b,
"Generate 128-bit tables using this polynomial "
"(most significant 64 bits)");
DEFINE_int64(
poly128_l,
0x8c5d5853bd77b0d3,
"Generate 128-bit tables using this polynomial "
"(least significant 64 bits)");
DEFINE_string(install_dir, ".", "Direectory to place output files in");
DEFINE_string(fbcode_dir, "", "fbcode directory (ignored)");
namespace {
template <int DEG>
void computeTables(FILE* file, const FingerprintPolynomial<DEG>& poly) {
uint64_t table[8][256][FingerprintPolynomial<DEG>::size()];
// table[i][q] is Q(X) * X^(k+8*i) mod P(X),
// where k is the number of bits in the fingerprint (and deg(P)) and
// Q(X) = q7*X^7 + q6*X^6 + ... + q1*X + q0 is a degree-7 polyonomial
// whose coefficients are the bits of q.
for (uint16_t x = 0; x < 256; x++) {
FingerprintPolynomial<DEG> t;
t.setHigh8Bits(uint8_t(x));
for (int i = 0; i < 8; i++) {
t.mulXkmod(8, poly);
t.write(&(table[i][x][0]));
}
}
// Write the actual polynomial used; this isn't needed during fast
// fingerprint calculation, but it's useful for reference and unittesting.
uint64_t poly_val[FingerprintPolynomial<DEG>::size()];
poly.write(poly_val);
CHECK_ERR(fprintf(
file,
"template <>\n"
"const uint64_t FingerprintTable<%d>::poly[%d] = {",
DEG + 1,
FingerprintPolynomial<DEG>::size()));
for (int j = 0; j < FingerprintPolynomial<DEG>::size(); j++) {
CHECK_ERR(fprintf(file, "%s%" PRIu64 "LLU", j ? ", " : "", poly_val[j]));
}
CHECK_ERR(fprintf(file, "};\n\n"));
// Write the tables.
CHECK_ERR(fprintf(
file,
"template <>\n"
"const uint64_t FingerprintTable<%d>::table[8][256][%d] = {\n",
DEG + 1,
FingerprintPolynomial<DEG>::size()));
for (int i = 0; i < 8; i++) {
CHECK_ERR(fprintf(
file,
" // Table %d"
"\n"
" {\n",
i));
for (int x = 0; x < 256; x++) {
CHECK_ERR(fprintf(file, " {"));
for (int j = 0; j < FingerprintPolynomial<DEG>::size(); j++) {
CHECK_ERR(
fprintf(file, "%s%" PRIu64 "LLU", (j ? ", " : ""), table[i][x][j]));
}
CHECK_ERR(fprintf(file, "},\n"));
}
CHECK_ERR(fprintf(file, " },\n"));
}
CHECK_ERR(fprintf(file, "\n};\n\n"));
}
} // namespace
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
std::string name = FLAGS_install_dir + "/" + "FingerprintTables.cpp";
FILE* file = fopen(name.c_str(), "w");
PCHECK(file);
CHECK_ERR(fprintf(
file,
"/**\n"
" * Fingerprint tables for 64-, 96-, and 128-bit Rabin fingerprints.\n"
" *\n"
" * AUTOMATICALLY GENERATED. DO NOT EDIT.\n"
" */\n"
"\n"
"#include <folly/Fingerprint.h>\n"
"\n"
"namespace folly {\n"
"namespace detail {\n"
"\n"));
FingerprintPolynomial<63> poly64((const uint64_t*)&FLAGS_poly64);
computeTables(file, poly64);
uint64_t poly96_val[2];
poly96_val[0] = (uint64_t)FLAGS_poly96_m;
poly96_val[1] = (uint64_t)FLAGS_poly96_l << 32;
FingerprintPolynomial<95> poly96(poly96_val);
computeTables(file, poly96);
uint64_t poly128_val[2];
poly128_val[0] = (uint64_t)FLAGS_poly128_m;
poly128_val[1] = (uint64_t)FLAGS_poly128_l;
FingerprintPolynomial<127> poly128(poly128_val);
computeTables(file, poly128);
CHECK_ERR(fprintf(
file,
"} // namespace detail\n"
"} // namespace folly\n"));
CHECK_ERR(fclose(file));
return 0;
}
......@@ -18,8 +18,6 @@
#include <cstdint>
#include <glog/logging.h>
namespace folly {
namespace detail {
......@@ -33,33 +31,30 @@ namespace detail {
template <int DEG>
class FingerprintPolynomial {
public:
FingerprintPolynomial() {
for (int i = 0; i < size(); i++) {
val_[i] = 0;
}
static constexpr int size() {
return 1 + DEG / 64;
}
explicit FingerprintPolynomial(const uint64_t* vals) {
constexpr FingerprintPolynomial() {}
constexpr explicit FingerprintPolynomial(const uint64_t (&vals)[size()]) {
for (int i = 0; i < size(); i++) {
val_[i] = vals[i];
}
}
void write(uint64_t* out) const {
for (int i = 0; i < size(); i++) {
out[i] = val_[i];
}
constexpr uint64_t get(size_t i) const {
return val_[i];
}
void add(const FingerprintPolynomial<DEG>& other) {
constexpr void add(const FingerprintPolynomial<DEG>& other) {
for (int i = 0; i < size(); i++) {
val_[i] ^= other.val_[i];
}
}
// Multiply by X. The actual degree must be < DEG.
void mulX() {
CHECK_EQ(0u, val_[0] & (1ULL << 63));
constexpr void mulX() {
uint64_t b = 0;
for (int i = size() - 1; i >= 0; i--) {
uint64_t nb = val_[i] >> 63;
......@@ -92,7 +87,7 @@ class FingerprintPolynomial {
// So A(X) * X mod P(X) is:
// the binary representation of A, left shift by 1,
// XOR p if a_(k-1) == 1
void mulXmod(const FingerprintPolynomial<DEG>& p) {
constexpr void mulXmod(const FingerprintPolynomial<DEG>& p) {
bool needXOR = (val_[0] & (1ULL << 63));
val_[0] &= ~(1ULL << 63);
mulX();
......@@ -102,16 +97,14 @@ class FingerprintPolynomial {
}
// Compute (this * X^k) mod P(X) by repeatedly multiplying by X (see above)
void mulXkmod(int k, const FingerprintPolynomial<DEG>& p) {
constexpr void mulXkmod(int k, const FingerprintPolynomial<DEG>& p) {
for (int i = 0; i < k; i++) {
mulXmod(p);
}
}
// add X^k, where k <= DEG
void addXk(int k) {
DCHECK_GE(k, 0);
DCHECK_LE(k, DEG);
constexpr void addXk(int k) {
int word_offset = (DEG - k) / 64;
int bit_offset = 63 - (DEG - k) % 64;
val_[word_offset] ^= (1ULL << bit_offset);
......@@ -120,17 +113,13 @@ class FingerprintPolynomial {
// Set the highest 8 bits to val.
// If val is interpreted as polynomial of degree 7, then this sets *this
// to val * X^(DEG-7)
void setHigh8Bits(uint8_t val) {
constexpr void setHigh8Bits(uint8_t val) {
val_[0] = ((uint64_t)val) << (64 - 8);
for (int i = 1; i < size(); i++) {
val_[i] = 0;
}
}
static constexpr int size() {
return 1 + DEG / 64;
}
private:
// Internal representation: big endian
// val_[0] contains the highest order coefficients, with bit 63 as the
......@@ -138,7 +127,7 @@ class FingerprintPolynomial {
//
// If DEG+1 is not a multiple of 64, val_[size()-1] only uses the highest
// order (DEG+1)%64 bits (the others are always 0)
uint64_t val_[1 + DEG / 64];
uint64_t val_[size()] = {};
};
} // namespace detail
......
......@@ -61,7 +61,9 @@ class SlowFingerprint {
}
void write(uint64_t* out) const {
fp_.write(out);
for (int i = 0; i < fp_.size(); ++i) {
out[i] = fp_.get(i);
}
}
private:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment