From 90826b1ac2ae7b69f0f1816e0354198ea9836796 Mon Sep 17 00:00:00 2001 From: mancoast Date: Thu, 18 Aug 2016 17:24:06 -0400 Subject: [PATCH 1/7] Critical Section __m512i union This improves performance using 512bit SIMD instructions. --- src/libethash/internal.c | 44 ++++++++++++++++++++++++++++++++++++++++ src/libethash/internal.h | 4 ++++ 2 files changed, 48 insertions(+) diff --git a/src/libethash/internal.c b/src/libethash/internal.c index 0a830fc8..8a34d3c9 100644 --- a/src/libethash/internal.c +++ b/src/libethash/internal.c @@ -108,6 +108,9 @@ void ethash_calculate_dag_item( __m128i xmm1 = ret->xmm[1]; __m128i xmm2 = ret->xmm[2]; __m128i xmm3 = ret->xmm[3]; +#elif defined(__MIC__) + __m512i const fnv_prime = _mm512_set1_epi32(FNV_PRIME); + __m512i zmm0 = ret->zmm[0]; #endif for (uint32_t i = 0; i != ETHASH_DATASET_PARENTS; ++i) { @@ -131,6 +134,14 @@ void ethash_calculate_dag_item( ret->xmm[2] = xmm2; ret->xmm[3] = xmm3; } + #elif defined(__MIC__) + { + zmm0 = _mm512_mullo_epi32(zmm0, fnv_prime); + + // have to write to ret as values are used to compute index + zmm0 = _mm512_xor_si512(zmm0, parent->zmm[0]); + ret->zmm[0] = zmm0; + } #else { for (unsigned w = 0; w != NODE_WORDS; ++w) { @@ -227,6 +238,14 @@ static bool ethash_hash( mix[n].xmm[2] = _mm_xor_si128(xmm2, dag_node->xmm[2]); mix[n].xmm[3] = _mm_xor_si128(xmm3, dag_node->xmm[3]); } + #elif defined(__MIC__) + { + // __m512i implementation via union + // Each vector register (zmm) can store sixteen 32-bit integer numbers + __m512i fnv_prime = _mm512_set1_epi32(FNV_PRIME); + __m512i zmm0 = _mm512_mullo_epi32(fnv_prime, mix[n].zmm[0]); + mix[n].zmm[0] = _mm512_xor_si512(zmm0, dag_node->zmm[0]); + } #else { for (unsigned w = 0; w != NODE_WORDS; ++w) { @@ -300,7 +319,11 @@ ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t cons if (!ret) { return NULL; } +#if defined(__MIC__) + ret->cache = _mm_malloc((size_t)cache_size, 64); +#else ret->cache = malloc((size_t)cache_size); +#endif if (!ret->cache) { goto fail_free_light; } @@ -312,7 +335,11 @@ ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t cons return ret; fail_free_cache_mem: +#if defined(__MIC__) + _mm_free(ret->cache); +#else free(ret->cache); +#endif fail_free_light: free(ret); return NULL; @@ -408,6 +435,17 @@ ethash_full_t ethash_full_new_internal( ETHASH_CRITICAL("mmap failure()"); goto fail_close_file; } +#if defined(__MIC__) + node* tmp_nodes = _mm_malloc((size_t)full_size, 64); + //copy all nodes from ret->data + //mmapped_nodes are not aligned properly + uint32_t const countnodes = (uint32_t) ((size_t)ret->file_size / sizeof(node)); + //fprintf(stderr,"ethash_full_new_internal:countnodes:%d",countnodes); + for (uint32_t i = 1; i != countnodes; ++i) { + tmp_nodes[i] = ret->data[i]; + } + ret->data = tmp_nodes; +#endif return ret; case ETHASH_IO_MEMO_SIZE_MISMATCH: // if a DAG of same filename but unexpected size is found, silently force new file creation @@ -424,6 +462,9 @@ ethash_full_t ethash_full_new_internal( break; } +#if defined(__MIC__) + ret->data = _mm_malloc((size_t)full_size, 64); +#endif if (!ethash_compute_full_data(ret->data, full_size, light, callback)) { ETHASH_CRITICAL("Failure at computing DAG data."); goto fail_free_full_data; @@ -448,6 +489,9 @@ ethash_full_t ethash_full_new_internal( fail_free_full_data: // could check that munmap(..) == 0 but even if it did not can't really do anything here munmap(ret->data, (size_t)full_size); +#if defined(__MIC__) + _mm_free(ret->data); +#endif fail_close_file: fclose(ret->file); fail_free_full: diff --git a/src/libethash/internal.h b/src/libethash/internal.h index 26c395ad..35419c6a 100644 --- a/src/libethash/internal.h +++ b/src/libethash/internal.h @@ -8,6 +8,8 @@ #if defined(_M_X64) && ENABLE_SSE #include +#elif defined(__MIC__) +#include #endif #ifdef __cplusplus @@ -27,6 +29,8 @@ typedef union node { #if defined(_M_X64) && ENABLE_SSE __m128i xmm[NODE_WORDS/4]; +#elif defined(__MIC__) + __m512i zmm[NODE_WORDS/16]; #endif } node; From 0e2f2ec9d460ae868dd1c04cdc60f9031fc01615 Mon Sep 17 00:00:00 2001 From: Neil Moore Date: Thu, 8 Jun 2017 21:34:33 -0400 Subject: [PATCH 2/7] Resolve GCC 7+ warnings Fixes #4115. --- src/libethash/internal.c | 52 +++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/src/libethash/internal.c b/src/libethash/internal.c index 8a34d3c9..0d8f12fc 100644 --- a/src/libethash/internal.c +++ b/src/libethash/internal.c @@ -218,10 +218,10 @@ static bool ethash_hash( for (unsigned n = 0; n != MIX_NODES; ++n) { node const* dag_node; + node tmp_node; if (full_nodes) { dag_node = &full_nodes[MIX_NODES * index + n]; } else { - node tmp_node; ethash_calculate_dag_item(&tmp_node, index * MIX_NODES + n, light); dag_node = &tmp_node; } @@ -426,42 +426,44 @@ ethash_full_t ethash_full_new_internal( return NULL; } ret->file_size = (size_t)full_size; - switch (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, false)) { - case ETHASH_IO_FAIL: - // ethash_io_prepare will do all ETHASH_CRITICAL() logging in fail case + + enum ethash_io_rc err = ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, false); + if (err == ETHASH_IO_FAIL) goto fail_free_full; - case ETHASH_IO_MEMO_MATCH: - if (!ethash_mmap(ret, f)) { - ETHASH_CRITICAL("mmap failure()"); - goto fail_close_file; - } -#if defined(__MIC__) - node* tmp_nodes = _mm_malloc((size_t)full_size, 64); - //copy all nodes from ret->data - //mmapped_nodes are not aligned properly - uint32_t const countnodes = (uint32_t) ((size_t)ret->file_size / sizeof(node)); - //fprintf(stderr,"ethash_full_new_internal:countnodes:%d",countnodes); - for (uint32_t i = 1; i != countnodes; ++i) { - tmp_nodes[i] = ret->data[i]; - } - ret->data = tmp_nodes; -#endif - return ret; - case ETHASH_IO_MEMO_SIZE_MISMATCH: + + if (err == ETHASH_IO_MEMO_SIZE_MISMATCH) { // if a DAG of same filename but unexpected size is found, silently force new file creation if (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, true) != ETHASH_IO_MEMO_MISMATCH) { ETHASH_CRITICAL("Could not recreate DAG file after finding existing DAG with unexpected size."); goto fail_free_full; } - // fallthrough to the mismatch case here, DO NOT go through match - case ETHASH_IO_MEMO_MISMATCH: + // we now need to go through the mismatch case, NOT the match case + err = ETHASH_IO_MEMO_MISMATCH; + } + + if (err == ETHASH_IO_MEMO_MISMATCH || err == ETHASH_IO_MEMO_MATCH) { if (!ethash_mmap(ret, f)) { ETHASH_CRITICAL("mmap failure()"); goto fail_close_file; } - break; + + if (err == ETHASH_IO_MEMO_MATCH) { +#if defined(__MIC__) + node* tmp_nodes = _mm_malloc((size_t)full_size, 64); + //copy all nodes from ret->data + //mmapped_nodes are not aligned properly + uint32_t const countnodes = (uint32_t) ((size_t)ret->file_size / sizeof(node)); + //fprintf(stderr,"ethash_full_new_internal:countnodes:%d",countnodes); + for (uint32_t i = 1; i != countnodes; ++i) { + tmp_nodes[i] = ret->data[i]; + } + ret->data = tmp_nodes; +#endif + return ret; + } } + #if defined(__MIC__) ret->data = _mm_malloc((size_t)full_size, 64); #endif From 2e05a27aa276c9d09d456ca521e89e32cad31790 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Wed, 29 Mar 2017 14:24:38 +0200 Subject: [PATCH 3/7] Use 64-bit seek functions instead of fseek when generating DAG file --- src/libethash/io.c | 2 +- src/libethash/io.h | 10 ++++++++++ src/libethash/io_posix.c | 5 +++++ src/libethash/io_win32.c | 6 ++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/libethash/io.c b/src/libethash/io.c index f4db477c..ffb1a0e7 100644 --- a/src/libethash/io.c +++ b/src/libethash/io.c @@ -91,7 +91,7 @@ enum ethash_io_rc ethash_io_prepare( goto free_memo; } // make sure it's of the proper size - if (fseek(f, (long int)(file_size + ETHASH_DAG_MAGIC_NUM_SIZE - 1), SEEK_SET) != 0) { + if (ethash_fseek(f, file_size + ETHASH_DAG_MAGIC_NUM_SIZE - 1, SEEK_SET) != 0) { fclose(f); ETHASH_CRITICAL("Could not seek to the end of DAG file: \"%s\". Insufficient space?", tmpfile); goto free_memo; diff --git a/src/libethash/io.h b/src/libethash/io.h index 7a27089c..06bd8c3b 100644 --- a/src/libethash/io.h +++ b/src/libethash/io.h @@ -113,6 +113,16 @@ enum ethash_io_rc ethash_io_prepare( */ FILE* ethash_fopen(char const* file_name, char const* mode); +/** + * An fseek wrapper for crossplatform 64-bit seek. + * + * @param f The file stream whose fd to get + * @param offset Number of bytes from @a origin + * @param origin Initial position + * @return Current offset or -1 to indicate an error + */ +int ethash_fseek(FILE* f, size_t offset, int origin); + /** * An strncat wrapper for no-warnings crossplatform strncat. * diff --git a/src/libethash/io_posix.c b/src/libethash/io_posix.c index c9a17d84..be8e6542 100644 --- a/src/libethash/io_posix.c +++ b/src/libethash/io_posix.c @@ -34,6 +34,11 @@ FILE* ethash_fopen(char const* file_name, char const* mode) return fopen(file_name, mode); } +int ethash_fseek(FILE* f, size_t offset, int origin) +{ + return fseeko(f, offset, origin); +} + char* ethash_strncat(char* dest, size_t dest_size, char const* src, size_t count) { return strlen(dest) + count + 1 <= dest_size ? strncat(dest, src, count) : NULL; diff --git a/src/libethash/io_win32.c b/src/libethash/io_win32.c index 34f1aaa7..2fb0e6c9 100644 --- a/src/libethash/io_win32.c +++ b/src/libethash/io_win32.c @@ -26,6 +26,7 @@ #include #include #include +#include FILE* ethash_fopen(char const* file_name, char const* mode) { @@ -33,6 +34,11 @@ FILE* ethash_fopen(char const* file_name, char const* mode) return fopen_s(&f, file_name, mode) == 0 ? f : NULL; } +int ethash_fseek(FILE* f, size_t offset, int origin) +{ + return _fseeki64(f, offset, origin); +} + char* ethash_strncat(char* dest, size_t dest_size, char const* src, size_t count) { return strncat_s(dest, dest_size, src, count) == 0 ? dest : NULL; From 7310406afae193fea51a7f5b546809b99a19e4b2 Mon Sep 17 00:00:00 2001 From: Bob Summerwill Date: Sun, 12 Jun 2016 21:20:06 -0700 Subject: [PATCH 4/7] Added warning suppression using pragmas for Debian. It looks like older versions of GCC have slightly unreliable logic for array out-of-bounds detection. Code in ethash which uses unions and arrays is firing a warning in both Debian Jesse (8.5) and in the ARM Linux cross-builds. Debian Jesse uses GCC 4.9.2. The cross-builds are using GCC 4.8.4. Other distros are using GCC 5.x or even GCC 6.x (Arch). The issue is https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56273 and was fixed in GCC 5.0 and backported to 4.9.3. Updated comments. --- src/libethash/internal.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/libethash/internal.c b/src/libethash/internal.c index 0d8f12fc..618238bd 100644 --- a/src/libethash/internal.c +++ b/src/libethash/internal.c @@ -257,6 +257,22 @@ static bool ethash_hash( } +// Workaround for a GCC regression which causes a bogus -Warray-bounds warning. +// The regression was introduced in GCC 4.8.4, fixed in GCC 5.0.0 and backported to GCC 4.9.3 but +// never to the GCC 4.8.x line. +// +// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56273 +// +// This regression is affecting Debian Jesse (8.5) builds of cpp-ethereum (GCC 4.9.2) and also +// manifests in the doublethinkco armel v5 cross-builds, which use crosstool-ng and resulting +// in the use of GCC 4.8.4. The Tizen runtime wants an even older GLIBC version - the one from +// GCC 4.6.0! + +#if defined(__GNUC__) && (__GNUC__ < 5) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif // define (__GNUC__) + // compress mix for (uint32_t w = 0; w != MIX_WORDS; w += 4) { uint32_t reduction = mix->words[w + 0]; @@ -266,6 +282,10 @@ static bool ethash_hash( mix->words[w / 4] = reduction; } +#if defined(__GNUC__) && (__GNUC__ < 5) +#pragma GCC diagnostic pop +#endif // define (__GNUC__) + fix_endian_arr32(mix->words, MIX_WORDS / 4); memcpy(&ret->mix_hash, mix->bytes, 32); // final Keccak hash From 31fbca2ef83b7219eba7cf0026ba9f292a54f360 Mon Sep 17 00:00:00 2001 From: Bob Summerwill Date: Mon, 9 May 2016 11:41:39 -0700 Subject: [PATCH 5/7] Fix GCC warning - 'static' is not at beginning of declaration. With this change we will be able to remove a global warning suppression from our CMake files. --- src/libethash/internal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libethash/internal.c b/src/libethash/internal.c index 618238bd..0e69f009 100644 --- a/src/libethash/internal.c +++ b/src/libethash/internal.c @@ -56,7 +56,7 @@ uint64_t ethash_get_cachesize(uint64_t const block_number) // Follows Sergio's "STRICT MEMORY HARD HASHING FUNCTIONS" (2014) // https://bitslog.files.wordpress.com/2013/12/memohash-v0-3.pdf // SeqMemoHash(s, R, N) -bool static ethash_compute_cache_nodes( +static bool ethash_compute_cache_nodes( node* const nodes, uint64_t cache_size, ethash_h256_t const* seed From 03d18d67ae6b259fb9b40871779575345c7bd0a4 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Thu, 1 Dec 2016 19:05:28 +0100 Subject: [PATCH 6/7] libethash: avoid passing nullptr to strlen --- src/libethash/io_posix.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libethash/io_posix.c b/src/libethash/io_posix.c index be8e6542..b831acf1 100644 --- a/src/libethash/io_posix.c +++ b/src/libethash/io_posix.c @@ -101,6 +101,8 @@ bool ethash_get_default_dirname(char* strbuf, size_t buffsize) struct passwd* pwd = getpwuid(getuid()); if (pwd) home_dir = pwd->pw_dir; + if (!home_dir) + return false; } size_t len = strlen(home_dir); From 04efce18196c952d50c1db110a81227503b88aea Mon Sep 17 00:00:00 2001 From: hackyminer Date: Sun, 25 Nov 2018 07:56:58 +0900 Subject: [PATCH 7/7] add a missing blockNum parameter --- ethash.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethash.go b/ethash.go index 21c10c87..837a16c1 100644 --- a/ethash.go +++ b/ethash.go @@ -130,7 +130,7 @@ func (l *Light) Verify(block Block) bool { // to prevent DOS attacks. blockNum := block.NumberU64() if blockNum >= epochLength*2048 { - log.Debug(fmt.Sprintf("block number %d too high, limit is %d", epochLength*2048)) + log.Debug(fmt.Sprintf("block number %d too high, limit is %d", blockNum, epochLength*2048)) return false }