Commit 4b34e197 by Abseil Team Committed by Copybara-Service

Optimize RemoveCrc32cSuffix.

The implementation can be optimized to not having to perform an ExtendByZero operation.

`RemoveCrc32cSuffix` can simply be implemented as

uint32_t result = static_cast<uint32_t>(full_string_crc) ^
                  static_cast<uint32_t>(suffix_crc);
CrcEngine()->UnextendByZeroes(&result, suffix_len);
return crc32c_t{result};

Math proof that this change is correct:

`ComputeCrc32c` actually computes the following:

ConditionedCRC(data) = UnconditionedCRC(data) + StartValue(data) + ~0
with:
StartValue(data) = ~0 * x**BitLength(data) mod P
(with `+` being a carry-less add, ie an xor).

``UnconditionedCRC` in the context of this description means: no initial or final xor with ~0 and a starting value of zero - ie the result that `CrcEngine()->Extend` would give you with a starting value of 0.

Given `full_string_crc` and `suffix_crc` (both conditioned CRCs), xoring them together results in:

(1):

full_string_crc + suffix_crc =
  UnconditionedCRC(full_string) + StartValue(full_string) + ~0
+ UnconditionedCRC(suffix)      + StartValue(suffix)      + ~0

Since `+` is carry-less addition (ie an XOR), the two ~0 cancel each other out.

(2)

full_string_crc + suffix_crc =
  UnconditionedCRC(full_string) + StartValue(full_string)
+ UnconditionedCRC(suffix)      + StartValue(suffix)

We can make use of the fact that:

(3)
UnconditionedCRC(full_string) + UnConditionedCRC(suffix)
= UnconditionedCRC(full_string_with_suffix_replaced_by_zeros).

Ie, UnconditionedCRC("AABBB") + UnconditionedCRC("BBB") = UnconditionedCRC("AA\0\0\0")

Putting (3) into (2) yields:

(4)
full_string_crc + suffix_crc =
 UnconditionedCRC(full_string_with_suffix_replaced_by_zeros)
 + StartValue(full_string) + StartValue(suffix)

Using:

(5)
UnconditionedCRC(full_string_with_suffix_replaced_by_zeros)
=
UnconditionedCRC(full_string_without_suffix) * x**Bitlength(suffix) mod P

and putting (5) into (4)

(6)
full_string_crc + suffix_crc =
 UnconditionedCRC(full_string_without_suffix) * x**Bitlength(suffix) mod P +
 StartValue(full_string) + StartValue(suffix)

Using
(7)
StartValue(full_string) =  ~0 * x ** Bitlength(full_string) mod P

and
(8)
StartValue(suffix) = ~0 * x**BitLength(suffix) mod P

Putting (7) and (8) in (6):
(9):
full_string_crc + suffix_crc =
 UnconditionedCRC(full_string_without_suffix) * x**(Bitlength(suffix)) mod P
 +  ~0 * x ** Bitlength(full_string) mod P
 +  ~0 * x ** BitLength(suffix) mod P

Using:
(10)
Bitlength(full_string) =
 Bitlength(full_string_without_suffix) +
 Bitlength(suffix)

And putting (10) in (9):

(11)
full_string_crc + suffix_crc =
 UnconditionedCRC(full_string_without_suffix) * x**(Bitlength(suffix)) mod P
 +  ~0 * x ** (Bitlength(full_string_without_suffix) + Bitlength(suffix)) mod P
 +  ~0 * x ** BitLength(suffix) mod P

using x**(A+B) = x**A * x**B results in:

(12)
full_string_crc + suffix_crc =
 UnconditionedCRC(full_string_without_suffix) * x**(Bitlength(suffix)) mod P
 +  [ ~0 * x ** Bitlength(full_string_without_suffix) * x**Bitlength(suffix)] mod P
 +  ~0 * x ** BitLength(suffix) mod P

using A mod P + B mod P + C mod P = (A + B + C) mod P:
(this works in carry-less arithmetic)

(13)
full_string_crc + suffix_crc = [
 UnconditionedCRC(full_string_without_suffix) * x**(Bitlength(suffix))
 +  [ ~0 * x ** Bitlength(full_string_without_suffix) * x**Bitlength(suffix)]
 +  ~0 * x ** BitLength(suffix) ] mod P

Factor out x**Bitlength(suffix):

(14)
full_string_crc + suffix_crc = [
 x**(Bitlength(suffix)) * [
  UnconditionedCRC(full_string_without_suffix)
 +  ~0 * x ** Bitlength(full_string_without_suffix)
 +  ~0 ] mod P

Using:
(15)
ConditionedCRC(full_string_without_suffix) =
  [ UnconditionedCRC(full_string_without_suffix)
    + ~0 * x ** Bitlength(full_string_without_suffix) ] mod P + ~0
 =
  [ UnconditionedCRC(full_string_without_suffix)
    + ~0 * x ** Bitlength(full_string_without_suffix) + ~0] mod P

(~0 is less than x**32, so ~0 mod P = ~0)

Putting (15) in (14) results in:

full_string_crc + suffix_crc = [
 x**(Bitlength(suffix)) * ConditionedCRC(full_string_without_suffix)] mod P

Or:

(16)
 ConditionedCRC(full_string_without_suffix) =
  (full_string_crc + suffix_crc) * x**(-Bitlength(suffix)) mod P

A multiplication by x**(-8*bytelength) mod P is implemented by `CrcEngine()->UnextendByZeros`.

PiperOrigin-RevId: 502659140
Change-Id: I66b0700d258f948be0885f691370b73d7fad56e3
parent e1c897f0
......@@ -89,12 +89,10 @@ crc32c_t MemcpyCrc32c(void* dest, const void* src, size_t count,
// This operation has a runtime cost of O(log(`suffix_len`))
crc32c_t RemoveCrc32cSuffix(crc32c_t full_string_crc, crc32c_t suffix_crc,
size_t suffix_len) {
crc32c_t crc_with_suffix_zeroed = crc32c_t{
static_cast<uint32_t>(suffix_crc) ^
static_cast<uint32_t>(full_string_crc) ^
static_cast<uint32_t>(ExtendCrc32cByZeroes(crc32c_t{0}, suffix_len))};
return crc_internal::UnextendCrc32cByZeroes(
crc_with_suffix_zeroed, suffix_len);
uint32_t result = static_cast<uint32_t>(full_string_crc) ^
static_cast<uint32_t>(suffix_crc);
CrcEngine()->UnextendByZeroes(&result, suffix_len);
return crc32c_t{result};
}
ABSL_NAMESPACE_END
......
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