Skip to content

Commit f732eab

Browse files
authored
Merge pull request #1567 from evoskuil/master
Use more efficient native single block hash, comments.
2 parents 6ebf3b5 + 7214571 commit f732eab

5 files changed

Lines changed: 103 additions & 65 deletions

File tree

include/bitcoin/system/hash/sha/algorithm.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ class algorithm
400400
static digest_t native_finalize_second(const state_t& half) NOEXCEPT;
401401
static digest_t native_finalize_double(state_t& half, size_t blocks) NOEXCEPT;
402402

403-
////static digest_t native_hash(const block_t& block) NOEXCEPT;
403+
static digest_t native_hash(const block_t& block) NOEXCEPT;
404404
static digest_t native_hash(const half_t& half) NOEXCEPT;
405405
static digest_t native_hash(const half_t& left, const half_t& right) NOEXCEPT;
406406

include/bitcoin/system/impl/hash/sha/algorithm_iterate.ipp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ template <size_t Size>
247247
INLINE void CLASS::
248248
iterate_native(state_t& state, const ablocks_t<Size>& blocks) NOEXCEPT
249249
{
250+
// Native hash() does not have an optimal array override because there is
251+
// no advantage to array sizing without the benefit of prescheduling.
250252
iblocks_t iblocks{ array_cast<byte_t>(blocks) };
251253
native_transform(state, iblocks);
252254
}

include/bitcoin/system/impl/hash/sha/algorithm_native.ipp

Lines changed: 73 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,33 @@ native_transform(state_t& state, const auto& block) NOEXCEPT
227227
// There is no benefit to caching pading because it is not prescheduled.
228228
// ----------------------------------------------------------------------------
229229

230+
// TODO: These transitions require state to be unloaded/loaded and
231+
// shuffled/unshuffled, whereas this is not logically necessary. This is a
232+
// fixed cost imposed once for any accumulation (which is inconsequential for
233+
// larger iterations), but reduces efficiency for lower block counts and hash
234+
// doubling. Large iterations are 15-16% wheras small iterations are 20-26%.
235+
// native_transform -> native_transform -> native_finalize
236+
// native_transform -> native_finalize
237+
// This can be resolved in the non-iterator scenarios (below) through
238+
// implementation of a finalizing and a doubling native_transform. This means
239+
// that padding must be incorporated, however since it is not prescheduled or
240+
// cached this is not an issue.
241+
242+
TEMPLATE
243+
template <size_t Blocks>
244+
typename CLASS::digest_t CLASS::
245+
native_finalize(state_t& state) NOEXCEPT
246+
{
247+
return native_finalize(state, Blocks);
248+
}
249+
250+
TEMPLATE
251+
typename CLASS::digest_t CLASS::
252+
native_finalize(state_t& state, size_t blocks) NOEXCEPT
253+
{
254+
return native_finalize(state, pad_blocks(blocks));
255+
}
256+
230257
TEMPLATE
231258
typename CLASS::digest_t CLASS::
232259
native_finalize(state_t& state, const words_t& pad) NOEXCEPT
@@ -248,21 +275,6 @@ native_finalize(state_t& state, const words_t& pad) NOEXCEPT
248275
return digest;
249276
}
250277

251-
TEMPLATE
252-
template <size_t Blocks>
253-
typename CLASS::digest_t CLASS::
254-
native_finalize(state_t& state) NOEXCEPT
255-
{
256-
return native_finalize(state, Blocks);
257-
}
258-
259-
TEMPLATE
260-
typename CLASS::digest_t CLASS::
261-
native_finalize(state_t& state, size_t blocks) NOEXCEPT
262-
{
263-
return native_finalize(state, pad_blocks(blocks));
264-
}
265-
266278
TEMPLATE
267279
typename CLASS::digest_t CLASS::
268280
native_finalize_second(const state_t& state) NOEXCEPT
@@ -273,9 +285,9 @@ native_finalize_second(const state_t& state) NOEXCEPT
273285
// Hash a state value and finalize it.
274286
auto state2 = H::get;
275287
words_t block{};
276-
inject_left(block, state); // swapped
277-
pad_half(block); // swapped
278-
return native_finalize(state2, block); // no block swap (swaps state)
288+
inject_left(block, state);
289+
pad_half(block);
290+
return native_finalize(state2, block);
279291
}
280292

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

289301
// This is native_finalize_second() but reuses the initial block.
290302
auto state2 = H::get;
291-
inject_left(block, state); // swapped
292-
pad_half(block); // swapped
293-
return native_finalize(state2, block); // no block swap (swaps state)
303+
inject_left(block, state);
304+
pad_half(block);
305+
return native_finalize(state2, block);
294306
}
295307

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

311+
TEMPLATE
312+
typename CLASS::digest_t CLASS::
313+
native_hash(const block_t& block) NOEXCEPT
314+
{
315+
auto state = H::get;
316+
native_transform<true>(state, block);
317+
return native_finalize(state, pad_block());
318+
}
319+
299320
TEMPLATE
300321
typename CLASS::digest_t CLASS::
301322
native_hash(const half_t& half) NOEXCEPT
302323
{
303324
// No hash(state_t) optimizations for sha160 (requires chunk_t/half_t).
304325
static_assert(is_same_type<state_t, chunk_t>);
305326

327+
// input_left is a non-native endianness conversion.
306328
auto state = H::get;
307329
words_t block{};
308-
input_left(block, half); // swaps
309-
pad_half(block); // swapped
310-
return native_finalize(state, block); // no block swap (swaps state)
330+
input_left(block, half);
331+
pad_half(block);
332+
return native_finalize(state, block);
311333
}
312334

313335
TEMPLATE
@@ -316,10 +338,10 @@ native_hash(const half_t& left, const half_t& right) NOEXCEPT
316338
{
317339
auto state = H::get;
318340
words_t block{};
319-
inject_left(block, array_cast<word_t>(left)); // unswapped
320-
inject_right(block, array_cast<word_t>(right)); // unswapped
321-
native_transform<true>(state, block); // swap
322-
return native_finalize<one>(state); // no block swap (swaps state)
341+
inject_left(block, array_cast<word_t>(left));
342+
inject_right(block, array_cast<word_t>(right));
343+
native_transform<true>(state, block);
344+
return native_finalize<one>(state);
323345
}
324346

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

336358
// Second hash
337359
words_t block2{};
338-
inject_left(block2, state); // swapped
339-
pad_half(block2); // swapped
340-
state = H::get; // [reuse state var]
341-
return native_finalize(state, block2); // no block swap (swaps state)
360+
inject_left(block2, state);
361+
pad_half(block2);
362+
state = H::get;
363+
return native_finalize(state, block2);
342364
}
343365

344366
TEMPLATE
345367
typename CLASS::digest_t CLASS::
346368
native_double_hash(const half_t& half) NOEXCEPT
347369
{
370+
// input_left is a non-native endianness conversion.
348371
auto state = H::get;
349372
words_t block{};
350-
input_left(block, half); // swaps
351-
pad_half(block); // swapped
352-
native_transform<false>(state, block); // no block swap
373+
input_left(block, half);
374+
pad_half(block);
375+
native_transform<false>(state, block);
353376

354377
// Second hash
355-
inject_left(block, state); // swapped
356-
pad_half(block); // swapped
357-
state = H::get; // [reuse state var]
358-
return native_finalize(state, block); // no block swap (swaps state)
378+
inject_left(block, state);
379+
pad_half(block);
380+
state = H::get;
381+
return native_finalize(state, block);
359382
}
360383

361384
TEMPLATE
@@ -364,16 +387,16 @@ native_double_hash(const half_t& left, const half_t& right) NOEXCEPT
364387
{
365388
auto state = H::get;
366389
words_t block{};
367-
inject_left(block, array_cast<word_t>(left)); // unswapped
368-
inject_right(block, array_cast<word_t>(right)); // unswapped
369-
native_transform<true>(state, block); // swap
370-
native_transform<false>(state, pad_block()); // swapped
390+
inject_left(block, array_cast<word_t>(left));
391+
inject_right(block, array_cast<word_t>(right));
392+
native_transform<true>(state, block);
393+
native_transform<false>(state, pad_block());
371394

372395
// Second hash
373-
inject_left(block, state); // swapped
374-
pad_half(block); // swapped
375-
state = H::get; // [reuse state var]
376-
return native_finalize(state, block); // no block swap (swaps state)
396+
inject_left(block, state);
397+
pad_half(block);
398+
state = H::get;
399+
return native_finalize(state, block);
377400
}
378401

379402
} // namespace sha

include/bitcoin/system/impl/hash/sha/algorithm_single.ipp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,21 @@ TEMPLATE
5454
constexpr typename CLASS::digest_t CLASS::
5555
hash(const block_t& block) NOEXCEPT
5656
{
57-
// As an array of a 1 arrays is the same as the array, this compiles away.
58-
return hash(ablocks_t<one>{ block });
57+
if (std::is_constant_evaluated())
58+
{
59+
// As an array of 1 arrays is same as the array, this compiles away.
60+
return hash(ablocks_t<one>{ block });
61+
}
62+
else if constexpr (native && SHA::strength == 256)
63+
{
64+
// Native hash() does not have an optimal array override.
65+
return native_hash(block);
66+
}
67+
else
68+
{
69+
// As an array of 1 arrays is same as the array, this compiles away.
70+
return hash(ablocks_t<one>{ block });
71+
}
5972
}
6073

6174
TEMPLATE

include/bitcoin/system/impl/hash/sha/algorithm_stream.ipp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ template <size_t Blocks>
5656
constexpr typename CLASS::digest_t CLASS::
5757
finalize(state_t& state) NOEXCEPT
5858
{
59-
const auto finalize1 = [](state_t& state) NOEXCEPT
59+
const auto finalizer = [](state_t& state) NOEXCEPT
6060
{
6161
buffer_t buffer{};
6262
schedule_n<Blocks>(buffer);
@@ -66,23 +66,23 @@ finalize(state_t& state) NOEXCEPT
6666

6767
if (std::is_constant_evaluated())
6868
{
69-
return finalize1(state);
69+
return finalizer(state);
7070
}
7171
else if constexpr (native && SHA::strength == 256)
7272
{
7373
return native_finalize<Blocks>(state);
7474
}
7575
else
7676
{
77-
return finalize1(state);
77+
return finalizer(state);
7878
}
7979
}
8080

8181
TEMPLATE
8282
constexpr typename CLASS::digest_t CLASS::
8383
finalize(state_t& state, size_t blocks) NOEXCEPT
8484
{
85-
const auto finalize1 = [](state_t& state, size_t blocks) NOEXCEPT
85+
const auto finalizer = [](state_t& state, size_t blocks) NOEXCEPT
8686
{
8787
buffer_t buffer{};
8888
schedule_n(buffer, blocks);
@@ -92,15 +92,15 @@ finalize(state_t& state, size_t blocks) NOEXCEPT
9292

9393
if (std::is_constant_evaluated())
9494
{
95-
return finalize1(state, blocks);
95+
return finalizer(state, blocks);
9696
}
9797
else if constexpr (native && SHA::strength == 256)
9898
{
9999
return native_finalize(state, blocks);
100100
}
101101
else
102102
{
103-
return finalize1(state, blocks);
103+
return finalizer(state, blocks);
104104
}
105105
}
106106

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

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

126126
if (std::is_constant_evaluated())
127127
{
128-
return finalize2(state);
128+
return finalizer(state);
129129
}
130130
else if constexpr (native && SHA::strength == 256)
131131
{
132132
return native_finalize_second(state);
133133
}
134134
else
135135
{
136-
return finalize2(state);
136+
return finalizer(state);
137137
}
138138
}
139139

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

161161
if (std::is_constant_evaluated())
162162
{
163-
return finalize2(state, blocks);
163+
return finalizer(state, blocks);
164164
}
165165
else if constexpr (native && SHA::strength == 256)
166166
{
167167
return native_finalize_double(state, blocks);
168168
}
169169
else
170170
{
171-
return finalize2(state, blocks);
171+
return finalizer(state, blocks);
172172
}
173173
}
174174

0 commit comments

Comments
 (0)