🔥 Speed and memory optimizations (OpenMP !):
- OpenMP multiprocessing for dataset generation - remove some temporaries - add march_native flag
This commit is contained in:
parent
78ff9d9305
commit
61108cbc13
|
@ -10,6 +10,11 @@ Implementation is based on the [spec revision 23 (2017-08-03)](https://github.co
|
|||
An unoptimized mining CPU backend is available through the compile-time flag ``-d:ethash_mining``.
|
||||
It requires compilation through the C++ backend.
|
||||
|
||||
## Optimizations
|
||||
For maximum speed, compile Ethash with `-d:release -d:march_native -d:openmp`.
|
||||
This will compile Ethash in Nim release mode, with all supported CPU extensions (AVX2 especially) and with OpenMP multiprocessing
|
||||
On MacOS, OpenMP requires installing GCC-7. It can be done through homebrew.
|
||||
|
||||
## Original implementation
|
||||
Original Ethereum implementation is available [here](https://github.com/ethereum/ethash).
|
||||
|
||||
|
|
|
@ -22,7 +22,9 @@ proc test(name: string, lang: string = "c") =
|
|||
task test, "Run Proof-of-Work tests (without mining)":
|
||||
test "all_tests"
|
||||
|
||||
task test_mining, "Run Proof-of-Work and mining tests (test in release mode)":
|
||||
task test_mining, "Run Proof-of-Work and mining tests (test in release mode + OpenMP + march=native)":
|
||||
switch("define", "release")
|
||||
switch("define", "openmp")
|
||||
switch("define", "march_native")
|
||||
switch("define", "ethash_mining")
|
||||
test "all_tests"
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
# Nim eth compilation flag config
|
||||
|
||||
#### From default nim.cfg, somehow it's not taken into account with a custom nim.cfg
|
||||
@if release or quick:
|
||||
obj_checks:off
|
||||
field_checks:off
|
||||
range_checks:off
|
||||
bound_checks:off
|
||||
overflow_checks:off
|
||||
assertions:off
|
||||
stacktrace:off
|
||||
linetrace:off
|
||||
debugger:off
|
||||
line_dir:off
|
||||
dead_code_elim:on
|
||||
@end
|
||||
|
||||
@if release:
|
||||
opt:speed
|
||||
@end
|
||||
|
||||
# Configuration for GCC compiler:
|
||||
gcc.options.speed = "-O3 -fno-strict-aliasing"
|
||||
gcc.options.size = "-Os"
|
||||
@if windows:
|
||||
gcc.options.debug = "-g3 -O0 -gdwarf-3"
|
||||
@else:
|
||||
gcc.options.debug = "-g3 -O0"
|
||||
@end
|
||||
gcc.cpp.options.speed = "-O3 -fno-strict-aliasing"
|
||||
gcc.cpp.options.size = "-Os"
|
||||
gcc.cpp.options.debug = "-g3 -O0"
|
||||
|
||||
# Configuration for the LLVM Clang compiler:
|
||||
clang.options.debug = "-g"
|
||||
clang.options.always = "-w"
|
||||
clang.options.speed = "-O3"
|
||||
clang.options.size = "-Os"
|
||||
#######
|
||||
|
||||
|
||||
@if openmp:
|
||||
# stackTrace:off # Stack traces are already removed selectively in the code
|
||||
# Otherwise heap alloc from string will crash the program
|
||||
@if macosx: # Default compiler on Mac is clang without OpenMP and gcc is an alias to clang.
|
||||
# Use Homebrew GCC instead for OpenMP support. GCC (v7), must be properly linked via `brew link gcc`
|
||||
cc:"gcc"
|
||||
gcc.exe:"/usr/local/bin/gcc-7"
|
||||
gcc.linkerexe:"/usr/local/bin/gcc-7"
|
||||
@end
|
||||
@end
|
|
@ -1,6 +1,13 @@
|
|||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0).
|
||||
|
||||
when defined(openmp):
|
||||
{.passC: "-fopenmp".}
|
||||
{.passL: "-fopenmp".}
|
||||
|
||||
when defined(march_native):
|
||||
{.passC: "-march=native".}
|
||||
|
||||
import ./proof_of_work
|
||||
export proof_of_work
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ proc mine*(full_size: Natural, dataset: seq[Hash[512]], header: Hash[256], diffi
|
|||
|
||||
randomize() # Start with a completely random seed
|
||||
result = uint64 random(high(int)) # TODO: Nim random does not work on uint64 range.
|
||||
# Also random is deprecate and do not include the end of the range.
|
||||
# Also random is deprecated in devel and does not include the end of the range.
|
||||
|
||||
while not result.isValid(difficulty, full_size, dataset, header):
|
||||
inc(result) # we rely on uin overflow (mod 2^64) here.
|
||||
inc(result) # we rely on uint overflow (mod 2^64) here.
|
||||
|
|
|
@ -126,18 +126,24 @@ proc calc_dataset_item*(cache: seq[Hash[512]], i: Natural): Hash[512] {.noSideEf
|
|||
|
||||
result = keccak512 mix[]
|
||||
|
||||
proc calc_dataset*(full_size: Natural, cache: seq[Hash[512]]): seq[Hash[512]] {.noSideEffect.} =
|
||||
when defined(openmp):
|
||||
# Remove stacktraces when using OpenMP, heap alloc from strings will crash.
|
||||
{.push stacktrace: off.}
|
||||
proc calc_dataset*(full_size: Natural, cache: seq[Hash[512]]): seq[Hash[512]] =
|
||||
|
||||
result = newSeq[Hash[512]](full_size div HASH_BYTES)
|
||||
for i in `||`(0, result.len - 1, "simd"):
|
||||
# OpenMP loop
|
||||
result[i] = calc_dataset_item(cache, i)
|
||||
|
||||
for i, hash in result.mpairs:
|
||||
hash = calc_dataset_item(cache, i)
|
||||
when defined(openmp):
|
||||
# Remove stacktraces when using OpenMP, heap alloc from strings will crash.
|
||||
{.pop.}
|
||||
|
||||
# ###############################################################################
|
||||
# Main loop
|
||||
|
||||
type HashimotoHash = tuple[mix_digest: Hash[256], value: Hash[256]]
|
||||
type DatasetLookup = proc(i: Natural): Hash[512] {.noSideEffect.}
|
||||
type HashimotoHash = tuple[mix_digest, value: Hash[256]]
|
||||
|
||||
template hashimoto(header: Hash[256],
|
||||
nonce: uint64,
|
||||
|
|
|
@ -19,16 +19,16 @@ suite "Test mining":
|
|||
full_size = get_datasize(blck)
|
||||
|
||||
echo "\nGenerating dataset"
|
||||
var start = cpuTime()
|
||||
var start = epochTime()
|
||||
let dag = calc_dataset(full_size, cache)
|
||||
echo " Done, time taken: ", $(cpuTime() - start), " seconds"
|
||||
echo " Done, time taken: ", $(epochTime() - start), " seconds"
|
||||
|
||||
echo "\nStarting mining"
|
||||
start = cpuTime()
|
||||
start = epochTime()
|
||||
let mined_nonce = mine(full_size, dag, header, difficulty)
|
||||
echo " Done, time taken: ", $(cpuTime() - start), " seconds"
|
||||
echo " Done, time taken: ", $(epochTime() - start), " seconds"
|
||||
|
||||
echo "\nUnfortunately we can't really test Ethereu mining as multiple nonces are valid"
|
||||
echo "\nUnfortunately we can't really test Ethereum mining as multiple nonces are valid"
|
||||
echo "for a set of parameters, so we only test that there is no exception or out of memory"
|
||||
echo "\nThe nonce mined was:"
|
||||
echo mined_nonce
|
||||
|
|
Loading…
Reference in New Issue