| 1692 | |
| 1693 | template <bool ignoreNullKeys> |
| 1694 | std::string HashTable<ignoreNullKeys>::toString() { |
| 1695 | std::stringstream out; |
| 1696 | out << "[HashTable keys: " << hashers_.size() |
| 1697 | << " hash mode: " << modeString(hashMode_) << " capacity: " << capacity_ |
| 1698 | << " distinct count: " << numDistinct_ |
| 1699 | << " tombstones count: " << numTombstones_ << "]"; |
| 1700 | if (table_ == nullptr) { |
| 1701 | out << " (no table)"; |
| 1702 | } |
| 1703 | |
| 1704 | for (auto& hasher : hashers_) { |
| 1705 | out << std::endl << hasher->toString(); |
| 1706 | } |
| 1707 | out << std::endl; |
| 1708 | |
| 1709 | if (kTrackLoads) { |
| 1710 | out << fmt::format( |
| 1711 | "{} probes {} tag loads {} row loads {} hits", |
| 1712 | numProbes_, |
| 1713 | numTagLoads_, |
| 1714 | numRowLoads_, |
| 1715 | numHits_) |
| 1716 | << std::endl; |
| 1717 | } |
| 1718 | |
| 1719 | if (hashMode_ == HashMode::kArray) { |
| 1720 | int64_t occupied = 0; |
| 1721 | if (table_ && tableAllocation_.data() && tableAllocation_.size()) { |
| 1722 | // 'size_' and 'table_' may not be set if initializing. |
| 1723 | uint64_t size = std::min<uint64_t>( |
| 1724 | tableAllocation_.size() / sizeof(char*), capacity_); |
| 1725 | for (int32_t i = 0; i < size; ++i) { |
| 1726 | occupied += table_[i] != nullptr; |
| 1727 | } |
| 1728 | } |
| 1729 | out << "Total slots used: " << occupied << std::endl; |
| 1730 | } else { |
| 1731 | int64_t occupied = 0; |
| 1732 | |
| 1733 | // Count of buckets indexed by the number of non-empty slots. |
| 1734 | // Each bucket has 16 slots. Hence, the number of non-empty slots is between |
| 1735 | // 0 and 16 (17 possible values). |
| 1736 | int64_t numBuckets[sizeof(TagVector) + 1] = {}; |
| 1737 | for (int64_t bucketOffset = 0; bucketOffset < sizeMask_; |
| 1738 | bucketOffset += kBucketSize) { |
| 1739 | auto tags = loadTags(bucketOffset); |
| 1740 | auto filled = simd::toBitMask(tags != TagVector::broadcast(0)); |
| 1741 | auto numOccupied = __builtin_popcount(filled); |
| 1742 | |
| 1743 | ++numBuckets[numOccupied]; |
| 1744 | occupied += numOccupied; |
| 1745 | } |
| 1746 | |
| 1747 | out << "Total buckets: " << (sizeMask_ / kBucketSize + 1) << std::endl; |
| 1748 | out << "Total slots used: " << occupied << std::endl; |
| 1749 | for (auto i = 1; i < sizeof(TagVector) + 1; ++i) { |
| 1750 | if (numBuckets[i] > 0) { |
| 1751 | out << numBuckets[i] << " buckets with " << i << " slots used" |