Skip to content

Commit

Permalink
Reduce unnnecessary allocations for some bits_ops operations (Reverse…
Browse files Browse the repository at this point in the history
… and the LongestCommonPrefix* functions)

Avoids building intermediate bit vectors, sticking entirely in the Bits & InlineBitmap space.

PiperOrigin-RevId: 627944570
  • Loading branch information
ericastor authored and copybara-github committed Apr 25, 2024
1 parent 8a0506c commit dc8f6b0
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 16 deletions.
46 changes: 30 additions & 16 deletions xls/ir/bits_ops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -500,9 +500,11 @@ Bits OneHotMsbToLsb(const Bits& bits) {
}

Bits Reverse(const Bits& bits) {
auto bits_vector = bits.ToBitVector();
std::reverse(bits_vector.begin(), bits_vector.end());
return Bits(bits_vector);
InlineBitmap reversed_bitmap(bits.bit_count());
for (int64_t i = 0; i < bits.bit_count(); ++i) {
reversed_bitmap.Set(bits.bit_count() - i - 1, bits.Get(i));
}
return Bits::FromBitmap(std::move(reversed_bitmap));
}

Bits DropLeadingZeroes(const Bits& bits) {
Expand Down Expand Up @@ -561,27 +563,39 @@ Bits LongestCommonPrefixLSB(absl::Span<const Bits> bits_span) {
CHECK_EQ(bits.bit_count(), input_size);
}

absl::InlinedVector<bool, 1> result_vector;
result_vector.reserve(input_size);
for (int32_t i = 0; i < input_size; ++i) {
bool expected_bit = bits_span[0].Get(i);
for (const Bits& bits : bits_span) {
if (bits.Get(i) != expected_bit) {
return Bits(result_vector);
int64_t first_difference = input_size;
for (int64_t i = 0; first_difference == input_size && i < input_size; ++i) {
for (const Bits& bits : bits_span.subspan(1)) {
if (bits_span[0].Get(i) != bits.Get(i)) {
first_difference = i;
break;
}
}
result_vector.push_back(expected_bit);
}
return Bits(result_vector);
return bits_span[0].Slice(0, first_difference);
}

Bits LongestCommonPrefixMSB(absl::Span<const Bits> bits_span) {
std::vector<Bits> reversed;
reversed.reserve(bits_span.size());
if (bits_span.empty()) {
return Bits();
}

int64_t input_size = bits_span[0].bit_count();
for (const Bits& bits : bits_span) {
reversed.push_back(bits_ops::Reverse(bits));
CHECK_EQ(bits.bit_count(), input_size);
}

int64_t first_difference = -1;
for (int64_t i = input_size - 1; first_difference == -1 && i >= 0; --i) {
for (const Bits& bits : bits_span.subspan(1)) {
if (bits_span[0].Get(i) != bits.Get(i)) {
first_difference = i;
break;
}
}
}
return bits_ops::Reverse(bits_ops::LongestCommonPrefixLSB(reversed));
return bits_span[0].Slice(first_difference + 1,
input_size - first_difference - 1);
}

} // namespace bits_ops
Expand Down
35 changes: 35 additions & 0 deletions xls/ir/bits_ops_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,41 @@ TEST(BitsOpsTest, LongestCommonPrefixLSBMoreThan2) {
EXPECT_EQ(bits_ops::LongestCommonPrefixLSB({x, y, z}), expected);
}

TEST(BitsOpsTest, LongestCommonPrefixMSBTypical) {
// Simple typical example
Bits x(absl::InlinedVector<bool, 1>{1, 1, 0, 1, 1});
Bits y(absl::InlinedVector<bool, 1>{1, 0, 0, 1, 1});
Bits expected(absl::InlinedVector<bool, 1>{0, 1, 1});
EXPECT_EQ(bits_ops::LongestCommonPrefixMSB({x, y}), expected)
<< "lcp: " << bits_ops::LongestCommonPrefixMSB({x, y}).ToDebugString()
<< " , expected: " << expected.ToDebugString();
}

TEST(BitsOpsTest, LongestCommonPrefixMSBEmptyResult) {
// Differ in the first bit => common prefix is the empty bitstring
Bits x(absl::InlinedVector<bool, 1>{1, 1, 0, 1, 0});
Bits y(absl::InlinedVector<bool, 1>{1, 0, 0, 1, 1});
Bits expected(0);
EXPECT_EQ(bits_ops::LongestCommonPrefixMSB({x, y}), expected);
}

TEST(BitsOpsTest, LongestCommonPrefixMSBSame) {
// Everything the same => common prefix is the entire bitstring
Bits x(absl::InlinedVector<bool, 1>{1, 0, 0, 1, 1});
Bits y(absl::InlinedVector<bool, 1>{1, 0, 0, 1, 1});
Bits expected(absl::InlinedVector<bool, 1>{1, 0, 0, 1, 1});
EXPECT_EQ(bits_ops::LongestCommonPrefixMSB({x, y}), expected);
}

TEST(BitsOpsTest, LongestCommonPrefixMSBMoreThan2) {
// Example with more than 2 bitstrings
Bits x(absl::InlinedVector<bool, 1>{1, 1, 1, 1, 0});
Bits y(absl::InlinedVector<bool, 1>{1, 0, 1, 1, 0});
Bits z(absl::InlinedVector<bool, 1>{0, 1, 1, 1, 0});
Bits expected(absl::InlinedVector<bool, 1>{1, 1, 0});
EXPECT_EQ(bits_ops::LongestCommonPrefixMSB({x, y, z}), expected);
}

void TestBinary(const Bits& b, const std::string& expected) {
EXPECT_EQ(BitsToString(b, FormatPreference::kBinary), expected);
EXPECT_EQ(BitsToString(b, FormatPreference::kPlainBinary),
Expand Down

0 comments on commit dc8f6b0

Please sign in to comment.