mirror of
https://github.com/abseil/abseil-cpp.git
synced 2026-06-04 12:07:05 +08:00
Optimize multiply() (renamed to MultiplyWithExtraX33()) to eliminate
several instructions that were present only to avoid introducing an
extra factor of x^33 into the multiplication. It's actually fine to
introduce the extra factor of x^33 as long as it's canceled out with an
extra factor of x^-33 in all the kCRC32CPowers[] entries.
To make this work, the number of bits dropped by ComputeZeroConstant()
had to be increased from 2 to at least 3, since 2^(i + 3 +
kNumDroppedBits) - 33 must be >= 0 for all i including i=0; otherwise
kCRC32CPowers[0] would need a negative power of x. However, this is
fine since it's more efficient to utilize CRC32_u32() and CRC32_u64()
for bits 2 and 3 anyway. So, increase kNumDroppedBits to 4.
Add a Python script that generates the updated kCRC32CPowers[]. It
isn't wired up to the build system, but rather is just added so that
kCRC32CPowers[] can be reproduced.
Also add a test which tests ExtendCrc32cByZeroes() with all the length
bits, thus testing all the entries of kCRC32CPowers[].
Note that the kCRC32CPowers[] generation script and new test case are
things we should have had anyway, regardless of the x^33 optimization.
This change slightly improves the performance of Extend() for lengths
greater than or equal to 2048 bytes, and also the performance of
ExtendByZeroes(). It also slightly reduces the binary code size.
Before:
BM_Calculate/2048 84.3 ns 84.3 ns 8307735
BM_Calculate/10000 376 ns 375 ns 1865976
BM_Calculate/500000 18538 ns 18531 ns 37813
BM_ExtendByZeroes/1 3.55 ns 3.55 ns 197111095
BM_ExtendByZeroes/10 3.90 ns 3.89 ns 179773877
BM_ExtendByZeroes/100 6.06 ns 6.06 ns 115242160
BM_ExtendByZeroes/1000 12.0 ns 12.0 ns 58078004
BM_ExtendByZeroes/10000 9.97 ns 9.97 ns 70335772
BM_ExtendByZeroes/100000 12.1 ns 12.1 ns 58157829
BM_ExtendByZeroes/1000000 14.4 ns 14.4 ns 48527365
After:
BM_Calculate/2048 82.8 ns 82.7 ns 8478296
BM_Calculate/10000 375 ns 375 ns 1869663
BM_Calculate/500000 18547 ns 18538 ns 37846
BM_ExtendByZeroes/1 2.96 ns 2.96 ns 236772500
BM_ExtendByZeroes/10 3.85 ns 3.85 ns 182059238
BM_ExtendByZeroes/100 5.42 ns 5.42 ns 129077546
BM_ExtendByZeroes/1000 9.43 ns 9.42 ns 74232457
BM_ExtendByZeroes/10000 8.14 ns 8.14 ns 86244218
BM_ExtendByZeroes/100000 10.7 ns 10.7 ns 65467391
BM_ExtendByZeroes/1000000 11.0 ns 11.0 ns 63575936
PiperOrigin-RevId: 786828855
Change-Id: I6208625fd1c35c2c137e756cf5fadc1adccfdd5d
258 lines
8.6 KiB
C++
258 lines
8.6 KiB
C++
// Copyright 2022 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 "absl/crc/crc32c.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <limits>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <tuple>
|
|
|
|
#include "gtest/gtest.h"
|
|
#include "absl/crc/internal/crc32c.h"
|
|
#include "absl/strings/str_cat.h"
|
|
#include "absl/strings/str_format.h"
|
|
#include "absl/strings/string_view.h"
|
|
|
|
namespace {
|
|
|
|
TEST(CRC32C, RFC3720) {
|
|
// Test the results of the vectors from
|
|
// https://www.rfc-editor.org/rfc/rfc3720#appendix-B.4
|
|
char data[32];
|
|
|
|
// 32 bytes of ones.
|
|
memset(data, 0, sizeof(data));
|
|
EXPECT_EQ(absl::ComputeCrc32c(absl::string_view(data, sizeof(data))),
|
|
absl::crc32c_t{0x8a9136aa});
|
|
|
|
// 32 bytes of ones.
|
|
memset(data, 0xff, sizeof(data));
|
|
EXPECT_EQ(absl::ComputeCrc32c(absl::string_view(data, sizeof(data))),
|
|
absl::crc32c_t{0x62a8ab43});
|
|
|
|
// 32 incrementing bytes.
|
|
for (int i = 0; i < 32; ++i) data[i] = static_cast<char>(i);
|
|
EXPECT_EQ(absl::ComputeCrc32c(absl::string_view(data, sizeof(data))),
|
|
absl::crc32c_t{0x46dd794e});
|
|
|
|
// 32 decrementing bytes.
|
|
for (int i = 0; i < 32; ++i) data[i] = static_cast<char>(31 - i);
|
|
EXPECT_EQ(absl::ComputeCrc32c(absl::string_view(data, sizeof(data))),
|
|
absl::crc32c_t{0x113fdb5c});
|
|
|
|
// An iSCSI - SCSI Read (10) Command PDU.
|
|
constexpr uint8_t cmd[48] = {
|
|
0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
|
|
0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x28, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
};
|
|
EXPECT_EQ(absl::ComputeCrc32c(absl::string_view(
|
|
reinterpret_cast<const char*>(cmd), sizeof(cmd))),
|
|
absl::crc32c_t{0xd9963a56});
|
|
}
|
|
|
|
std::string TestString(size_t len) {
|
|
std::string result;
|
|
result.reserve(len);
|
|
for (size_t i = 0; i < len; ++i) {
|
|
result.push_back(static_cast<char>(i % 256));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
TEST(CRC32C, Compute) {
|
|
EXPECT_EQ(absl::ComputeCrc32c(""), absl::crc32c_t{0});
|
|
EXPECT_EQ(absl::ComputeCrc32c("hello world"), absl::crc32c_t{0xc99465aa});
|
|
}
|
|
|
|
TEST(CRC32C, Extend) {
|
|
uint32_t base = 0xC99465AA; // CRC32C of "Hello World"
|
|
std::string extension = "Extension String";
|
|
|
|
EXPECT_EQ(
|
|
absl::ExtendCrc32c(absl::crc32c_t{base}, extension),
|
|
absl::crc32c_t{0xD2F65090}); // CRC32C of "Hello WorldExtension String"
|
|
}
|
|
|
|
TEST(CRC32C, ExtendByZeroes) {
|
|
std::string base = "hello world";
|
|
absl::crc32c_t base_crc = absl::crc32c_t{0xc99465aa};
|
|
|
|
constexpr size_t kExtendByValues[] = {100, 10000, 100000};
|
|
for (const size_t extend_by : kExtendByValues) {
|
|
SCOPED_TRACE(extend_by);
|
|
absl::crc32c_t crc2 = absl::ExtendCrc32cByZeroes(base_crc, extend_by);
|
|
EXPECT_EQ(crc2, absl::ComputeCrc32c(base + std::string(extend_by, '\0')));
|
|
}
|
|
}
|
|
|
|
// Test ExtendCrc32cByZeroes() for the full range of the size_t length,
|
|
// including every bit. This is important because ExtendCrc32cByZeroes() is
|
|
// implemented using an array of constants, where each entry in the array is
|
|
// used only when a particular bit in the size_t length is set. This test
|
|
// verifies that every entry in that array is correct.
|
|
TEST(CRC32C, ExtendByZeroesAllLengthBits) {
|
|
absl::crc32c_t base_crc = absl::crc32c_t{0xc99465aa};
|
|
const std::array<std::tuple<uint64_t, absl::crc32c_t>, 5> kTestCases = {{
|
|
{0, absl::crc32c_t(0xc99465aa)},
|
|
{std::numeric_limits<uint32_t>::max(), absl::crc32c_t(0x9b1d5aaa)},
|
|
{0x12345678, absl::crc32c_t(0xcf0e9553)},
|
|
{std::numeric_limits<uint64_t>::max(), absl::crc32c_t(0xf5bff489)},
|
|
{0x12345678abcdefff, absl::crc32c_t(0xaa1ffb0b)},
|
|
}};
|
|
for (const auto &test_case : kTestCases) {
|
|
uint64_t length = std::get<0>(test_case);
|
|
absl::crc32c_t expected_value = std::get<1>(test_case);
|
|
SCOPED_TRACE(length);
|
|
if (length > std::numeric_limits<size_t>::max()) {
|
|
// On 32-bit platforms, 64-bit lengths cannot be used or tested.
|
|
continue;
|
|
}
|
|
EXPECT_EQ(absl::ExtendCrc32cByZeroes(base_crc, static_cast<size_t>(length)),
|
|
expected_value);
|
|
}
|
|
}
|
|
|
|
TEST(CRC32C, UnextendByZeroes) {
|
|
constexpr size_t kExtendByValues[] = {2, 200, 20000, 200000, 20000000};
|
|
constexpr size_t kUnextendByValues[] = {0, 100, 10000, 100000, 10000000};
|
|
|
|
for (auto seed_crc : {absl::crc32c_t{0}, absl::crc32c_t{0xc99465aa}}) {
|
|
SCOPED_TRACE(seed_crc);
|
|
for (const size_t size_1 : kExtendByValues) {
|
|
for (const size_t size_2 : kUnextendByValues) {
|
|
size_t extend_size = std::max(size_1, size_2);
|
|
size_t unextend_size = std::min(size_1, size_2);
|
|
SCOPED_TRACE(extend_size);
|
|
SCOPED_TRACE(unextend_size);
|
|
|
|
// Extending by A zeroes an unextending by B<A zeros should be identical
|
|
// to extending by A-B zeroes.
|
|
absl::crc32c_t crc1 = seed_crc;
|
|
crc1 = absl::ExtendCrc32cByZeroes(crc1, extend_size);
|
|
crc1 = absl::crc_internal::UnextendCrc32cByZeroes(crc1, unextend_size);
|
|
|
|
absl::crc32c_t crc2 = seed_crc;
|
|
crc2 = absl::ExtendCrc32cByZeroes(crc2, extend_size - unextend_size);
|
|
|
|
EXPECT_EQ(crc1, crc2);
|
|
}
|
|
}
|
|
}
|
|
|
|
constexpr size_t kSizes[] = {0, 1, 100, 10000};
|
|
for (const size_t size : kSizes) {
|
|
SCOPED_TRACE(size);
|
|
std::string string_before = TestString(size);
|
|
std::string string_after = string_before + std::string(size, '\0');
|
|
|
|
absl::crc32c_t crc_before = absl::ComputeCrc32c(string_before);
|
|
absl::crc32c_t crc_after = absl::ComputeCrc32c(string_after);
|
|
|
|
EXPECT_EQ(crc_before,
|
|
absl::crc_internal::UnextendCrc32cByZeroes(crc_after, size));
|
|
}
|
|
}
|
|
|
|
TEST(CRC32C, Concat) {
|
|
std::string hello = "Hello, ";
|
|
std::string world = "world!";
|
|
std::string hello_world = absl::StrCat(hello, world);
|
|
|
|
absl::crc32c_t crc_a = absl::ComputeCrc32c(hello);
|
|
absl::crc32c_t crc_b = absl::ComputeCrc32c(world);
|
|
absl::crc32c_t crc_ab = absl::ComputeCrc32c(hello_world);
|
|
|
|
EXPECT_EQ(absl::ConcatCrc32c(crc_a, crc_b, world.size()), crc_ab);
|
|
}
|
|
|
|
TEST(CRC32C, Memcpy) {
|
|
constexpr size_t kBytesSize[] = {0, 1, 20, 500, 100000};
|
|
for (size_t bytes : kBytesSize) {
|
|
SCOPED_TRACE(bytes);
|
|
std::string sample_string = TestString(bytes);
|
|
std::string target_buffer = std::string(bytes, '\0');
|
|
|
|
absl::crc32c_t memcpy_crc =
|
|
absl::MemcpyCrc32c(&(target_buffer[0]), sample_string.data(), bytes);
|
|
absl::crc32c_t compute_crc = absl::ComputeCrc32c(sample_string);
|
|
|
|
EXPECT_EQ(memcpy_crc, compute_crc);
|
|
EXPECT_EQ(sample_string, target_buffer);
|
|
}
|
|
}
|
|
|
|
TEST(CRC32C, RemovePrefix) {
|
|
std::string hello = "Hello, ";
|
|
std::string world = "world!";
|
|
std::string hello_world = absl::StrCat(hello, world);
|
|
|
|
absl::crc32c_t crc_a = absl::ComputeCrc32c(hello);
|
|
absl::crc32c_t crc_b = absl::ComputeCrc32c(world);
|
|
absl::crc32c_t crc_ab = absl::ComputeCrc32c(hello_world);
|
|
|
|
EXPECT_EQ(absl::RemoveCrc32cPrefix(crc_a, crc_ab, world.size()), crc_b);
|
|
}
|
|
|
|
TEST(CRC32C, RemoveSuffix) {
|
|
std::string hello = "Hello, ";
|
|
std::string world = "world!";
|
|
std::string hello_world = absl::StrCat(hello, world);
|
|
|
|
absl::crc32c_t crc_a = absl::ComputeCrc32c(hello);
|
|
absl::crc32c_t crc_b = absl::ComputeCrc32c(world);
|
|
absl::crc32c_t crc_ab = absl::ComputeCrc32c(hello_world);
|
|
|
|
EXPECT_EQ(absl::RemoveCrc32cSuffix(crc_ab, crc_b, world.size()), crc_a);
|
|
}
|
|
|
|
TEST(CRC32C, InsertionOperator) {
|
|
{
|
|
std::ostringstream buf;
|
|
buf << absl::crc32c_t{0xc99465aa};
|
|
EXPECT_EQ(buf.str(), "c99465aa");
|
|
}
|
|
{
|
|
std::ostringstream buf;
|
|
buf << absl::crc32c_t{0};
|
|
EXPECT_EQ(buf.str(), "00000000");
|
|
}
|
|
{
|
|
std::ostringstream buf;
|
|
buf << absl::crc32c_t{17};
|
|
EXPECT_EQ(buf.str(), "00000011");
|
|
}
|
|
}
|
|
|
|
TEST(CRC32C, AbslStringify) {
|
|
// StrFormat
|
|
EXPECT_EQ(absl::StrFormat("%v", absl::crc32c_t{0xc99465aa}), "c99465aa");
|
|
EXPECT_EQ(absl::StrFormat("%v", absl::crc32c_t{0}), "00000000");
|
|
EXPECT_EQ(absl::StrFormat("%v", absl::crc32c_t{17}), "00000011");
|
|
|
|
// StrCat
|
|
EXPECT_EQ(absl::StrCat(absl::crc32c_t{0xc99465aa}), "c99465aa");
|
|
EXPECT_EQ(absl::StrCat(absl::crc32c_t{0}), "00000000");
|
|
EXPECT_EQ(absl::StrCat(absl::crc32c_t{17}), "00000011");
|
|
}
|
|
|
|
} // namespace
|