2019-10-15 18:31:36 +00:00
/ *
Copyright ( c ) 2019 DERO Foundation . All rights reserved .
Redistribution and use in source and binary forms , with or without modification ,
are permitted provided that the following conditions are met :
1. Redistributions of source code must retain the above copyright notice ,
this list of conditions and the following disclaimer .
2. Redistributions in binary form must reproduce the above copyright notice ,
this list of conditions and the following disclaimer in the documentation
and / or other materials provided with the distribution .
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission .
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* /
2019-10-15 17:45:39 +00:00
package randomx
2024-04-11 08:24:12 +00:00
import (
2024-05-01 20:43:52 +00:00
"encoding/hex"
2024-05-02 14:11:08 +00:00
"errors"
2024-05-01 20:43:52 +00:00
"git.gammaspectra.live/P2Pool/go-randomx/v3/internal/aes"
2024-05-02 14:11:08 +00:00
"git.gammaspectra.live/P2Pool/go-randomx/v3/internal/memory"
2024-04-20 19:17:33 +00:00
"os"
2024-04-11 08:24:12 +00:00
"runtime"
2024-04-20 19:17:33 +00:00
"slices"
2024-05-01 20:43:52 +00:00
"strings"
2024-05-02 14:28:38 +00:00
"unsafe"
2024-04-11 08:24:12 +00:00
)
2019-10-15 17:45:39 +00:00
import "testing"
2024-05-01 20:43:52 +00:00
type testdata struct {
name string
key [ ] byte
input [ ] byte
// expected result, in hex
expected string
2024-04-11 08:24:12 +00:00
}
2019-10-15 17:45:39 +00:00
2024-05-01 20:43:52 +00:00
func mustHex ( str string ) [ ] byte {
b , err := hex . DecodeString ( str )
if err != nil {
panic ( err )
}
return b
}
var Tests = [ ] testdata {
{ "example" , [ ] byte ( "RandomX example key\x00" ) , [ ] byte ( "RandomX example input\x00" ) , "8a48e5f9db45ab79d9080574c4d81954fe6ac63842214aff73c244b26330b7c9" } ,
{ "test_a" , [ ] byte ( "test key 000" ) , [ ] byte ( "This is a test" ) , "639183aae1bf4c9a35884cb46b09cad9175f04efd7684e7262a0ac1c2f0b4e3f" } ,
{ "test_b" , [ ] byte ( "test key 000" ) , [ ] byte ( "Lorem ipsum dolor sit amet" ) , "300a0adb47603dedb42228ccb2b211104f4da45af709cd7547cd049e9489c969" } ,
{ "test_c" , [ ] byte ( "test key 000" ) , [ ] byte ( "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" ) , "c36d4ed4191e617309867ed66a443be4075014e2b061bcdaf9ce7b721d2b77a8" } ,
{ "test_d" , [ ] byte ( "test key 001" ) , [ ] byte ( "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" ) , "e9ff4503201c0c2cca26d285c93ae883f9b1d30c9eb240b820756f2d5a7905fc" } ,
{ "test_e" , [ ] byte ( "test key 001" ) , mustHex ( "0b0b98bea7e805e0010a2126d287a2a0cc833d312cb786385a7c2f9de69d25537f584a9bc9977b00000000666fd8753bf61a8631f12984e3fd44f4014eca629276817b56f32e9b68bd82f416" ) , "c56414121acda1713c2f2a819d8ae38aed7c80c35c2a769298d34f03833cd5f1" } ,
}
func testFlags ( name string , flags Flags ) ( f Flags , skip bool ) {
flags |= GetFlags ( )
2024-05-02 14:11:08 +00:00
flags &^= RANDOMX_FLAG_LARGE_PAGES
2024-05-01 20:43:52 +00:00
nn := strings . Split ( name , "/" )
switch nn [ len ( nn ) - 1 ] {
case "interpreter" :
flags &^= RANDOMX_FLAG_JIT
case "compiler" :
flags |= RANDOMX_FLAG_JIT
if ! flags . HasJIT ( ) {
return flags , true
}
case "softaes" :
flags &^= RANDOMX_FLAG_HARD_AES
case "hardaes" :
flags |= RANDOMX_FLAG_HARD_AES
if aes . NewHardAES ( ) == nil {
return flags , true
}
2024-05-02 14:11:08 +00:00
case "largepages" :
flags |= RANDOMX_FLAG_LARGE_PAGES
if largePageAllocator == nil {
return flags , true
}
2024-05-02 14:28:38 +00:00
if unsafe . Sizeof ( uint ( 0 ) ) < 8 {
//not 64-bit platforms
return flags , true
}
2024-05-01 20:43:52 +00:00
}
2019-10-15 17:45:39 +00:00
2024-05-01 20:43:52 +00:00
return flags , false
}
2019-10-15 17:45:39 +00:00
2024-05-01 20:43:52 +00:00
func Test_RandomXLight ( t * testing . T ) {
2024-05-02 01:46:03 +00:00
t . Parallel ( )
2024-05-02 14:11:08 +00:00
for _ , n := range [ ] string { "interpreter" , "compiler" , "softaes" , "hardaes" , "largepages" } {
2024-05-01 20:43:52 +00:00
t . Run ( n , func ( t * testing . T ) {
2024-05-02 01:46:03 +00:00
t . Parallel ( )
2024-05-01 20:43:52 +00:00
tFlags , skip := testFlags ( t . Name ( ) , 0 )
if skip {
t . Skip ( "not supported on this platform" )
}
2019-10-15 17:45:39 +00:00
2024-05-02 14:11:08 +00:00
c , err := NewCache ( tFlags )
if err != nil {
if tFlags . Has ( RANDOMX_FLAG_LARGE_PAGES ) && errors . Is ( err , memory . PageNoMemoryErr ) {
t . Skip ( "cannot allocate memory" )
}
t . Fatal ( err )
2024-05-01 20:43:52 +00:00
}
2024-04-12 00:00:46 +00:00
defer func ( ) {
err := c . Close ( )
if err != nil {
t . Error ( err )
}
} ( )
2024-04-11 05:44:12 +00:00
2024-05-01 20:43:52 +00:00
for _ , test := range Tests {
t . Run ( test . name , func ( t * testing . T ) {
c . Init ( test . key )
vm , err := NewVM ( tFlags , c , nil )
if err != nil {
t . Fatal ( err )
}
defer func ( ) {
err := vm . Close ( )
if err != nil {
t . Error ( err )
}
} ( )
var outputHash [ RANDOMX_HASH_SIZE ] byte
vm . CalculateHash ( test . input , & outputHash )
outputHex := hex . EncodeToString ( outputHash [ : ] )
if outputHex != test . expected {
t . Errorf ( "key=%v, input=%v" , test . key , test . input )
t . Errorf ( "expected=%s, actual=%s" , test . expected , outputHex )
t . FailNow ( )
}
} )
2024-04-11 05:44:12 +00:00
}
2024-05-01 20:43:52 +00:00
2024-04-11 05:44:12 +00:00
} )
2019-10-15 17:45:39 +00:00
}
2024-04-20 19:17:33 +00:00
}
2024-05-02 09:41:00 +00:00
func Test_RandomXBatch ( t * testing . T ) {
t . Parallel ( )
for _ , n := range [ ] string { "softaes" , "hardaes" } {
t . Run ( n , func ( t * testing . T ) {
t . Parallel ( )
tFlags , skip := testFlags ( t . Name ( ) , 0 )
if skip {
t . Skip ( "not supported on this platform" )
}
2024-05-02 14:11:08 +00:00
c , err := NewCache ( tFlags )
if tFlags . Has ( RANDOMX_FLAG_LARGE_PAGES ) && errors . Is ( err , memory . PageNoMemoryErr ) {
t . Skip ( "cannot allocate memory" )
}
if err != nil {
t . Fatal ( err )
2024-05-02 09:41:00 +00:00
}
defer func ( ) {
err := c . Close ( )
if err != nil {
t . Error ( err )
}
} ( )
tests := Tests [ 1 : 4 ]
c . Init ( tests [ 0 ] . key )
vm , err := NewVM ( tFlags , c , nil )
if err != nil {
t . Fatal ( err )
}
defer func ( ) {
err := vm . Close ( )
if err != nil {
t . Error ( err )
}
} ( )
var outputHash [ 3 ] [ RANDOMX_HASH_SIZE ] byte
vm . CalculateHashFirst ( tests [ 0 ] . input )
vm . CalculateHashNext ( tests [ 1 ] . input , & outputHash [ 0 ] )
vm . CalculateHashNext ( tests [ 2 ] . input , & outputHash [ 1 ] )
vm . CalculateHashLast ( & outputHash [ 2 ] )
for i , test := range tests {
outputHex := hex . EncodeToString ( outputHash [ i ] [ : ] )
if outputHex != test . expected {
t . Errorf ( "key=%v, input=%v" , test . key , test . input )
t . Errorf ( "expected=%s, actual=%s" , test . expected , outputHex )
t . FailNow ( )
}
}
} )
}
}
2024-04-20 19:17:33 +00:00
func Test_RandomXFull ( t * testing . T ) {
2024-05-01 20:43:52 +00:00
if testing . Short ( ) {
t . Skip ( "Skipping full mode with -short" )
}
2024-04-20 19:17:33 +00:00
if os . Getenv ( "CI" ) != "" {
t . Skip ( "Skipping full mode in CI environment" )
}
2024-05-02 14:11:08 +00:00
for _ , n := range [ ] string { "interpreter" , "compiler" , "softaes" , "hardaes" , "largepages" } {
2024-05-01 20:43:52 +00:00
t . Run ( n , func ( t * testing . T ) {
2024-04-20 19:17:33 +00:00
2024-05-01 20:43:52 +00:00
tFlags , skip := testFlags ( t . Name ( ) , RANDOMX_FLAG_FULL_MEM )
if skip {
t . Skip ( "not supported on this platform" )
}
2024-04-20 19:17:33 +00:00
2024-05-02 14:11:08 +00:00
c , err := NewCache ( tFlags )
if tFlags . Has ( RANDOMX_FLAG_LARGE_PAGES ) && errors . Is ( err , memory . PageNoMemoryErr ) {
t . Skip ( "cannot allocate memory" )
}
if err != nil {
t . Fatal ( err )
2024-05-01 20:43:52 +00:00
}
2024-04-20 19:17:33 +00:00
defer func ( ) {
err := c . Close ( )
if err != nil {
t . Error ( err )
}
} ( )
2024-05-01 20:43:52 +00:00
dataset , err := NewDataset ( tFlags )
if err != nil {
t . Fatal ( err )
2024-04-20 19:17:33 +00:00
}
2024-05-01 20:43:52 +00:00
defer func ( ) {
err := dataset . Close ( )
if err != nil {
t . Error ( err )
}
} ( )
2019-10-15 17:45:39 +00:00
2024-05-01 20:43:52 +00:00
for _ , test := range Tests {
t . Run ( test . name , func ( t * testing . T ) {
c . Init ( test . key )
dataset . InitDatasetParallel ( c , runtime . NumCPU ( ) )
vm , err := NewVM ( tFlags , nil , dataset )
if err != nil {
t . Fatal ( err )
}
defer func ( ) {
err := vm . Close ( )
if err != nil {
t . Error ( err )
}
} ( )
var outputHash [ RANDOMX_HASH_SIZE ] byte
vm . CalculateHash ( test . input , & outputHash )
outputHex := hex . EncodeToString ( outputHash [ : ] )
if outputHex != test . expected {
t . Errorf ( "key=%v, input=%v" , test . key , test . input )
t . Errorf ( "expected=%s, actual=%s" , test . expected , outputHex )
t . FailNow ( )
}
} )
// cleanup between runs
runtime . GC ( )
2024-04-20 19:17:33 +00:00
}
2024-05-01 20:43:52 +00:00
2024-04-20 19:17:33 +00:00
} )
2024-05-01 20:43:52 +00:00
// cleanup 2 GiB between runs
2024-04-20 19:17:33 +00:00
runtime . GC ( )
}
2019-10-15 17:45:39 +00:00
}
2024-04-11 08:24:12 +00:00
2024-04-20 19:17:33 +00:00
var BenchmarkTest = Tests [ 0 ]
var BenchmarkCache * Cache
2024-05-01 20:43:52 +00:00
var BenchmarkDataset * Dataset
var BenchmarkFlags = GetFlags ( )
2024-04-20 19:17:33 +00:00
func TestMain ( m * testing . M ) {
if slices . Contains ( os . Args , "-test.bench" ) {
2024-05-01 20:43:52 +00:00
flags := GetFlags ( )
flags |= RANDOMX_FLAG_FULL_MEM
var err error
2024-04-20 19:17:33 +00:00
//init light and full dataset
2024-05-02 14:11:08 +00:00
BenchmarkCache , err = NewCache ( flags | RANDOMX_FLAG_LARGE_PAGES )
if err != nil {
BenchmarkCache , err = NewCache ( flags )
if err != nil {
panic ( err )
}
}
2024-04-20 19:17:33 +00:00
defer BenchmarkCache . Close ( )
2024-05-01 20:43:52 +00:00
BenchmarkCache . Init ( BenchmarkTest . key )
2024-05-02 14:11:08 +00:00
BenchmarkDataset , err = NewDataset ( flags | RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_LARGE_PAGES )
2024-05-01 20:43:52 +00:00
if err != nil {
2024-05-02 14:11:08 +00:00
BenchmarkDataset , err = NewDataset ( flags | RANDOMX_FLAG_FULL_MEM )
if err != nil {
panic ( err )
}
2024-05-01 20:43:52 +00:00
}
defer BenchmarkDataset . Close ( )
BenchmarkDataset . InitDatasetParallel ( BenchmarkCache , runtime . NumCPU ( ) )
2024-04-20 19:17:33 +00:00
}
os . Exit ( m . Run ( ) )
}
func Benchmark_RandomXLight ( b * testing . B ) {
2024-04-11 08:24:12 +00:00
b . ReportAllocs ( )
2024-05-01 20:43:52 +00:00
vm , err := NewVM ( BenchmarkFlags , BenchmarkCache , nil )
if err != nil {
b . Fatal ( err )
}
2024-04-20 19:17:33 +00:00
defer vm . Close ( )
2024-04-11 08:24:12 +00:00
2024-04-20 19:17:33 +00:00
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
var output_hash [ 32 ] byte
vm . CalculateHash ( BenchmarkTest . input , & output_hash )
runtime . KeepAlive ( output_hash )
}
}
2024-04-11 08:24:12 +00:00
2024-04-20 19:17:33 +00:00
func Benchmark_RandomXFull ( b * testing . B ) {
b . ReportAllocs ( )
2024-04-11 08:24:12 +00:00
2024-05-01 20:43:52 +00:00
vm , err := NewVM ( BenchmarkFlags | RANDOMX_FLAG_FULL_MEM , nil , BenchmarkDataset )
if err != nil {
b . Fatal ( err )
}
2024-04-18 10:09:05 +00:00
defer vm . Close ( )
2024-04-20 19:17:33 +00:00
2024-04-11 08:24:12 +00:00
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
var output_hash [ 32 ] byte
2024-04-20 19:17:33 +00:00
vm . CalculateHash ( BenchmarkTest . input , & output_hash )
2024-04-11 08:24:12 +00:00
runtime . KeepAlive ( output_hash )
}
}
2024-04-12 00:08:40 +00:00
2024-04-20 19:17:33 +00:00
func Benchmark_RandomXLight_Parallel ( b * testing . B ) {
2024-04-12 00:08:40 +00:00
b . ReportAllocs ( )
2024-04-20 19:17:33 +00:00
b . ResetTimer ( )
2024-04-12 00:08:40 +00:00
2024-04-20 19:17:33 +00:00
b . RunParallel ( func ( pb * testing . PB ) {
var output_hash [ 32 ] byte
2024-04-12 00:08:40 +00:00
2024-05-01 20:43:52 +00:00
vm , err := NewVM ( BenchmarkFlags , BenchmarkCache , nil )
if err != nil {
b . Fatal ( err )
}
2024-04-20 19:17:33 +00:00
defer vm . Close ( )
2024-04-12 00:08:40 +00:00
2024-04-20 19:17:33 +00:00
for pb . Next ( ) {
vm . CalculateHash ( BenchmarkTest . input , & output_hash )
runtime . KeepAlive ( output_hash )
2024-04-12 00:08:40 +00:00
}
2024-04-20 19:17:33 +00:00
} )
}
2024-04-12 00:08:40 +00:00
2024-04-20 19:17:33 +00:00
func Benchmark_RandomXFull_Parallel ( b * testing . B ) {
b . ReportAllocs ( )
2024-04-12 00:08:40 +00:00
b . ResetTimer ( )
2024-04-20 19:17:33 +00:00
2024-04-12 00:08:40 +00:00
b . RunParallel ( func ( pb * testing . PB ) {
var output_hash [ 32 ] byte
2024-04-20 19:17:33 +00:00
2024-05-01 20:43:52 +00:00
vm , err := NewVM ( BenchmarkFlags | RANDOMX_FLAG_FULL_MEM , nil , BenchmarkDataset )
if err != nil {
b . Fatal ( err )
}
2024-04-18 10:09:05 +00:00
defer vm . Close ( )
2024-04-12 00:08:40 +00:00
for pb . Next ( ) {
2024-04-20 19:17:33 +00:00
vm . CalculateHash ( BenchmarkTest . input , & output_hash )
2024-04-12 00:08:40 +00:00
runtime . KeepAlive ( output_hash )
}
} )
}