Compare commits
2 commits
d20dd880ce
...
9f77218ff8
Author | SHA1 | Date | |
---|---|---|---|
DataHoarder | 9f77218ff8 | ||
DataHoarder | 4903cd7407 |
39
README.md
39
README.md
|
@ -1,29 +1,38 @@
|
||||||
# RandomX (Golang Implementation)
|
# RandomX (Golang Implementation)
|
||||||
|
RandomX is a proof-of-work (PoW) algorithm that is optimized for general-purpose CPUs.
|
||||||
|
RandomX uses random code execution (hence the name) together with several memory-hard techniques to minimize the efficiency advantage of specialized hardware.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
Fork from [git.dero.io/DERO_Foundation/RandomX](https://git.dero.io/DERO_Foundation/RandomX). Also related, their [Analysis of RandomX writeup](https://medium.com/deroproject/analysis-of-randomx-dde9dfe9bbc6).
|
Fork from [git.dero.io/DERO_Foundation/RandomX](https://git.dero.io/DERO_Foundation/RandomX). Also related, their [Analysis of RandomX writeup](https://medium.com/deroproject/analysis-of-randomx-dde9dfe9bbc6).
|
||||||
|
|
||||||
Original code failed RandomX testcases and was implemented using big.Float.
|
Original code failed RandomX testcases and was implemented using big.Float.
|
||||||
|
|
||||||
This package implements RandomX without CGO, using only Golang code, pure float64 ops and two small assembly sections to implement CFROUND modes, with optional soft float implementation.
|
---
|
||||||
|
|
||||||
|
This package implements RandomX without CGO, using only Golang code, native float64 ops, some assembly, but with optional soft float _purego_ implementation.
|
||||||
|
|
||||||
All test cases pass properly.
|
All test cases pass properly.
|
||||||
|
|
||||||
JIT is supported on a few platforms but can be hard-disabled via the `disable_jit` build flag, or at runtime.
|
Supports Full mode and Light mode.
|
||||||
|
|
||||||
|
For the C++ implementation and design of RandomX, see [github.com/tevador/RandomX](https://github.com/tevador/RandomX)
|
||||||
|
|
||||||
|
| Feature | 386 | amd64 | arm | arm64 | mips | mips64 | riscv64 | wasm |
|
||||||
|
|:----------------------------:|:---:|:-----:|:---:|:-----:|:----:|:------:|:-------:|:----:|
|
||||||
|
| purego | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| Hardware Float Operations | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||||
|
| Hardware AES Operations | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||||
|
| Native Superscalar Execution | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| Superscalar JIT Execution | ❌ | ✅* | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||||
|
| Native VM Execution | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||||
|
| VM JIT Execution | ❌ | ✅* | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||||
|
|
||||||
|
|
||||||
A pure Golang implementation can be used on platforms without hard float support or via the `purego` build flag manually.
|
A pure Golang implementation can be used on platforms without hard float support or via the `purego` build flag manually.
|
||||||
|
|
||||||
| Platform | Hard Float | Hard AES | JIT | Native | purego | Notes |
|
Any platform with no hard float support or when enabled manually will use soft float, using [softfloat64](https://git.gammaspectra.live/P2Pool/softfloat64). This will be very slow.
|
||||||
|:-----------:|:----------:|:--------:|:---:|:------:|:------:|:----------------:|
|
|
||||||
| **386** | ✅ | ❌ | ❌ | ✅ | ✅ | |
|
|
||||||
| **amd64** | ✅ | ✅ | ✅* | ✅ | ✅ | JIT only on Unix |
|
|
||||||
| **arm** | ❌ | ❌ | ❌ | ❌ | ✅ | |
|
|
||||||
| **arm64** | ✅ | ❌ | ❌ | ✅ | ✅ | |
|
|
||||||
| **mips** | ❌ | ❌ | ❌ | ❌ | ✅ | |
|
|
||||||
| **mips64** | ❌ | ❌ | ❌ | ❌ | ✅ | |
|
|
||||||
| **riscv64** | ❌ | ❌ | ❌ | ❌ | ✅ | |
|
|
||||||
| **wasm** | ❌ | ❌ | ❌ | ❌ | ✅ | |
|
|
||||||
|
|
||||||
|
Native hard float can be added with supporting rounding mode under _asm_.
|
||||||
|
|
||||||
Any platform with no hard float support (soft float using [softfloat64](git.gammaspectra.live/P2Pool/softfloat64)) will be vastly slow.
|
JIT only supported under Unix systems (Linux, *BSD, macOS), and can be hard-disabled via the `disable_jit` build flag, or at runtime.
|
||||||
|
|
||||||
Native hard float can be added with supporting rounding mode under _asm_.
|
|
|
@ -30,7 +30,7 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
package aes
|
package aes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.gammaspectra.live/P2Pool/go-randomx/v2/keys"
|
"git.gammaspectra.live/P2Pool/go-randomx/v3/keys"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,29 +3,29 @@
|
||||||
package aes
|
package aes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "git.gammaspectra.live/P2Pool/go-randomx/v2/asm"
|
_ "git.gammaspectra.live/P2Pool/go-randomx/v3/asm"
|
||||||
"golang.org/x/sys/cpu"
|
"golang.org/x/sys/cpu"
|
||||||
_ "unsafe"
|
_ "unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
//go:linkname hard_aesdec git.gammaspectra.live/P2Pool/go-randomx/v2/asm.aesdec
|
//go:linkname hard_aesdec git.gammaspectra.live/P2Pool/go-randomx/v3/asm.aesdec
|
||||||
func hard_aesdec(state *[4]uint32, key *[4]uint32)
|
func hard_aesdec(state *[4]uint32, key *[4]uint32)
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
//go:linkname hard_aesenc git.gammaspectra.live/P2Pool/go-randomx/v2/asm.aesenc
|
//go:linkname hard_aesenc git.gammaspectra.live/P2Pool/go-randomx/v3/asm.aesenc
|
||||||
func hard_aesenc(state *[4]uint32, key *[4]uint32)
|
func hard_aesenc(state *[4]uint32, key *[4]uint32)
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
//go:linkname hard_aesroundtrip_decenc git.gammaspectra.live/P2Pool/go-randomx/v2/asm.aesroundtrip_decenc
|
//go:linkname hard_aesroundtrip_decenc git.gammaspectra.live/P2Pool/go-randomx/v3/asm.aesroundtrip_decenc
|
||||||
func hard_aesroundtrip_decenc(states *[4][4]uint32, keys *[4][4]uint32)
|
func hard_aesroundtrip_decenc(states *[4][4]uint32, keys *[4][4]uint32)
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
//go:linkname hard_aesroundtrip_encdec git.gammaspectra.live/P2Pool/go-randomx/v2/asm.aesroundtrip_encdec
|
//go:linkname hard_aesroundtrip_encdec git.gammaspectra.live/P2Pool/go-randomx/v3/asm.aesroundtrip_encdec
|
||||||
func hard_aesroundtrip_encdec(states *[4][4]uint32, keys *[4][4]uint32)
|
func hard_aesroundtrip_encdec(states *[4][4]uint32, keys *[4][4]uint32)
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
//go:linkname hard_aesroundtrip_encdec1 git.gammaspectra.live/P2Pool/go-randomx/v2/asm.aesroundtrip_encdec1
|
//go:linkname hard_aesroundtrip_encdec1 git.gammaspectra.live/P2Pool/go-randomx/v3/asm.aesroundtrip_encdec1
|
||||||
func hard_aesroundtrip_encdec1(states *[4][4]uint32, key *[4]uint32)
|
func hard_aesroundtrip_encdec1(states *[4][4]uint32, key *[4]uint32)
|
||||||
|
|
||||||
var supportsAES = cpu.X86.HasAES
|
var supportsAES = cpu.X86.HasAES
|
||||||
|
|
55
cache.go
55
cache.go
|
@ -1,8 +1,8 @@
|
||||||
package randomx
|
package randomx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.gammaspectra.live/P2Pool/go-randomx/v2/argon2"
|
"git.gammaspectra.live/P2Pool/go-randomx/v3/argon2"
|
||||||
"git.gammaspectra.live/P2Pool/go-randomx/v2/keys"
|
"git.gammaspectra.live/P2Pool/go-randomx/v3/keys"
|
||||||
"runtime"
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
@ -15,7 +15,7 @@ func (m *MemoryBlock) GetLine(addr uint64) *RegisterLine {
|
||||||
return (*RegisterLine)(unsafe.Pointer(unsafe.SliceData(m[addr : addr+8 : addr+8])))
|
return (*RegisterLine)(unsafe.Pointer(unsafe.SliceData(m[addr : addr+8 : addr+8])))
|
||||||
}
|
}
|
||||||
|
|
||||||
type Randomx_Cache struct {
|
type Cache struct {
|
||||||
Blocks []MemoryBlock
|
Blocks []MemoryBlock
|
||||||
|
|
||||||
Programs [RANDOMX_PROGRAM_COUNT]SuperScalarProgram
|
Programs [RANDOMX_PROGRAM_COUNT]SuperScalarProgram
|
||||||
|
@ -25,36 +25,20 @@ type Randomx_Cache struct {
|
||||||
Flags uint64
|
Flags uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func Randomx_alloc_cache(flags uint64) *Randomx_Cache {
|
func NewCache(flags uint64) *Cache {
|
||||||
if flags == RANDOMX_FLAG_DEFAULT {
|
if flags == RANDOMX_FLAG_DEFAULT {
|
||||||
flags = RANDOMX_FLAG_JIT
|
flags = RANDOMX_FLAG_JIT
|
||||||
}
|
}
|
||||||
return &Randomx_Cache{
|
return &Cache{
|
||||||
Flags: flags,
|
Flags: flags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cache *Randomx_Cache) HasJIT() bool {
|
func (cache *Cache) HasJIT() bool {
|
||||||
return cache.Flags&RANDOMX_FLAG_JIT > 0 && cache.JitPrograms[0] != nil
|
return cache.Flags&RANDOMX_FLAG_JIT > 0 && cache.JitPrograms[0] != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cache *Randomx_Cache) VM_Initialize() *VM {
|
func (cache *Cache) Close() error {
|
||||||
|
|
||||||
vm := &VM{
|
|
||||||
Dataset: &Randomx_DatasetLight{
|
|
||||||
Cache: cache,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if cache.HasJIT() {
|
|
||||||
vm.JITProgram = mapProgram(nil, int(RandomXCodeSize))
|
|
||||||
if cache.Flags&RANDOMX_FLAG_SECURE == 0 {
|
|
||||||
mapProgramRWX(vm.JITProgram)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return vm
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cache *Randomx_Cache) Close() error {
|
|
||||||
for _, p := range cache.JitPrograms {
|
for _, p := range cache.JitPrograms {
|
||||||
if p != nil {
|
if p != nil {
|
||||||
err := p.Close()
|
err := p.Close()
|
||||||
|
@ -66,10 +50,12 @@ func (cache *Randomx_Cache) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cache *Randomx_Cache) Init(key []byte) {
|
func (cache *Cache) Init(key []byte) {
|
||||||
// Lock due to external JIT madness
|
if cache.Flags&RANDOMX_FLAG_JIT > 0 {
|
||||||
runtime.LockOSThread()
|
// Lock due to external JIT madness
|
||||||
defer runtime.UnlockOSThread()
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
}
|
||||||
|
|
||||||
kkey := slices.Clone(key)
|
kkey := slices.Clone(key)
|
||||||
|
|
||||||
|
@ -93,7 +79,7 @@ func (cache *Randomx_Cache) Init(key []byte) {
|
||||||
const Mask = CacheSize/CacheLineSize - 1
|
const Mask = CacheSize/CacheLineSize - 1
|
||||||
|
|
||||||
// GetMixBlock fetch a 64 byte block in uint64 form
|
// GetMixBlock fetch a 64 byte block in uint64 form
|
||||||
func (cache *Randomx_Cache) GetMixBlock(addr uint64) *RegisterLine {
|
func (cache *Cache) GetMixBlock(addr uint64) *RegisterLine {
|
||||||
|
|
||||||
addr = (addr & Mask) * CacheLineSize
|
addr = (addr & Mask) * CacheLineSize
|
||||||
|
|
||||||
|
@ -101,7 +87,7 @@ func (cache *Randomx_Cache) GetMixBlock(addr uint64) *RegisterLine {
|
||||||
return cache.Blocks[block].GetLine(addr % 1024)
|
return cache.Blocks[block].GetLine(addr % 1024)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cache *Randomx_Cache) InitDatasetItem(rl *RegisterLine, itemNumber uint64) {
|
func (cache *Cache) InitDatasetItem(rl *RegisterLine, itemNumber uint64) {
|
||||||
registerValue := itemNumber
|
registerValue := itemNumber
|
||||||
|
|
||||||
rl[0] = (itemNumber + 1) * keys.SuperScalar_Constants[0]
|
rl[0] = (itemNumber + 1) * keys.SuperScalar_Constants[0]
|
||||||
|
@ -129,7 +115,7 @@ func (cache *Randomx_Cache) InitDatasetItem(rl *RegisterLine, itemNumber uint64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cache *Randomx_Cache) InitDatasetItemJIT(rl *RegisterLine, itemNumber uint64) {
|
func (cache *Cache) InitDatasetItemJIT(rl *RegisterLine, itemNumber uint64) {
|
||||||
registerValue := itemNumber
|
registerValue := itemNumber
|
||||||
|
|
||||||
rl[0] = (itemNumber + 1) * keys.SuperScalar_Constants[0]
|
rl[0] = (itemNumber + 1) * keys.SuperScalar_Constants[0]
|
||||||
|
@ -155,9 +141,12 @@ func (cache *Randomx_Cache) InitDatasetItemJIT(rl *RegisterLine, itemNumber uint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cache *Randomx_Cache) initDataset(dataset []RegisterLine, startItem, endItem uint64) {
|
func (cache *Cache) InitDataset(dataset []RegisterLine, startItem, endItem uint64) {
|
||||||
panic("todo")
|
|
||||||
for itemNumber := startItem; itemNumber < endItem; itemNumber, dataset = itemNumber+1, dataset[1:] {
|
for itemNumber := startItem; itemNumber < endItem; itemNumber, dataset = itemNumber+1, dataset[1:] {
|
||||||
cache.InitDatasetItem(&dataset[0], itemNumber)
|
if cache.HasJIT() {
|
||||||
|
cache.InitDatasetItemJIT(&dataset[0], itemNumber)
|
||||||
|
} else {
|
||||||
|
cache.InitDatasetItem(&dataset[0], itemNumber)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
package randomx
|
package randomx
|
||||||
|
|
||||||
import "git.gammaspectra.live/P2Pool/go-randomx/v2/argon2"
|
import "git.gammaspectra.live/P2Pool/go-randomx/v3/argon2"
|
||||||
|
|
||||||
// see reference configuration.h
|
// see reference configuration.h
|
||||||
// Cache size in KiB. Must be a power of 2.
|
// Cache size in KiB. Must be a power of 2.
|
||||||
|
@ -81,7 +81,7 @@ const RANDOMX_JUMP_BITS = 8
|
||||||
// Jump condition mask offset in bits. The sum of RANDOMX_JUMP_BITS and RANDOMX_JUMP_OFFSET must not exceed 16.
|
// Jump condition mask offset in bits. The sum of RANDOMX_JUMP_BITS and RANDOMX_JUMP_OFFSET must not exceed 16.
|
||||||
const RANDOMX_JUMP_OFFSET = 8
|
const RANDOMX_JUMP_OFFSET = 8
|
||||||
|
|
||||||
const DATASETEXTRAITEMS = RANDOMX_DATASET_EXTRA_SIZE / RANDOMX_DATASET_ITEM_SIZE
|
const DatasetExtraItems = RANDOMX_DATASET_EXTRA_SIZE / RANDOMX_DATASET_ITEM_SIZE
|
||||||
|
|
||||||
const SuperscalarMaxSize = 3*RANDOMX_SUPERSCALAR_LATENCY + 2
|
const SuperscalarMaxSize = 3*RANDOMX_SUPERSCALAR_LATENCY + 2
|
||||||
const RANDOMX_DATASET_ITEM_SIZE uint64 = 64
|
const RANDOMX_DATASET_ITEM_SIZE uint64 = 64
|
||||||
|
|
28
dataset.go
28
dataset.go
|
@ -1,8 +1,30 @@
|
||||||
package randomx
|
package randomx
|
||||||
|
|
||||||
type Randomx_Dataset interface {
|
import "sync"
|
||||||
InitDataset(startItem, endItem uint64)
|
|
||||||
ReadDataset(address uint64, r, cache *RegisterLine)
|
type Dataset interface {
|
||||||
|
InitDataset(startItem, itemCount uint64)
|
||||||
|
ReadDataset(address uint64, r *RegisterLine)
|
||||||
PrefetchDataset(address uint64)
|
PrefetchDataset(address uint64)
|
||||||
Flags() uint64
|
Flags() uint64
|
||||||
|
Cache() *Cache
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitDatasetParallel(dataset Dataset, n int) {
|
||||||
|
n = max(1, n)
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := uint64(1); i < uint64(n); i++ {
|
||||||
|
a := (DatasetItemCount * i) / uint64(n)
|
||||||
|
b := (DatasetItemCount * (i + 1)) / uint64(n)
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func(a, b uint64) {
|
||||||
|
defer wg.Done()
|
||||||
|
dataset.InitDataset(a, b-a)
|
||||||
|
}(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
dataset.InitDataset(0, DatasetItemCount/uint64(n))
|
||||||
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
46
dataset_full.go
Normal file
46
dataset_full.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package randomx
|
||||||
|
|
||||||
|
const DatasetSize = RANDOMX_DATASET_BASE_SIZE + RANDOMX_DATASET_EXTRA_SIZE
|
||||||
|
|
||||||
|
const DatasetItemCount = DatasetSize / CacheLineSize
|
||||||
|
|
||||||
|
type DatasetFull struct {
|
||||||
|
cache *Cache
|
||||||
|
Memory [DatasetItemCount]RegisterLine
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFullDataset(cache *Cache) *DatasetFull {
|
||||||
|
return &DatasetFull{
|
||||||
|
cache: cache,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DatasetFull) PrefetchDataset(address uint64) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DatasetFull) ReadDataset(address uint64, r *RegisterLine) {
|
||||||
|
cache := &d.Memory[address/CacheLineSize]
|
||||||
|
|
||||||
|
for i := range r {
|
||||||
|
r[i] ^= cache[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DatasetFull) Cache() *Cache {
|
||||||
|
return d.cache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DatasetFull) Flags() uint64 {
|
||||||
|
return d.cache.Flags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DatasetFull) InitDataset(startItem, itemCount uint64) {
|
||||||
|
if startItem >= DatasetItemCount || itemCount > DatasetItemCount {
|
||||||
|
panic("out of range")
|
||||||
|
}
|
||||||
|
if startItem+itemCount > DatasetItemCount {
|
||||||
|
panic("out of range")
|
||||||
|
}
|
||||||
|
d.cache.InitDataset(d.Memory[startItem:startItem+itemCount], startItem, startItem+itemCount)
|
||||||
|
}
|
|
@ -1,19 +1,25 @@
|
||||||
package randomx
|
package randomx
|
||||||
|
|
||||||
type Randomx_DatasetLight struct {
|
type DatasetLight struct {
|
||||||
Cache *Randomx_Cache
|
cache *Cache
|
||||||
Memory []uint64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Randomx_DatasetLight) PrefetchDataset(address uint64) {
|
func NewLightDataset(cache *Cache) *DatasetLight {
|
||||||
|
return &DatasetLight{
|
||||||
|
cache: cache,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DatasetLight) PrefetchDataset(address uint64) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Randomx_DatasetLight) ReadDataset(address uint64, r, cache *RegisterLine) {
|
func (d *DatasetLight) ReadDataset(address uint64, r *RegisterLine) {
|
||||||
if d.Cache.HasJIT() {
|
var cache RegisterLine
|
||||||
d.Cache.InitDatasetItemJIT(cache, address/CacheLineSize)
|
if d.cache.HasJIT() {
|
||||||
|
d.cache.InitDatasetItemJIT(&cache, address/CacheLineSize)
|
||||||
} else {
|
} else {
|
||||||
d.Cache.InitDatasetItem(cache, address/CacheLineSize)
|
d.cache.InitDatasetItem(&cache, address/CacheLineSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range r {
|
for i := range r {
|
||||||
|
@ -21,10 +27,14 @@ func (d *Randomx_DatasetLight) ReadDataset(address uint64, r, cache *RegisterLin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Randomx_DatasetLight) Flags() uint64 {
|
func (d *DatasetLight) Flags() uint64 {
|
||||||
return d.Cache.Flags
|
return d.cache.Flags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Randomx_DatasetLight) InitDataset(startItem, endItem uint64) {
|
func (d *DatasetLight) Cache() *Cache {
|
||||||
//d.Cache.initDataset(d.Cache.Programs)
|
return d.cache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DatasetLight) InitDataset(startItem, itemCount uint64) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -1,4 +1,4 @@
|
||||||
module git.gammaspectra.live/P2Pool/go-randomx/v2
|
module git.gammaspectra.live/P2Pool/go-randomx/v3
|
||||||
|
|
||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ package randomx
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"git.gammaspectra.live/P2Pool/go-randomx/v2/asm"
|
"git.gammaspectra.live/P2Pool/go-randomx/v3/asm"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
133
randomx_test.go
133
randomx_test.go
|
@ -31,7 +31,9 @@ package randomx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"slices"
|
||||||
)
|
)
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
|
@ -47,9 +49,9 @@ var Tests = []struct {
|
||||||
{[]byte("test key 001"), []byte("sed do eiusmod tempor incididunt ut labore et dolore magna aliqua"), "e9ff4503201c0c2cca26d285c93ae883f9b1d30c9eb240b820756f2d5a7905fc"}, // test d
|
{[]byte("test key 001"), []byte("sed do eiusmod tempor incididunt ut labore et dolore magna aliqua"), "e9ff4503201c0c2cca26d285c93ae883f9b1d30c9eb240b820756f2d5a7905fc"}, // test d
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Randomx(t *testing.T) {
|
func Test_RandomXLight(t *testing.T) {
|
||||||
|
|
||||||
c := Randomx_alloc_cache(0)
|
c := NewCache(0)
|
||||||
|
|
||||||
for ix, tt := range Tests {
|
for ix, tt := range Tests {
|
||||||
|
|
||||||
|
@ -62,7 +64,10 @@ func Test_Randomx(t *testing.T) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
vm := c.VM_Initialize()
|
dataset := NewLightDataset(c)
|
||||||
|
dataset.InitDataset(0, DatasetItemCount)
|
||||||
|
|
||||||
|
vm := NewVM(dataset)
|
||||||
defer vm.Close()
|
defer vm.Close()
|
||||||
|
|
||||||
var output_hash [32]byte
|
var output_hash [32]byte
|
||||||
|
@ -74,57 +79,119 @@ func Test_Randomx(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Benchmark_RandomX(b *testing.B) {
|
func Test_RandomXFull(t *testing.T) {
|
||||||
|
|
||||||
|
c := NewCache(0)
|
||||||
|
|
||||||
|
for ix, tt := range Tests {
|
||||||
|
|
||||||
|
t.Run(string(tt.key)+"_____"+string(tt.input), func(t *testing.T) {
|
||||||
|
c.Init(tt.key)
|
||||||
|
defer func() {
|
||||||
|
err := c.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
dataset := NewFullDataset(c)
|
||||||
|
InitDatasetParallel(dataset, runtime.NumCPU())
|
||||||
|
|
||||||
|
vm := NewVM(dataset)
|
||||||
|
defer vm.Close()
|
||||||
|
|
||||||
|
var output_hash [32]byte
|
||||||
|
vm.CalculateHash(tt.input, &output_hash)
|
||||||
|
|
||||||
|
actual := fmt.Sprintf("%x", output_hash)
|
||||||
|
if actual != tt.expected {
|
||||||
|
t.Errorf("#%d Fib(%v): expected %s, actual %s", ix, tt.key, tt.expected, actual)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// cleanup 2GiB between runs
|
||||||
|
runtime.GC()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var BenchmarkTest = Tests[0]
|
||||||
|
var BenchmarkCache *Cache
|
||||||
|
var BenchmarkDatasetLight *DatasetLight
|
||||||
|
var BenchmarkDatasetFull *DatasetFull
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
if slices.Contains(os.Args, "-test.bench") {
|
||||||
|
//init light and full dataset
|
||||||
|
BenchmarkCache = NewCache(0)
|
||||||
|
BenchmarkCache.Init(BenchmarkTest.key)
|
||||||
|
BenchmarkDatasetLight = NewLightDataset(BenchmarkCache)
|
||||||
|
BenchmarkDatasetLight.InitDataset(0, DatasetItemCount)
|
||||||
|
BenchmarkDatasetFull = NewFullDataset(BenchmarkCache)
|
||||||
|
InitDatasetParallel(BenchmarkDatasetFull, runtime.NumCPU())
|
||||||
|
defer BenchmarkCache.Close()
|
||||||
|
}
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_RandomXLight(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
|
||||||
tt := Tests[0]
|
vm := NewVM(BenchmarkDatasetLight)
|
||||||
|
|
||||||
c := Randomx_alloc_cache(0)
|
|
||||||
|
|
||||||
c.Init(tt.key)
|
|
||||||
defer func() {
|
|
||||||
err := c.Close()
|
|
||||||
if err != nil {
|
|
||||||
b.Error(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
vm := c.VM_Initialize()
|
|
||||||
defer vm.Close()
|
defer vm.Close()
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
var output_hash [32]byte
|
var output_hash [32]byte
|
||||||
vm.CalculateHash(tt.input, &output_hash)
|
vm.CalculateHash(BenchmarkTest.input, &output_hash)
|
||||||
runtime.KeepAlive(output_hash)
|
runtime.KeepAlive(output_hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Benchmark_RandomXParallel(b *testing.B) {
|
func Benchmark_RandomXFull(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
|
||||||
tt := Tests[0]
|
vm := NewVM(BenchmarkDatasetFull)
|
||||||
|
defer vm.Close()
|
||||||
c := Randomx_alloc_cache(0)
|
|
||||||
|
|
||||||
c.Init(tt.key)
|
|
||||||
defer func() {
|
|
||||||
err := c.Close()
|
|
||||||
if err != nil {
|
|
||||||
b.Error(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
var output_hash [32]byte
|
||||||
|
vm.CalculateHash(BenchmarkTest.input, &output_hash)
|
||||||
|
runtime.KeepAlive(output_hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_RandomXLight_Parallel(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
var output_hash [32]byte
|
var output_hash [32]byte
|
||||||
vm := c.VM_Initialize()
|
|
||||||
|
vm := NewVM(BenchmarkDatasetLight)
|
||||||
defer vm.Close()
|
defer vm.Close()
|
||||||
|
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
vm.CalculateHash(tt.input, &output_hash)
|
vm.CalculateHash(BenchmarkTest.input, &output_hash)
|
||||||
|
runtime.KeepAlive(output_hash)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_RandomXFull_Parallel(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
var output_hash [32]byte
|
||||||
|
|
||||||
|
vm := NewVM(BenchmarkDatasetFull)
|
||||||
|
defer vm.Close()
|
||||||
|
|
||||||
|
for pb.Next() {
|
||||||
|
vm.CalculateHash(BenchmarkTest.input, &output_hash)
|
||||||
runtime.KeepAlive(output_hash)
|
runtime.KeepAlive(output_hash)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -307,11 +307,11 @@ var slot10 = []*Instruction{&IMUL_RCP}
|
||||||
// SuperScalarInstruction superscalar program is built with superscalar instructions
|
// SuperScalarInstruction superscalar program is built with superscalar instructions
|
||||||
type SuperScalarInstruction struct {
|
type SuperScalarInstruction struct {
|
||||||
Opcode byte
|
Opcode byte
|
||||||
Dst_Reg int
|
Dst int
|
||||||
Src_Reg int
|
Src int
|
||||||
Mod byte
|
Mod byte
|
||||||
Imm32 uint32
|
Imm32 uint32
|
||||||
Type int
|
Imm64 uint64
|
||||||
OpGroup int
|
OpGroup int
|
||||||
OpGroupPar int
|
OpGroupPar int
|
||||||
GroupParIsSource int
|
GroupParIsSource int
|
||||||
|
@ -320,17 +320,15 @@ type SuperScalarInstruction struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sins *SuperScalarInstruction) FixSrcReg() {
|
func (sins *SuperScalarInstruction) FixSrcReg() {
|
||||||
if sins.Src_Reg >= 0 {
|
if sins.Src == 0xff {
|
||||||
// do nothing
|
sins.Src = sins.Dst
|
||||||
} else {
|
|
||||||
sins.Src_Reg = sins.Dst_Reg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
func (sins *SuperScalarInstruction) Reset() {
|
func (sins *SuperScalarInstruction) Reset() {
|
||||||
sins.Opcode = 99
|
sins.Opcode = 99
|
||||||
sins.Src_Reg = -1
|
sins.Src = 0xff
|
||||||
sins.Dst_Reg = -1
|
sins.Dst = 0xff
|
||||||
sins.CanReuse = false
|
sins.CanReuse = false
|
||||||
sins.GroupParIsSource = 0
|
sins.GroupParIsSource = 0
|
||||||
}
|
}
|
||||||
|
@ -406,6 +404,8 @@ func create(sins *SuperScalarInstruction, ins *Instruction, gen *Blake2Generator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sins.Imm64 = randomx_reciprocal(sins.Imm32)
|
||||||
|
|
||||||
sins.OpGroup = S_IMUL_RCP
|
sins.OpGroup = S_IMUL_RCP
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -450,11 +450,11 @@ func CreateSuperScalarInstruction(sins *SuperScalarInstruction, gen *Blake2Gener
|
||||||
type SuperScalarProgram []SuperScalarInstruction
|
type SuperScalarProgram []SuperScalarInstruction
|
||||||
|
|
||||||
func (p SuperScalarProgram) setAddressRegister(addressRegister int) {
|
func (p SuperScalarProgram) setAddressRegister(addressRegister int) {
|
||||||
p[0].Dst_Reg = addressRegister
|
p[0].Dst = addressRegister
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p SuperScalarProgram) AddressRegister() int {
|
func (p SuperScalarProgram) AddressRegister() int {
|
||||||
return p[0].Dst_Reg
|
return p[0].Dst
|
||||||
}
|
}
|
||||||
func (p SuperScalarProgram) Program() []SuperScalarInstruction {
|
func (p SuperScalarProgram) Program() []SuperScalarInstruction {
|
||||||
return p[1:]
|
return p[1:]
|
||||||
|
@ -569,9 +569,9 @@ func Build_SuperScalar_Program(gen *Blake2Generator) SuperScalarProgram {
|
||||||
depcycle = scheduleCycle + mop.GetLatency() // calculate when will the result be ready
|
depcycle = scheduleCycle + mop.GetLatency() // calculate when will the result be ready
|
||||||
|
|
||||||
if macro_op_index == sins.ins.ResultOP { // fix me
|
if macro_op_index == sins.ins.ResultOP { // fix me
|
||||||
registers[sins.Dst_Reg].Latency = depcycle
|
registers[sins.Dst].Latency = depcycle
|
||||||
registers[sins.Dst_Reg].LastOpGroup = sins.OpGroup
|
registers[sins.Dst].LastOpGroup = sins.OpGroup
|
||||||
registers[sins.Dst_Reg].LastOpPar = sins.OpGroupPar
|
registers[sins.Dst].LastOpPar = sins.OpGroupPar
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -609,12 +609,12 @@ func Build_SuperScalar_Program(gen *Blake2Generator) SuperScalarProgram {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
lastdst := asic_latencies[program[i].Dst_Reg] + 1
|
lastdst := asic_latencies[program[i].Dst] + 1
|
||||||
lastsrc := 0
|
lastsrc := 0
|
||||||
if program[i].Dst_Reg != program[i].Src_Reg {
|
if program[i].Dst != program[i].Src {
|
||||||
lastsrc = asic_latencies[program[i].Src_Reg] + 1
|
lastsrc = asic_latencies[program[i].Src] + 1
|
||||||
}
|
}
|
||||||
asic_latencies[program[i].Dst_Reg] = max(lastdst, lastsrc)
|
asic_latencies[program[i].Dst] = max(lastdst, lastsrc)
|
||||||
}
|
}
|
||||||
|
|
||||||
asic_latency_max := 0
|
asic_latency_max := 0
|
||||||
|
@ -719,18 +719,18 @@ func (sins *SuperScalarInstruction) SelectSource(preAllocatedAvailableRegisters
|
||||||
|
|
||||||
if len(available_registers) == 2 && sins.Opcode == S_IADD_RS {
|
if len(available_registers) == 2 && sins.Opcode == S_IADD_RS {
|
||||||
if available_registers[0] == RegisterNeedsDisplacement || available_registers[1] == RegisterNeedsDisplacement {
|
if available_registers[0] == RegisterNeedsDisplacement || available_registers[1] == RegisterNeedsDisplacement {
|
||||||
sins.Src_Reg = RegisterNeedsDisplacement
|
sins.Src = RegisterNeedsDisplacement
|
||||||
sins.OpGroupPar = sins.Src_Reg
|
sins.OpGroupPar = sins.Src
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if selectRegister(available_registers, gen, &sins.Src_Reg) {
|
if selectRegister(available_registers, gen, &sins.Src) {
|
||||||
|
|
||||||
if sins.GroupParIsSource == 0 {
|
if sins.GroupParIsSource == 0 {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
sins.OpGroupPar = sins.Src_Reg
|
sins.OpGroupPar = sins.Src
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -741,7 +741,7 @@ func (sins *SuperScalarInstruction) SelectDestination(preAllocatedAvailableRegis
|
||||||
preAllocatedAvailableRegisters = preAllocatedAvailableRegisters[:0]
|
preAllocatedAvailableRegisters = preAllocatedAvailableRegisters[:0]
|
||||||
|
|
||||||
for i := range Registers {
|
for i := range Registers {
|
||||||
if Registers[i].Latency <= cycle && (sins.CanReuse || i != sins.Src_Reg) &&
|
if Registers[i].Latency <= cycle && (sins.CanReuse || i != sins.Src) &&
|
||||||
(allowChainedMul || sins.OpGroup != S_IMUL_R || Registers[i].LastOpGroup != S_IMUL_R) &&
|
(allowChainedMul || sins.OpGroup != S_IMUL_R || Registers[i].LastOpGroup != S_IMUL_R) &&
|
||||||
(Registers[i].LastOpGroup != sins.OpGroup || Registers[i].LastOpPar != sins.OpGroupPar) &&
|
(Registers[i].LastOpGroup != sins.OpGroup || Registers[i].LastOpPar != sins.OpGroupPar) &&
|
||||||
(sins.Opcode != S_IADD_RS || i != RegisterNeedsDisplacement) {
|
(sins.Opcode != S_IADD_RS || i != RegisterNeedsDisplacement) {
|
||||||
|
@ -749,7 +749,7 @@ func (sins *SuperScalarInstruction) SelectDestination(preAllocatedAvailableRegis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return selectRegister(preAllocatedAvailableRegisters, gen, &sins.Dst_Reg)
|
return selectRegister(preAllocatedAvailableRegisters, gen, &sins.Dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectRegister(available_registers []int, gen *Blake2Generator, reg *int) bool {
|
func selectRegister(available_registers []int, gen *Blake2Generator, reg *int) bool {
|
||||||
|
@ -776,25 +776,25 @@ func executeSuperscalar(p []SuperScalarInstruction, r *RegisterLine) {
|
||||||
ins := &p[i]
|
ins := &p[i]
|
||||||
switch ins.Opcode {
|
switch ins.Opcode {
|
||||||
case S_ISUB_R:
|
case S_ISUB_R:
|
||||||
r[ins.Dst_Reg] -= r[ins.Src_Reg]
|
r[ins.Dst] -= r[ins.Src]
|
||||||
case S_IXOR_R:
|
case S_IXOR_R:
|
||||||
r[ins.Dst_Reg] ^= r[ins.Src_Reg]
|
r[ins.Dst] ^= r[ins.Src]
|
||||||
case S_IADD_RS:
|
case S_IADD_RS:
|
||||||
r[ins.Dst_Reg] += r[ins.Src_Reg] << ins.Imm32
|
r[ins.Dst] += r[ins.Src] << ins.Imm32
|
||||||
case S_IMUL_R:
|
case S_IMUL_R:
|
||||||
r[ins.Dst_Reg] *= r[ins.Src_Reg]
|
r[ins.Dst] *= r[ins.Src]
|
||||||
case S_IROR_C:
|
case S_IROR_C:
|
||||||
r[ins.Dst_Reg] = bits.RotateLeft64(r[ins.Dst_Reg], 0-int(ins.Imm32))
|
r[ins.Dst] = bits.RotateLeft64(r[ins.Dst], 0-int(ins.Imm32))
|
||||||
case S_IADD_C7, S_IADD_C8, S_IADD_C9:
|
case S_IADD_C7, S_IADD_C8, S_IADD_C9:
|
||||||
r[ins.Dst_Reg] += signExtend2sCompl(ins.Imm32)
|
r[ins.Dst] += signExtend2sCompl(ins.Imm32)
|
||||||
case S_IXOR_C7, S_IXOR_C8, S_IXOR_C9:
|
case S_IXOR_C7, S_IXOR_C8, S_IXOR_C9:
|
||||||
r[ins.Dst_Reg] ^= signExtend2sCompl(ins.Imm32)
|
r[ins.Dst] ^= signExtend2sCompl(ins.Imm32)
|
||||||
case S_IMULH_R:
|
case S_IMULH_R:
|
||||||
r[ins.Dst_Reg], _ = bits.Mul64(r[ins.Dst_Reg], r[ins.Src_Reg])
|
r[ins.Dst], _ = bits.Mul64(r[ins.Dst], r[ins.Src])
|
||||||
case S_ISMULH_R:
|
case S_ISMULH_R:
|
||||||
r[ins.Dst_Reg] = smulh(int64(r[ins.Dst_Reg]), int64(r[ins.Src_Reg]))
|
r[ins.Dst] = smulh(int64(r[ins.Dst]), int64(r[ins.Src]))
|
||||||
case S_IMUL_RCP:
|
case S_IMUL_RCP:
|
||||||
r[ins.Dst_Reg] *= randomx_reciprocal(ins.Imm32)
|
r[ins.Dst] *= ins.Imm64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,8 @@ func generateSuperscalarCode(scalarProgram SuperScalarProgram) SuperScalarProgra
|
||||||
for i := range p {
|
for i := range p {
|
||||||
instr := &p[i]
|
instr := &p[i]
|
||||||
|
|
||||||
dst := instr.Dst_Reg % RegistersCount
|
dst := instr.Dst % RegistersCount
|
||||||
src := instr.Src_Reg % RegistersCount
|
src := instr.Src % RegistersCount
|
||||||
|
|
||||||
switch instr.Opcode {
|
switch instr.Opcode {
|
||||||
case S_ISUB_R:
|
case S_ISUB_R:
|
||||||
|
@ -80,9 +80,9 @@ func generateSuperscalarCode(scalarProgram SuperScalarProgram) SuperScalarProgra
|
||||||
program = append(program, byte(0xc2+8*dst))
|
program = append(program, byte(0xc2+8*dst))
|
||||||
case S_IMUL_RCP:
|
case S_IMUL_RCP:
|
||||||
program = append(program, MOV_RAX_I...)
|
program = append(program, MOV_RAX_I...)
|
||||||
program = binary.LittleEndian.AppendUint64(program, randomx_reciprocal(instr.Imm32))
|
program = binary.LittleEndian.AppendUint64(program, instr.Imm64)
|
||||||
program = append(program, REX_IMUL_RM...)
|
program = append(program, REX_IMUL_RM...)
|
||||||
program = append(program, byte(0xc0+8*instr.Dst_Reg))
|
program = append(program, byte(0xc0+8*instr.Dst))
|
||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
24
vm.go
24
vm.go
|
@ -30,7 +30,7 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
package randomx
|
package randomx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.gammaspectra.live/P2Pool/go-randomx/v2/aes"
|
"git.gammaspectra.live/P2Pool/go-randomx/v3/aes"
|
||||||
"math"
|
"math"
|
||||||
"runtime"
|
"runtime"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
@ -45,11 +45,25 @@ type REG struct {
|
||||||
type VM struct {
|
type VM struct {
|
||||||
ScratchPad ScratchPad
|
ScratchPad ScratchPad
|
||||||
|
|
||||||
Dataset Randomx_Dataset
|
Dataset Dataset
|
||||||
|
|
||||||
JITProgram VMProgramFunc
|
JITProgram VMProgramFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewVM(dataset Dataset) *VM {
|
||||||
|
|
||||||
|
vm := &VM{
|
||||||
|
Dataset: dataset,
|
||||||
|
}
|
||||||
|
if dataset.Cache().HasJIT() {
|
||||||
|
vm.JITProgram = mapProgram(nil, int(RandomXCodeSize))
|
||||||
|
if dataset.Flags()&RANDOMX_FLAG_SECURE == 0 {
|
||||||
|
mapProgramRWX(vm.JITProgram)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vm
|
||||||
|
}
|
||||||
|
|
||||||
// Run calculate hash based on input
|
// Run calculate hash based on input
|
||||||
// Warning: Underlying callers will run float64 SetRoundingMode directly
|
// Warning: Underlying callers will run float64 SetRoundingMode directly
|
||||||
// It is the caller's responsibility to set and restore the mode to IEEE 754 roundTiesToEven between full executions
|
// It is the caller's responsibility to set and restore the mode to IEEE 754 roundTiesToEven between full executions
|
||||||
|
@ -86,7 +100,7 @@ func (vm *VM) Run(inputHash [64]byte, roundingMode uint8) (reg RegisterFile) {
|
||||||
addressRegisters >>= 1
|
addressRegisters >>= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
datasetOffset := (entropy[13] % (DATASETEXTRAITEMS + 1)) * CacheLineSize
|
datasetOffset := (entropy[13] % (DatasetExtraItems + 1)) * CacheLineSize
|
||||||
|
|
||||||
eMask := [2]uint64{EMask(entropy[14]), EMask(entropy[15])}
|
eMask := [2]uint64{EMask(entropy[14]), EMask(entropy[15])}
|
||||||
|
|
||||||
|
@ -95,8 +109,6 @@ func (vm *VM) Run(inputHash [64]byte, roundingMode uint8) (reg RegisterFile) {
|
||||||
spAddr0 := mem.mx
|
spAddr0 := mem.mx
|
||||||
spAddr1 := mem.ma
|
spAddr1 := mem.ma
|
||||||
|
|
||||||
var rlCache RegisterLine
|
|
||||||
|
|
||||||
if vm.JITProgram != nil {
|
if vm.JITProgram != nil {
|
||||||
if vm.Dataset.Flags()&RANDOMX_FLAG_SECURE > 0 {
|
if vm.Dataset.Flags()&RANDOMX_FLAG_SECURE > 0 {
|
||||||
mapProgramRW(vm.JITProgram)
|
mapProgramRW(vm.JITProgram)
|
||||||
|
@ -143,7 +155,7 @@ func (vm *VM) Run(inputHash [64]byte, roundingMode uint8) (reg RegisterFile) {
|
||||||
|
|
||||||
vm.Dataset.PrefetchDataset(datasetOffset + mem.mx)
|
vm.Dataset.PrefetchDataset(datasetOffset + mem.mx)
|
||||||
// execute diffuser superscalar program to get dataset 64 bytes
|
// execute diffuser superscalar program to get dataset 64 bytes
|
||||||
vm.Dataset.ReadDataset(datasetOffset+mem.ma, ®.R, &rlCache)
|
vm.Dataset.ReadDataset(datasetOffset+mem.ma, ®.R)
|
||||||
|
|
||||||
// swap the elements
|
// swap the elements
|
||||||
mem.mx, mem.ma = mem.ma, mem.mx
|
mem.mx, mem.ma = mem.ma, mem.mx
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
package randomx
|
package randomx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.gammaspectra.live/P2Pool/go-randomx/v2/asm"
|
"git.gammaspectra.live/P2Pool/go-randomx/v3/asm"
|
||||||
"math"
|
"math"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
)
|
)
|
||||||
|
|
|
@ -30,7 +30,7 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
package randomx
|
package randomx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.gammaspectra.live/P2Pool/go-randomx/v2/aes"
|
"git.gammaspectra.live/P2Pool/go-randomx/v3/aes"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
import "encoding/binary"
|
import "encoding/binary"
|
||||||
|
|
Loading…
Reference in a new issue