Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use more efficient native single block hash, comments. #1567

Merged
merged 1 commit into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading