Skip to content

Instantly share code, notes, and snippets.

@KohakuBlueleaf
Last active November 26, 2021 14:29
Show Gist options
  • Save KohakuBlueleaf/3f8cccc79858284e00102a4e2885070d to your computer and use it in GitHub Desktop.
Save KohakuBlueleaf/3f8cccc79858284e00102a4e2885070d to your computer and use it in GitHub Desktop.
Xmrig benchmark script

Benchmark for Xmrig

This is a python script that run a customized stratum server to specific the algorigthm combinations of 6 CN algos(used in ghostrider) And it calculate the Expected Average Hashrate automatically(All the weights are as same as cpuminer-gr's)

How to run

You need to install python3.8↑ with pip first! And download the script to xmrig's path

linux

sudo python3 -m pip install requests

#run this at xmrig's path
sudo python3 benchmark.py

windows

you should open cmd/poweshell/windows terminal in admin mode

python -m pip install requests

#run this at xmrig's path
python benchmark.py

Argument

If you want to get a more precise data Use this

#linux
sudo python3 benchmark.py -20m

#windows
python benchmark.py -20m

This will run a longer benchmark which take 21~23min to complete

#native module
import os, sys
import socket
import threading as th
from json import loads, dumps
from time import sleep
from itertools import combinations
from subprocess import Popen, PIPE
#3rd-party module
from requests import get
'''
Constant
'''
#template for stratum protocol's mining.notify
NOTIFY_TEMPLATE = {
"id": None,
"method": "mining.notify",
"params": [
"{}",
"3960633ee64ba81fc408ced0e1a6a8bf0d2513cccab2d6eb78cb2c0e50ee4ded",
"03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff200364ee0204709b9f6108",
"0d2f6e6f64655374726174756d2f00000000068fea7648170000001976a91414abbb7ccb2822390579b2ad6703bf736f7a4c9088aca3ba1dd2050000001976a914cc36a8de36f6ff61dbfb798982e8b47354ad4e0e88acc20b5a16000000001976a914019e66f413066ca54ca58181fae890e636d6541f88aceb349564000000001976a9145b0ebe871b1ccea91909f435804d3a3f879e016988aceb349564000000001976a9146229d9b8f61eab84120c9b9ae46db9c3b2b1e21988ac027a3970560000001976a914ef126063b86e7c1d2f673bf7d1f785f2864e3ae088ac0000000046020064ee020047a9ad69c5fdac75bbe05c904331b53bb826955c3f94ce2c17e7b97997156363458f8c5a524f2f68c3e3395ed360547ba4a75cdb9feca60d32be828c929a1f67",
[],
"20000000",
"1c237b3d",
"619f9b70",
True
]
}
CONF = {
"http": {
"enabled": True,
"host": "127.0.0.1",
"port": 9999,
"access-token": "KBlueleaf Benchmark",
"restricted": True
},
"pools": [
{
"algo": "ghostrider",
"url": "127.0.0.1:8888",
"user": "",
"pass": "",
"keepalive": True,
"enabled": True,
}
]
}
#code for CN algos
ALGO_CODE = [
'00',
'11',
'22',
'33',
'44',
'55'
]
#weights for calculated the "Expected Average Hashrate"(from cpuminer-gr)
WEIGHTS = [
0.094555,
0.055475,
0.021256,
0.022461,
0.128275,
0.044810,
0.063186,
0.037458,
0.035620,
0.010150,
0.111971,
0.051201,
0.058180,
0.044812,
0.029149,
0.009918,
0.074940,
0.058862,
0.032738,
0.014982
]
# get runing path
CWD_PATH = os.getcwd()
'''
utils
'''
# encode and decode to/from utf-8
encode = lambda x:bytes(x, encoding='utf-8')
decode = lambda x:x.decode(encoding='utf-8')
def make_header(algo_choose, sub_rotation):
'''
make RTM mining header for specific rotation of CN algos
'''
header = ALGO_CODE[algo_choose[0]]*8
if sub_rotation:
header += ALGO_CODE[algo_choose[2]]*8
header += ALGO_CODE[algo_choose[1]]*8
else:
header += ALGO_CODE[algo_choose[1]]*8
header += ALGO_CODE[algo_choose[2]]*8
header += os.urandom(8).hex()
return header
def get_job(job_id, algo_choose, sub_rotation):
'''
get notify string
'''
new_notify = eval(repr(NOTIFY_TEMPLATE))
new_notify['params'][0].format(job_id)
new_notify['params'][1] = make_header(algo_choose, sub_rotation)
return dumps(new_notify)+'\n'
def get_hash_rate(period = 0):
'''
get hashrate from xmrig api
'''
try:
summary = get('http://127.0.0.1:9999/1/summary', headers={'Authorization': 'Bearer KBlueleaf Benchmark'}).content
hash_rate = loads(summary)['hashrate']['total'][period]
return hash_rate
except:
return 0
def bench_log(*string):
'''
log string
'''
print(
'============benchmark info============',
''.join(string),
'======================================',
sep='\n'
)
class StratumHandler(th.Thread):
'''
A thread object for handling the client(Miner)
'''
def __init__(self, *args, **kwargs):
super(StratumHandler, self).__init__(*args, **kwargs)
self.stop_flag = False
def stop(self):
self.stop_flag = True
def run(self):
global authed, conn
while not self.stop_flag:
try:
#check message every 0.5sec
#when self.stop is called, this setting can break the loop
conn.settimeout(0.5)
datas = decode(conn.recv(10000)).split('\n')
except TimeoutError:
datas = []
except socket.timeout:
datas = []
except ConnectionResetError:
break
#read all jsonrpc line
for i in datas:
if i:
#jsonrpc data
data = loads(i)
response = None
if 'id' in data:
id = data['id']
#check method
if data['method'] == 'mining.subscribe':
#make response
response = [
f'{{"id": {id},"result": [[["mining.set_difficulty","deadbeefcafebabe3b00000000000000"],["mining.notify","deadbeefcafebabe3b00000000000000"]],"",4],"error": null}}\n',
'{"id": null, "method": "mining.set_difficulty", "params":[10000000.0]}\n'
]
elif data['method'] == 'mining.authorize':
response = [
f'{{"id": {id}, "result": true, "error": null}}\n'
]
authed = True
if response is not None:
if isinstance(response, list):
for re in response:
conn.sendall(encode(re))
sleep(0.1)
else:
conn.sendall(encode(response))
'''
Main program
'''
authed = False
conn = None
def main():
'''
Main function
'''
global conn
'''
setup
'''
# parse args
if len(sys.argv)>1:
benchtime = sys.argv[1]
if benchtime not in {'-8m','-20m'}:
print('Invalid Arg')
return
else:
benchtime = '-8m'
# read existing config file for cpu config
config = None
if os.path.isfile(f'{CWD_PATH}/config.json'):
with open(f'{CWD_PATH}/config.json', 'r', encoding='utf-8') as f:
config = loads(f.read())
if config and 'cpu' in config:
CONF['cpu'] = config['cpu']
# write config file for benchmark
with open('./_benchamrk.json', 'w', encoding='utf-8') as f:
f.write(dumps(CONF))
# create custom stratum server
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('127.0.0.1',8888))
server.listen(1)
server.settimeout(3)
# create miner process
if sys.platform == 'win32':
miner = Popen([f'{CWD_PATH}/xmrig.exe', '--config', '_benchamrk.json'], stdin=PIPE)
else:
miner = Popen([f'{CWD_PATH}/xmrig', '--config','_benchamrk.json'], stdin=PIPE)
# connect with miner
while True:
try:
conn, addr = server.accept()
print(addr)
break
except TimeoutError:
continue
# start stratum handler
handler = StratumHandler()
handler.start()
'''
benchmark
'''
try:
# wait for auth
while not authed:
sleep(0.1)
# warm up for 5 secs
bench_log('Warm up')
conn.sendall(encode(get_job(-1, [2,3,1], 0)))
sleep(5)
# start benchmark
bench_log(
'Start Benchmark\n',
f'Set benchmark config to {benchtime} version\n',
'It may take ' ,
('8 minutes' if benchtime=='-8m' else '21 minutes'),
' to complete',
)
all_data = []
total = 0
# get all combination for all 6 algos
for id, algos in enumerate(combinations(range(6), 3)):
# every combination has 2 rotation pattern
for sub_rotation in range(2):
bench_log(
'Now Benchmark for:\n'
f'Rotation {id+1}.{sub_rotation+1}'
)
# send custom job to miner
conn.sendall(encode(get_job(id, algos, sub_rotation)))
sleep(2)
if benchtime=='-20m':
hash_rate = 0
for i in range(3):
sleep(10)
hash_rate += get_hash_rate(0)
hash_rate /= 3
elif benchtime=='-8m':
sleep(10)
hash_rate = get_hash_rate(0)
bench_log(
f'Rotation {id+1}.{sub_rotation+1} result:\n'
f'Hashrate: {hash_rate:.1f}H/s'
)
total += hash_rate*WEIGHTS[id]
all_data.append(hash_rate)
# print result
bench_log(
'Total Result\n'
f'Expected Average Hashrate: {total/2:.1f}H/s'
)
finally:
handler.stop()
miner.kill()
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
pass
except Exception as e:
print(e)
finally:
if os.path.isfile(f'./_benchmark.json'):
os.remove(f'./_benchamrk.json')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment