Skip to content

Commit

Permalink
Merge pull request #1567 from evoskuil/master
Browse files Browse the repository at this point in the history
Use more efficient native single block hash, comments.
  • Loading branch information
evoskuil authored Dec 12, 2024
2 parents 6ebf3b5 + 7214571 commit f732eab
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 65 deletions.
2 changes: 1 addition & 1 deletion include/bitcoin/system/hash/sha/algorithm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ class algorithm
static digest_t native_finalize_second(const state_t& half) NOEXCEPT;
static digest_t native_finalize_double(state_t& half, size_t blocks) NOEXCEPT;

////static digest_t native_hash(const block_t& block) NOEXCEPT;
static digest_t native_hash(const block_t& block) NOEXCEPT;
static digest_t native_hash(const half_t& half) NOEXCEPT;
static digest_t native_hash(const half_t& left, const half_t& right) NOEXCEPT;

Expand Down
2 changes: 2 additions & 0 deletions include/bitcoin/system/impl/hash/sha/algorithm_iterate.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ template <size_t Size>
INLINE void CLASS::
iterate_native(state_t& state, const ablocks_t<Size>& blocks) NOEXCEPT
{
// Native hash() does not have an optimal array override because there is
// no advantage to array sizing without the benefit of prescheduling.
iblocks_t iblocks{ array_cast<byte_t>(blocks) };
native_transform(state, iblocks);
}
Expand Down
123 changes: 73 additions & 50 deletions include/bitcoin/system/impl/hash/sha/algorithm_native.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,33 @@ native_transform(state_t& state, const auto& block) NOEXCEPT
// There is no benefit to caching pading because it is not prescheduled.
// ----------------------------------------------------------------------------

// TODO: These transitions require state to be unloaded/loaded and
// shuffled/unshuffled, whereas this is not logically necessary. This is a
// fixed cost imposed once for any accumulation (which is inconsequential for
// larger iterations), but reduces efficiency for lower block counts and hash
// doubling. Large iterations are 15-16% wheras small iterations are 20-26%.
// native_transform -> native_transform -> native_finalize
// native_transform -> native_finalize
// This can be resolved in the non-iterator scenarios (below) through
// implementation of a finalizing and a doubling native_transform. This means
// that padding must be incorporated, however since it is not prescheduled or
// cached this is not an issue.

TEMPLATE
template <size_t Blocks>
typename CLASS::digest_t CLASS::
native_finalize(state_t& state) NOEXCEPT
{
return native_finalize(state, Blocks);
}

TEMPLATE
typename CLASS::digest_t CLASS::
native_finalize(state_t& state, size_t blocks) NOEXCEPT
{
return native_finalize(state, pad_blocks(blocks));
}

TEMPLATE
typename CLASS::digest_t CLASS::
native_finalize(state_t& state, const words_t& pad) NOEXCEPT
Expand All @@ -248,21 +275,6 @@ native_finalize(state_t& state, const words_t& pad) NOEXCEPT
return digest;
}

TEMPLATE
template <size_t Blocks>
typename CLASS::digest_t CLASS::
native_finalize(state_t& state) NOEXCEPT
{
return native_finalize(state, Blocks);
}

TEMPLATE
typename CLASS::digest_t CLASS::
native_finalize(state_t& state, size_t blocks) NOEXCEPT
{
return native_finalize(state, pad_blocks(blocks));
}

TEMPLATE
typename CLASS::digest_t CLASS::
native_finalize_second(const state_t& state) NOEXCEPT
Expand All @@ -273,9 +285,9 @@ native_finalize_second(const state_t& state) NOEXCEPT
// Hash a state value and finalize it.
auto state2 = H::get;
words_t block{};
inject_left(block, state); // swapped
pad_half(block); // swapped
return native_finalize(state2, block); // no block swap (swaps state)
inject_left(block, state);
pad_half(block);
return native_finalize(state2, block);
}

TEMPLATE
Expand All @@ -284,30 +296,40 @@ native_finalize_double(state_t& state, size_t blocks) NOEXCEPT
{
// Complete first hash by transforming padding, but don't convert state.
auto block = pad_blocks(blocks);
native_transform<false>(state, block); // no swap
native_transform<false>(state, block);

// This is native_finalize_second() but reuses the initial block.
auto state2 = H::get;
inject_left(block, state); // swapped
pad_half(block); // swapped
return native_finalize(state2, block); // no block swap (swaps state)
inject_left(block, state);
pad_half(block);
return native_finalize(state2, block);
}

// Hash functions start with BE data and end with BE digest_t.
// ----------------------------------------------------------------------------

TEMPLATE
typename CLASS::digest_t CLASS::
native_hash(const block_t& block) NOEXCEPT
{
auto state = H::get;
native_transform<true>(state, block);
return native_finalize(state, pad_block());
}

TEMPLATE
typename CLASS::digest_t CLASS::
native_hash(const half_t& half) NOEXCEPT
{
// No hash(state_t) optimizations for sha160 (requires chunk_t/half_t).
static_assert(is_same_type<state_t, chunk_t>);

// input_left is a non-native endianness conversion.
auto state = H::get;
words_t block{};
input_left(block, half); // swaps
pad_half(block); // swapped
return native_finalize(state, block); // no block swap (swaps state)
input_left(block, half);
pad_half(block);
return native_finalize(state, block);
}

TEMPLATE
Expand All @@ -316,10 +338,10 @@ native_hash(const half_t& left, const half_t& right) NOEXCEPT
{
auto state = H::get;
words_t block{};
inject_left(block, array_cast<word_t>(left)); // unswapped
inject_right(block, array_cast<word_t>(right)); // unswapped
native_transform<true>(state, block); // swap
return native_finalize<one>(state); // no block swap (swaps state)
inject_left(block, array_cast<word_t>(left));
inject_right(block, array_cast<word_t>(right));
native_transform<true>(state, block);
return native_finalize<one>(state);
}

// Double hash functions start with BE data and end with BE digest_t.
Expand All @@ -330,32 +352,33 @@ typename CLASS::digest_t CLASS::
native_double_hash(const block_t& block) NOEXCEPT
{
auto state = H::get;
native_transform<true>(state, block); // swap
native_transform<false>(state, pad_block()); // swapped
native_transform<true>(state, block);
native_transform<false>(state, pad_block());

// Second hash
words_t block2{};
inject_left(block2, state); // swapped
pad_half(block2); // swapped
state = H::get; // [reuse state var]
return native_finalize(state, block2); // no block swap (swaps state)
inject_left(block2, state);
pad_half(block2);
state = H::get;
return native_finalize(state, block2);
}

TEMPLATE
typename CLASS::digest_t CLASS::
native_double_hash(const half_t& half) NOEXCEPT
{
// input_left is a non-native endianness conversion.
auto state = H::get;
words_t block{};
input_left(block, half); // swaps
pad_half(block); // swapped
native_transform<false>(state, block); // no block swap
input_left(block, half);
pad_half(block);
native_transform<false>(state, block);

// Second hash
inject_left(block, state); // swapped
pad_half(block); // swapped
state = H::get; // [reuse state var]
return native_finalize(state, block); // no block swap (swaps state)
inject_left(block, state);
pad_half(block);
state = H::get;
return native_finalize(state, block);
}

TEMPLATE
Expand All @@ -364,16 +387,16 @@ native_double_hash(const half_t& left, const half_t& right) NOEXCEPT
{
auto state = H::get;
words_t block{};
inject_left(block, array_cast<word_t>(left)); // unswapped
inject_right(block, array_cast<word_t>(right)); // unswapped
native_transform<true>(state, block); // swap
native_transform<false>(state, pad_block()); // swapped
inject_left(block, array_cast<word_t>(left));
inject_right(block, array_cast<word_t>(right));
native_transform<true>(state, block);
native_transform<false>(state, pad_block());

// Second hash
inject_left(block, state); // swapped
pad_half(block); // swapped
state = H::get; // [reuse state var]
return native_finalize(state, block); // no block swap (swaps state)
inject_left(block, state);
pad_half(block);
state = H::get;
return native_finalize(state, block);
}

} // namespace sha
Expand Down
17 changes: 15 additions & 2 deletions include/bitcoin/system/impl/hash/sha/algorithm_single.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,21 @@ TEMPLATE
constexpr typename CLASS::digest_t CLASS::
hash(const block_t& block) NOEXCEPT
{
// As an array of a 1 arrays is the same as the array, this compiles away.
return hash(ablocks_t<one>{ block });
if (std::is_constant_evaluated())
{
// As an array of 1 arrays is same as the array, this compiles away.
return hash(ablocks_t<one>{ block });
}
else if constexpr (native && SHA::strength == 256)
{
// Native hash() does not have an optimal array override.
return native_hash(block);
}
else
{
// As an array of 1 arrays is same as the array, this compiles away.
return hash(ablocks_t<one>{ block });
}
}

TEMPLATE
Expand Down
24 changes: 12 additions & 12 deletions include/bitcoin/system/impl/hash/sha/algorithm_stream.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ template <size_t Blocks>
constexpr typename CLASS::digest_t CLASS::
finalize(state_t& state) NOEXCEPT
{
const auto finalize1 = [](state_t& state) NOEXCEPT
const auto finalizer = [](state_t& state) NOEXCEPT
{
buffer_t buffer{};
schedule_n<Blocks>(buffer);
Expand All @@ -66,23 +66,23 @@ finalize(state_t& state) NOEXCEPT

if (std::is_constant_evaluated())
{
return finalize1(state);
return finalizer(state);
}
else if constexpr (native && SHA::strength == 256)
{
return native_finalize<Blocks>(state);
}
else
{
return finalize1(state);
return finalizer(state);
}
}

TEMPLATE
constexpr typename CLASS::digest_t CLASS::
finalize(state_t& state, size_t blocks) NOEXCEPT
{
const auto finalize1 = [](state_t& state, size_t blocks) NOEXCEPT
const auto finalizer = [](state_t& state, size_t blocks) NOEXCEPT
{
buffer_t buffer{};
schedule_n(buffer, blocks);
Expand All @@ -92,15 +92,15 @@ finalize(state_t& state, size_t blocks) NOEXCEPT

if (std::is_constant_evaluated())
{
return finalize1(state, blocks);
return finalizer(state, blocks);
}
else if constexpr (native && SHA::strength == 256)
{
return native_finalize(state, blocks);
}
else
{
return finalize1(state, blocks);
return finalizer(state, blocks);
}
}

Expand All @@ -112,7 +112,7 @@ finalize_second(const state_t& state) NOEXCEPT
static_assert(is_same_type<state_t, chunk_t>);

// This hashes a hash result (state) without the endianness conversion.
const auto finalize2 = [](const state_t& state) NOEXCEPT
const auto finalizer = [](const state_t& state) NOEXCEPT
{
auto state2 = H::get;
buffer_t buffer{};
Expand All @@ -125,15 +125,15 @@ finalize_second(const state_t& state) NOEXCEPT

if (std::is_constant_evaluated())
{
return finalize2(state);
return finalizer(state);
}
else if constexpr (native && SHA::strength == 256)
{
return native_finalize_second(state);
}
else
{
return finalize2(state);
return finalizer(state);
}
}

Expand All @@ -142,7 +142,7 @@ constexpr typename CLASS::digest_t CLASS::
finalize_double(state_t& state, size_t blocks) NOEXCEPT
{
// Pad a hash state from a number of blocks.
const auto finalize2 = [](state_t& state, size_t blocks) NOEXCEPT
const auto finalizer = [](state_t& state, size_t blocks) NOEXCEPT
{
buffer_t buffer{};
schedule_n(buffer, blocks);
Expand All @@ -160,15 +160,15 @@ finalize_double(state_t& state, size_t blocks) NOEXCEPT

if (std::is_constant_evaluated())
{
return finalize2(state, blocks);
return finalizer(state, blocks);
}
else if constexpr (native && SHA::strength == 256)
{
return native_finalize_double(state, blocks);
}
else
{
return finalize2(state, blocks);
return finalizer(state, blocks);
}
}

Expand Down

0 comments on commit f732eab

Please sign in to comment.