03: engine comparability
This commit is contained in:
12
README.md
12
README.md
@@ -1,8 +1,8 @@
|
||||
# External Miner Service for Quantus Network
|
||||
|
||||
Note: This repository is now a Cargo workspace. Build and run the CLI with:
|
||||
Note: This repository is now a Cargo workspace. Build and run the CLI with (the --num-cores flag remains available as an alias for --cores):
|
||||
- cargo build -p miner-cli --release
|
||||
- cargo run -p miner-cli -- --port 9833 [--metrics-port 9900] [--num-cores N]
|
||||
- cargo run -p miner-cli -- --port 9833 [--metrics-port 9900] [--cores N]
|
||||
|
||||
This crate provides an external mining service that can be used with a Quantus Network node. It exposes an HTTP API for
|
||||
managing mining jobs.
|
||||
@@ -25,7 +25,7 @@ The service can be configured using command-line arguments or environment variab
|
||||
| Argument | Environment Variable | Description | Default |
|
||||
|-------------------|----------------------|--------------------------------------------|---------------|
|
||||
| `--port <PORT>` | `MINER_PORT` | The port for the HTTP server to listen on. | `9833` |
|
||||
| `--num-cores <N>` | `MINER_CORES` | The number of CPU cores to use for mining. | All available |
|
||||
| `--cores <N>` (alias: `--num-cores`) | `MINER_CORES` | The number of CPU cores to use for mining. | All available |
|
||||
|
||||
Example:
|
||||
|
||||
@@ -34,7 +34,7 @@ Example:
|
||||
../target/release/quantus-miner
|
||||
|
||||
# Run on a custom port with 4 cores
|
||||
../target/release/quantus-miner --port 8000 --num-cores 4
|
||||
../target/release/quantus-miner --port 8000 --cores 4
|
||||
|
||||
# Equivalent using environment variables
|
||||
export MINER_PORT=8000
|
||||
@@ -51,10 +51,10 @@ After building the service, you can run it directly from the command line:
|
||||
RUST_LOG=info ../target/release/quantus-miner
|
||||
|
||||
# Run with a specific port and 2 cores
|
||||
RUST_LOG=info ../target/release/quantus-miner --port 12345 --num-cores 2
|
||||
RUST_LOG=info ../target/release/quantus-miner --port 12345 --cores 2
|
||||
|
||||
# Run in debug mode
|
||||
RUST_LOG=info,miner=debug ../target/release/quantus-miner --num-cores 4
|
||||
RUST_LOG=info,miner=debug ../target/release/quantus-miner --cores 4
|
||||
|
||||
```
|
||||
|
||||
|
||||
@@ -43,10 +43,19 @@ pub struct Candidate {
|
||||
/// `Running` is included for potential future async/streaming engines.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum EngineStatus {
|
||||
Running { hash_count: u64 },
|
||||
Found(Candidate),
|
||||
Exhausted { hash_count: u64 },
|
||||
Cancelled { hash_count: u64 },
|
||||
Running {
|
||||
hash_count: u64,
|
||||
},
|
||||
Found {
|
||||
candidate: Candidate,
|
||||
hash_count: u64,
|
||||
},
|
||||
Exhausted {
|
||||
hash_count: u64,
|
||||
},
|
||||
Cancelled {
|
||||
hash_count: u64,
|
||||
},
|
||||
}
|
||||
|
||||
/// Abstract mining engine interface.
|
||||
@@ -120,7 +129,10 @@ impl MinerEngine for BaselineCpuEngine {
|
||||
work,
|
||||
distance,
|
||||
};
|
||||
return EngineStatus::Found(candidate);
|
||||
return EngineStatus::Found {
|
||||
candidate,
|
||||
hash_count,
|
||||
};
|
||||
}
|
||||
|
||||
// Advance or finish
|
||||
@@ -182,11 +194,14 @@ impl MinerEngine for FastCpuEngine {
|
||||
|
||||
if is_valid_distance(ctx, distance) {
|
||||
let work = current.to_big_endian();
|
||||
return EngineStatus::Found(Candidate {
|
||||
nonce: current,
|
||||
work,
|
||||
distance,
|
||||
});
|
||||
return EngineStatus::Found {
|
||||
candidate: Candidate {
|
||||
nonce: current,
|
||||
work,
|
||||
distance,
|
||||
},
|
||||
hash_count,
|
||||
};
|
||||
}
|
||||
|
||||
if current == range.end {
|
||||
|
||||
@@ -11,7 +11,7 @@ struct Args {
|
||||
port: u16,
|
||||
|
||||
/// Number of CPU cores to use for mining (defaults to all logical CPUs)
|
||||
#[arg(long, env = "MINER_CORES")]
|
||||
#[arg(long = "cores", alias = "num-cores", env = "MINER_CORES")]
|
||||
num_cores: Option<usize>,
|
||||
|
||||
/// Optional Prometheus metrics exporter port; if omitted, metrics are disabled
|
||||
@@ -48,7 +48,11 @@ impl From<EngineCli> for EngineSelection {
|
||||
async fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
// Initialize logger early to capture startup messages
|
||||
// Initialize logger early to capture startup messages.
|
||||
// If RUST_LOG is not set, default to info level for our app.
|
||||
if std::env::var("RUST_LOG").is_err() {
|
||||
std::env::set_var("RUST_LOG", "info");
|
||||
}
|
||||
env_logger::init();
|
||||
|
||||
// Log effective configuration
|
||||
|
||||
@@ -479,17 +479,20 @@ fn mine_range_with_engine(
|
||||
};
|
||||
|
||||
match status {
|
||||
engine_cpu::EngineStatus::Found(EngineCandidate {
|
||||
nonce,
|
||||
work,
|
||||
distance,
|
||||
}) => {
|
||||
engine_cpu::EngineStatus::Found {
|
||||
candidate: EngineCandidate {
|
||||
nonce,
|
||||
work,
|
||||
distance,
|
||||
},
|
||||
hash_count,
|
||||
} => {
|
||||
final_result.result = Some(MiningJobResult {
|
||||
nonce,
|
||||
work,
|
||||
distance,
|
||||
});
|
||||
final_result.hash_count = 0; // engine may not report per-iteration counts
|
||||
final_result.hash_count = hash_count;
|
||||
}
|
||||
engine_cpu::EngineStatus::Exhausted { hash_count } => {
|
||||
final_result.hash_count = hash_count;
|
||||
|
||||
405
docs/grafana/engines-comparison-dashboard.json
Normal file
405
docs/grafana/engines-comparison-dashboard.json
Normal file
@@ -0,0 +1,405 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"type": "stat",
|
||||
"title": "Engine Hash Rate Ratio (left/right)",
|
||||
"description": "Ratio of total per-job hash rate between two selected engines",
|
||||
"gridPos": { "h": 5, "w": 8, "x": 0, "y": 0 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "sum(miner_job_hash_rate{engine=~\"$engine_left\"}) / sum(miner_job_hash_rate{engine=~\"$engine_right\"})"
|
||||
}
|
||||
],
|
||||
"options": {
|
||||
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
|
||||
"orientation": "auto",
|
||||
"colorMode": "value",
|
||||
"graphMode": "none",
|
||||
"justifyMode": "auto",
|
||||
"textMode": "value"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": { "unit": "none", "decimals": 2 },
|
||||
"overrides": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "stat",
|
||||
"title": "Solutions Found (24h) by Engine (left/right)",
|
||||
"description": "Completed jobs increase in the last 24h for left and right engines",
|
||||
"gridPos": { "h": 5, "w": 8, "x": 8, "y": 0 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "Left",
|
||||
"expr": "sum(increase(miner_jobs_by_engine_total{engine=~\"$engine_left\", status=\"completed\"}[24h]))"
|
||||
},
|
||||
{
|
||||
"refId": "Right",
|
||||
"expr": "sum(increase(miner_jobs_by_engine_total{engine=~\"$engine_right\", status=\"completed\"}[24h]))"
|
||||
}
|
||||
],
|
||||
"options": {
|
||||
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
|
||||
"orientation": "horizontal",
|
||||
"colorMode": "value",
|
||||
"graphMode": "none",
|
||||
"justifyMode": "auto",
|
||||
"textMode": "auto"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": { "unit": "none", "decimals": 0 },
|
||||
"overrides": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "stat",
|
||||
"title": "Global Hash Rate (All Engines)",
|
||||
"description": "Sum of reported job hash rates across all engines",
|
||||
"gridPos": { "h": 5, "w": 8, "x": 16, "y": 0 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "sum(miner_job_hash_rate)"
|
||||
}
|
||||
],
|
||||
"options": {
|
||||
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
|
||||
"orientation": "auto",
|
||||
"colorMode": "value",
|
||||
"graphMode": "area",
|
||||
"justifyMode": "auto",
|
||||
"textMode": "value"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": { "unit": "ops" },
|
||||
"overrides": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Hash Rate by Engine",
|
||||
"description": "sum(miner_job_hash_rate) grouped by engine",
|
||||
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 5 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "sum by (engine) (miner_job_hash_rate{engine=~\"$engine\"})",
|
||||
"legendFormat": "{{engine}}"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": { "unit": "ops" },
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"legend": { "displayMode": "list", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi", "sort": "none" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "bargauge",
|
||||
"title": "Jobs by Engine and Status (Total)",
|
||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 13 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "sum by (engine, status) (miner_jobs_by_engine_total{engine=~\"$engine\"})"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": { "unit": "none", "decimals": 0 },
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"displayMode": "gradient",
|
||||
"orientation": "horizontal",
|
||||
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
|
||||
"showUnfilled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Per-Engine Total Hashes Increase (over time)",
|
||||
"description": "increase(miner_job_hashes_total[$__rate_interval]) grouped by engine",
|
||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 13 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "sum by (engine) (increase(miner_job_hashes_total{engine=~\"$engine\"}[$__rate_interval]))",
|
||||
"legendFormat": "{{engine}}"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": { "unit": "ops" },
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"legend": { "displayMode": "list", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi", "sort": "none" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Average Per-Thread Hash Rate by Engine",
|
||||
"description": "avg(miner_thread_hash_rate) grouped by engine (EMA-smoothed)",
|
||||
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 21 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "avg by (engine) (miner_thread_hash_rate{engine=~\"$engine\"})",
|
||||
"legendFormat": "{{engine}}"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": { "unit": "ops" },
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"legend": { "displayMode": "list", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi", "sort": "none" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "table",
|
||||
"title": "Top 10 Jobs by Hash Rate (filter by engine)",
|
||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 29 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "topk(10, miner_job_hash_rate{engine=~\"$engine\"})"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": { "align": "auto" },
|
||||
"unit": "ops",
|
||||
"decimals": 2
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"showHeader": true,
|
||||
"footer": { "show": false },
|
||||
"frameIndex": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "table",
|
||||
"title": "Per-Engine Total Hashes (5m increase)",
|
||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 29 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "sum by (engine) (increase(miner_job_hashes_total{engine=~\"$engine\"}[5m]))"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": { "align": "auto" },
|
||||
"unit": "ops",
|
||||
"decimals": 0
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"showHeader": true,
|
||||
"footer": { "show": false },
|
||||
"frameIndex": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Per-Job Hash Rate by Engine (focus comparisons)",
|
||||
"description": "Use filters above to narrow by engine(s) and job(s)",
|
||||
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 37 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "miner_job_hash_rate{engine=~\"$engine\", job_id=~\"$job_id\"}",
|
||||
"legendFormat": "{{engine}} | {{job_id}}"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": { "unit": "ops" },
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"legend": { "displayMode": "list", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi", "sort": "none" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "table",
|
||||
"title": "Per-Thread Hash Rate (filter by engine and job)",
|
||||
"description": "EMA-smoothed per-thread rates with labels for engine/job/thread",
|
||||
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 45 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "miner_thread_hash_rate{engine=~\"$engine\", job_id=~\"$job_id\", thread_id=~\"$thread_id\"}"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": { "align": "auto" },
|
||||
"unit": "ops",
|
||||
"decimals": 2
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"showHeader": true,
|
||||
"footer": { "show": false },
|
||||
"frameIndex": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"refresh": "10s",
|
||||
"schemaVersion": 38,
|
||||
"style": "dark",
|
||||
"tags": ["quantus", "miner", "prometheus", "engines", "comparison"],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"type": "datasource",
|
||||
"name": "DS_PROMETHEUS",
|
||||
"hide": 0,
|
||||
"query": "prometheus",
|
||||
"current": {},
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"type": "query",
|
||||
"name": "engine",
|
||||
"label": "Engine(s)",
|
||||
"hide": 0,
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"definition": "label_values(miner_job_hash_rate, engine)",
|
||||
"includeAll": true,
|
||||
"multi": true,
|
||||
"refresh": 2,
|
||||
"sort": 1,
|
||||
"query": "label_values(miner_job_hash_rate, engine)",
|
||||
"regex": "",
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": ["cpu-baseline", "cpu-fast"],
|
||||
"value": ["cpu-baseline", "cpu-fast"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "query",
|
||||
"name": "engine_left",
|
||||
"label": "Left Engine",
|
||||
"hide": 0,
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"definition": "label_values(miner_job_hash_rate, engine)",
|
||||
"includeAll": false,
|
||||
"multi": false,
|
||||
"refresh": 2,
|
||||
"sort": 1,
|
||||
"query": "label_values(miner_job_hash_rate, engine)",
|
||||
"regex": "",
|
||||
"current": {
|
||||
"selected": true,
|
||||
"text": "cpu-fast",
|
||||
"value": "cpu-fast"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "query",
|
||||
"name": "engine_right",
|
||||
"label": "Right Engine",
|
||||
"hide": 0,
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"definition": "label_values(miner_job_hash_rate, engine)",
|
||||
"includeAll": false,
|
||||
"multi": false,
|
||||
"refresh": 2,
|
||||
"sort": 1,
|
||||
"query": "label_values(miner_job_hash_rate, engine)",
|
||||
"regex": "",
|
||||
"current": {
|
||||
"selected": true,
|
||||
"text": "cpu-baseline",
|
||||
"value": "cpu-baseline"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "query",
|
||||
"name": "job_id",
|
||||
"label": "Job ID",
|
||||
"hide": 0,
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"definition": "label_values(miner_job_hash_rate{engine=~\"$engine\"}, job_id)",
|
||||
"includeAll": true,
|
||||
"multi": true,
|
||||
"refresh": 2,
|
||||
"sort": 1,
|
||||
"query": "label_values(miner_job_hash_rate{engine=~\"$engine\"}, job_id)",
|
||||
"regex": "",
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": ["All"],
|
||||
"value": ["$__all"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "query",
|
||||
"name": "thread_id",
|
||||
"label": "Thread ID",
|
||||
"hide": 0,
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"definition": "label_values(miner_thread_hash_rate{engine=~\"$engine\", job_id=~\"$job_id\"}, thread_id)",
|
||||
"includeAll": true,
|
||||
"multi": true,
|
||||
"refresh": 2,
|
||||
"sort": 1,
|
||||
"query": "label_values(miner_thread_hash_rate{engine=~\"$engine\", job_id=~\"$job_id\"}, thread_id)",
|
||||
"regex": "",
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": ["All"],
|
||||
"value": ["$__all"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": { "from": "now-6h", "to": "now" },
|
||||
"timepicker": {},
|
||||
"timezone": "",
|
||||
"title": "Quantus Miner - Engines Comparison",
|
||||
"version": 1,
|
||||
"uid": null
|
||||
}
|
||||
302
docs/grafana/miner-dashboard.json
Normal file
302
docs/grafana/miner-dashboard.json
Normal file
@@ -0,0 +1,302 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"type": "stat",
|
||||
"title": "Global Hash Rate (nonces/sec)",
|
||||
"description": "Sum of reported miner hash rate across all jobs and engines",
|
||||
"gridPos": { "h": 5, "w": 8, "x": 0, "y": 0 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "sum(miner_hash_rate)"
|
||||
}
|
||||
],
|
||||
"options": {
|
||||
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
|
||||
"orientation": "auto",
|
||||
"colorMode": "value",
|
||||
"graphMode": "none",
|
||||
"justifyMode": "auto",
|
||||
"textMode": "value"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": { "unit": "ops" },
|
||||
"overrides": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Global Hash Rate (nonces/sec) over time",
|
||||
"gridPos": { "h": 8, "w": 16, "x": 8, "y": 0 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "sum(miner_hash_rate)",
|
||||
"legendFormat": "global"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": { "unit": "ops" },
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"legend": { "displayMode": "list", "placement": "bottom", "calcs": [] },
|
||||
"tooltip": { "mode": "multi", "sort": "none" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "bargauge",
|
||||
"title": "Jobs by Status (total)",
|
||||
"gridPos": { "h": 8, "w": 8, "x": 0, "y": 5 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "sum by (status) (miner_jobs_total)"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": { "unit": "none", "decimals": 0 },
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"displayMode": "gradient",
|
||||
"orientation": "horizontal",
|
||||
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
|
||||
"showUnfilled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "bargauge",
|
||||
"title": "Jobs by Engine and Status (total)",
|
||||
"gridPos": { "h": 8, "w": 8, "x": 0, "y": 13 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "sum by (engine, status) (miner_jobs_by_engine_total)"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": { "unit": "none", "decimals": 0 },
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"displayMode": "gradient",
|
||||
"orientation": "horizontal",
|
||||
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
|
||||
"showUnfilled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Job Hash Rate by Job (filter by engine)",
|
||||
"gridPos": { "h": 8, "w": 16, "x": 8, "y": 8 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "sum by (job_id) (miner_job_hash_rate{engine=~\"$engine\"})",
|
||||
"legendFormat": "{{job_id}}"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": { "unit": "ops" },
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"legend": { "displayMode": "list", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi", "sort": "none" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Thread Hash Rate (filter by engine and job_id)",
|
||||
"description": "EMA-smoothed per-thread rates. Use variables above to filter.",
|
||||
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 21 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "miner_thread_hash_rate{engine=~\"$engine\", job_id=~\"$job_id\", thread_id=~\"$thread_id\"}",
|
||||
"legendFormat": "{{engine}} | job={{job_id}} | th={{thread_id}}"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": { "unit": "ops" },
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"legend": { "displayMode": "list", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi", "sort": "none" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "stat",
|
||||
"title": "Total Hashes (5m increase)",
|
||||
"gridPos": { "h": 5, "w": 8, "x": 0, "y": 29 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "increase(miner_hashes_total[5m])"
|
||||
}
|
||||
],
|
||||
"options": {
|
||||
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
|
||||
"orientation": "auto",
|
||||
"colorMode": "value",
|
||||
"graphMode": "none",
|
||||
"justifyMode": "auto",
|
||||
"textMode": "value"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": { "unit": "ops" },
|
||||
"overrides": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "table",
|
||||
"title": "Per-Job Hashes (5m increase, filter by engine)",
|
||||
"gridPos": { "h": 8, "w": 16, "x": 8, "y": 29 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "sum by (engine, job_id) (increase(miner_job_hashes_total{engine=~\"$engine\"}[5m]))"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": { "custom": { "align": "auto" } },
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"showHeader": true,
|
||||
"footer": { "show": false },
|
||||
"frameIndex": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Job Status Gauges (running/completed/failed/cancelled)",
|
||||
"description": "Job status gauges as time series (1 for current status, else 0). Filter by engine/job.",
|
||||
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 37 },
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "miner_job_status{engine=~\"$engine\", job_id=~\"$job_id\"}",
|
||||
"legendFormat": "{{job_id}}:{{status}}"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": { "unit": "none", "min": 0, "max": 1 },
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"legend": { "displayMode": "list", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi", "sort": "none" }
|
||||
}
|
||||
}
|
||||
],
|
||||
"refresh": "10s",
|
||||
"schemaVersion": 38,
|
||||
"style": "dark",
|
||||
"tags": ["quantus", "miner", "prometheus"],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"type": "datasource",
|
||||
"name": "DS_PROMETHEUS",
|
||||
"hide": 0,
|
||||
"query": "prometheus",
|
||||
"current": {},
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"type": "query",
|
||||
"name": "engine",
|
||||
"label": "Engine",
|
||||
"hide": 0,
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"definition": "label_values(miner_job_hash_rate, engine)",
|
||||
"includeAll": true,
|
||||
"multi": true,
|
||||
"refresh": 2,
|
||||
"sort": 1,
|
||||
"query": "label_values(miner_job_hash_rate, engine)",
|
||||
"regex": "",
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": ["All"],
|
||||
"value": ["$__all"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "query",
|
||||
"name": "job_id",
|
||||
"label": "Job ID",
|
||||
"hide": 0,
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"definition": "label_values(miner_job_hash_rate{engine=~\"$engine\"}, job_id)",
|
||||
"includeAll": true,
|
||||
"multi": true,
|
||||
"refresh": 2,
|
||||
"sort": 1,
|
||||
"query": "label_values(miner_job_hash_rate{engine=~\"$engine\"}, job_id)",
|
||||
"regex": "",
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": ["All"],
|
||||
"value": ["$__all"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "query",
|
||||
"name": "thread_id",
|
||||
"label": "Thread ID",
|
||||
"hide": 0,
|
||||
"datasource": "$DS_PROMETHEUS",
|
||||
"definition": "label_values(miner_thread_hash_rate{engine=~\"$engine\", job_id=~\"$job_id\"}, thread_id)",
|
||||
"includeAll": true,
|
||||
"multi": true,
|
||||
"refresh": 2,
|
||||
"sort": 1,
|
||||
"query": "label_values(miner_thread_hash_rate{engine=~\"$engine\", job_id=~\"$job_id\"}, thread_id)",
|
||||
"regex": "",
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": ["All"],
|
||||
"value": ["$__all"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": { "from": "now-6h", "to": "now" },
|
||||
"timepicker": {},
|
||||
"timezone": "",
|
||||
"title": "Quantus Miner - Overview",
|
||||
"version": 1,
|
||||
"uid": null
|
||||
}
|
||||
113
docs/implementation/03-prompt-engine-comparability.md
Normal file
113
docs/implementation/03-prompt-engine-comparability.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# Prompt Summary (03): Accurate Metrics for Engine Comparisons, Logging Defaults, CLI Flag Rename, and Dashboards
|
||||
|
||||
## Context
|
||||
|
||||
To compare the performance of different mining engines (e.g., `cpu-baseline` vs `cpu-fast`) in Grafana using Prometheus metrics, we need precise per-job and per-thread hash counts. Currently, when a worker thread finds a solution, the engine reports only the `Candidate` without the associated `hash_count`. This causes undercounting in totals/rates, especially for the winning thread.
|
||||
|
||||
Additionally, we want a better out-of-the-box operator experience and evaluation tooling:
|
||||
- Default the logging level to `info` when `RUST_LOG` is not set.
|
||||
- Rename the CLI flag `--num-cores` to `--cores` but keep the internal field `num_cores`, and preserve `--num-cores` as an alias for backward compatibility.
|
||||
- Provide two Grafana dashboards:
|
||||
- A “Miner Overview” dashboard for operators, focusing on the fastest implementation.
|
||||
- An “Engines Comparison” dashboard for performance evaluations across implementations.
|
||||
|
||||
We want to extend the engine’s result reporting so that the final “Found” event carries the `hash_count` accumulated by that thread. The miner-service will consume this information to update metrics correctly (global, per-job, per-thread), enabling accurate comparison between engines.
|
||||
|
||||
We also want sane defaults for logging, a consistent CLI, and packaged dashboards to support evaluation and production usage.
|
||||
|
||||
## Objectives
|
||||
|
||||
- Extend the engine result type to carry per-thread `hash_count` when a solution is found.
|
||||
- Update both CPU engines (`cpu-baseline` and `cpu-fast`) to return the `hash_count` on `Found`.
|
||||
- Update the miner-service to:
|
||||
- Include `hash_count` from the `Found` status in totals and rate calculations.
|
||||
- Maintain engine-aware, per-job, and per-thread metrics parity.
|
||||
- Preserve the node-facing HTTP API and service behavior.
|
||||
- Keep engine selection and metrics toggle behavior unchanged.
|
||||
- Default RUST_LOG to `info` if not set, so operators get useful logs without extra env configuration.
|
||||
- Rename CLI flag to `--cores` (keep `--num-cores` as an alias) while keeping the code field as `num_cores`.
|
||||
- Add two Grafana dashboards under `docs/grafana/`:
|
||||
- `miner-dashboard.json` for operators running the fastest engine.
|
||||
- `engines-comparison-dashboard.json` for evaluating implementations.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Engine API:
|
||||
- Change `EngineStatus::Found(Candidate)` to carry `{ candidate: Candidate, hash_count: u64 }`.
|
||||
- Maintain the existing `Exhausted { hash_count }` and `Cancelled { hash_count }` patterns for consistency.
|
||||
- Engines:
|
||||
- `BaselineCpuEngine` and `FastCpuEngine` must return accurate `hash_count` on `Found`.
|
||||
- Service:
|
||||
- In `update_from_results`, when a `ThreadResult` is built from an engine’s `Found`, carry the returned `hash_count` and accumulate it in:
|
||||
- Global totals: `miner_hashes_total`
|
||||
- Per-job totals: `miner_job_hashes_total{engine,job_id}`
|
||||
- Per-thread totals: `miner_thread_hashes_total{engine,job_id,thread_id}`
|
||||
- Ensure per-thread delta-rate and EMA smoothing logic still runs using the `hash_count` returned by `Found`.
|
||||
- Metrics:
|
||||
- No new metrics required; existing labeled counters/gauges should be used.
|
||||
- Accuracy improves because the winning thread’s final work is now captured.
|
||||
- Logging:
|
||||
- If `RUST_LOG` is unset, default to `info` at startup.
|
||||
- CLI:
|
||||
- Rename `--num-cores` to `--cores`, keeping `--num-cores` as an alias and `num_cores` as the field name.
|
||||
- Dashboards:
|
||||
- Add `docs/grafana/miner-dashboard.json` (operator view).
|
||||
- Add `docs/grafana/engines-comparison-dashboard.json` (evaluation view).
|
||||
|
||||
## Constraints and Compatibility
|
||||
|
||||
- The public HTTP API (endpoints/payloads) must remain unchanged.
|
||||
- The engine abstraction is internal to the repo, so the type change is acceptable and should be updated wherever consumed (service only).
|
||||
- Maintain compatibility between `cpu-baseline` and `cpu-fast` engines for apples-to-apples comparison.
|
||||
- Logging default must not override an explicitly set `RUST_LOG`.
|
||||
- CLI change must preserve backward compatibility (`--num-cores` alias).
|
||||
- Dashboards must be optional artifacts (no runtime impact).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- `EngineStatus::Found` carries `{ candidate, hash_count }`.
|
||||
- Both engines report the correct `hash_count` when returning `Found`.
|
||||
- Miner-service uses this `hash_count` to update:
|
||||
- `miner_hashes_total`
|
||||
- `miner_job_hashes_total{engine,job_id}`
|
||||
- `miner_thread_hashes_total{engine,job_id,thread_id}`
|
||||
- Per-thread delta-rate → `miner_thread_hash_rate{engine,job_id,thread_id}` (EMA-smoothed).
|
||||
- Build success in release mode.
|
||||
- No changes to node-facing HTTP behavior.
|
||||
- Grafana can now accurately compare `cpu-baseline` vs `cpu-fast` with improved fidelity for the winning thread.
|
||||
- When `RUST_LOG` is unset, logs default to `info`.
|
||||
- `--cores` is available (with `--num-cores` as an alias) and maps to `num_cores`.
|
||||
- Two dashboards exist under `docs/grafana/` for operators and engine evaluation.
|
||||
|
||||
## Deliverables
|
||||
|
||||
- Engine API update:
|
||||
- Enum change for `EngineStatus::Found` to include `hash_count`.
|
||||
- Engine implementations:
|
||||
- Baseline and fast CPU engines updated to return `hash_count` on `Found`.
|
||||
- Service updates:
|
||||
- `mine_range_with_engine` and `update_from_results` modified to forward and accumulate the `hash_count` from `Found`.
|
||||
- Metrics update hooks remain correct and comprehensive (global, per-job, per-thread, per-thread EMA rate).
|
||||
- CLI/logging updates:
|
||||
- Default `RUST_LOG` to `info` when unset.
|
||||
- Expose `--cores` flag (keep `--num-cores` alias) while retaining `num_cores` as the code field.
|
||||
- Dashboards:
|
||||
- `docs/grafana/miner-dashboard.json` (operator-focused).
|
||||
- `docs/grafana/engines-comparison-dashboard.json` (engine evaluation).
|
||||
- Documentation:
|
||||
- Capture this prompt and a separate response summary as iteration “03” in `docs/implementation/`.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Additional metrics beyond those already implemented.
|
||||
- Changes to the HTTP API or external protocol.
|
||||
- Performance optimizations (e.g., Montgomery arithmetic) not directly related to the `hash_count` reporting tweak.
|
||||
- Further CLI renames or logging customization (beyond defaults).
|
||||
- Grafana provisioning/export automation.
|
||||
|
||||
## Notes
|
||||
|
||||
- This tweak corrects the main remaining accuracy gap for the winning thread in metrics.
|
||||
- With this change, Grafana dashboards can make fair and accurate comparisons between different engines using the `engine` label, across global/job/thread-level telemetry.
|
||||
- Default logging reduces operational friction; CLI alias retains backward compatibility during the transition.
|
||||
- Packaged dashboards (overview and engines comparison) provide out-of-the-box visibility for operators and evaluators.
|
||||
89
docs/implementation/03-response-engine-comparability.md
Normal file
89
docs/implementation/03-response-engine-comparability.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Response Summary (03): Engine `Found` hash_count Included for Accurate Metrics, Logging/CLI Updates, and Dashboards
|
||||
|
||||
This document summarizes the implementation that augments the mining engine’s result reporting so that the final “Found” event carries the per-thread `hash_count`. This enables accurate global, per-job, and per-thread metrics (including rates), improving Grafana comparisons between engines (e.g., `cpu-baseline` vs `cpu-fast`).
|
||||
|
||||
## Overview
|
||||
|
||||
- Extended the engine result type so `Found` includes `{ candidate, hash_count }`.
|
||||
- Updated both CPU engines to return the final `hash_count` on `Found`.
|
||||
- Modified the service to forward and accumulate this count:
|
||||
- Global totals (`miner_hashes_total`)
|
||||
- Per-job totals (`miner_job_hashes_total{engine,job_id}`)
|
||||
- Per-thread totals (`miner_thread_hashes_total{engine,job_id,thread_id}`)
|
||||
- Per-thread hash-rate gauge via delta-rate with EMA smoothing
|
||||
- No changes to the node-facing HTTP API.
|
||||
|
||||
## Changes Implemented
|
||||
|
||||
### 1) Engine API
|
||||
|
||||
- File: `crates/engine-cpu/src/lib.rs`
|
||||
- Updated `EngineStatus`:
|
||||
- Before: `Found(Candidate)`
|
||||
- Now: `Found { candidate: Candidate, hash_count: u64 }`
|
||||
- Other variants (`Exhausted`, `Cancelled`, `Running`) retain their `hash_count` shape for consistency.
|
||||
|
||||
### 2) Engines
|
||||
|
||||
- Files: `crates/engine-cpu/src/lib.rs`
|
||||
- `BaselineCpuEngine`:
|
||||
- On discovering a solution, returns `EngineStatus::Found { candidate, hash_count }` with the accumulated count for the current range.
|
||||
- `FastCpuEngine`:
|
||||
- Same behavior as baseline but using the incremental path (init once with `init_worker_y0`, then `step_mul` per nonce).
|
||||
|
||||
### 3) Service
|
||||
|
||||
- File: `crates/miner-service/src/lib.rs`
|
||||
- `mine_range_with_engine`:
|
||||
- Updated to extract `hash_count` from `EngineStatus::Found` and include it in the `ThreadResult`.
|
||||
- `update_from_results`:
|
||||
- Accumulates `hash_count` into:
|
||||
- `total_hash_count`
|
||||
- Global/engine-aware per-job/per-thread counters
|
||||
- Per-thread delta-rate calculation with EMA smoothing now uses the `hash_count` from `Found` as well.
|
||||
- All metric updates are engine-aware and job-/thread-labeled where appropriate.
|
||||
|
||||
### 4) Metrics
|
||||
|
||||
- No new metrics introduced; existing labeled series are now fully accurate for winning threads:
|
||||
- Global:
|
||||
- `miner_jobs_total{status}`
|
||||
- `miner_hashes_total`
|
||||
- `miner_hash_rate`
|
||||
- Engine-/job-aware:
|
||||
- `miner_job_hashes_total{engine,job_id}`
|
||||
- `miner_job_hash_rate{engine,job_id}`
|
||||
- `miner_job_status{engine,job_id,status}` (IntGauge)
|
||||
- `miner_jobs_by_engine_total{engine,status}`
|
||||
- Engine-/job-/thread-aware:
|
||||
- `miner_thread_hashes_total{engine,job_id,thread_id}`
|
||||
- `miner_thread_hash_rate{engine,job_id,thread_id}` (EMA-smoothed)
|
||||
|
||||
## Compatibility
|
||||
|
||||
- The public HTTP API (endpoints and payloads) remains unchanged.
|
||||
- Engine abstraction change is internal to the workspace; service updated accordingly.
|
||||
- Works with both `cpu-baseline` and `cpu-fast`, enabling apples-to-apples comparisons using the `engine` label in Grafana.
|
||||
|
||||
## Build and Run
|
||||
|
||||
- Build (release):
|
||||
- `cargo build -p miner-cli --release`
|
||||
- Run (baseline, no metrics):
|
||||
- `cargo run -p miner-cli -- --port 9833 --engine cpu-baseline`
|
||||
- Run (fast engine, with metrics):
|
||||
- `cargo run -p miner-cli -- --port 9833 --engine cpu-fast --metrics-port 9900`
|
||||
- Scrape: `http://localhost:9900/metrics`
|
||||
|
||||
## Verification
|
||||
|
||||
- Compile-time: Verified in release mode after updating the service to consume the new `Found` shape.
|
||||
- Runtime: The winning thread’s final work is now counted in totals and informs the per-thread rate EMA, reducing undercounting and improving rate fidelity.
|
||||
|
||||
## Notes and Next Steps
|
||||
|
||||
- With winning-thread `hash_count` included, Grafana dashboards can fairly compare engines across global/job/thread metrics.
|
||||
- Next logical optimization steps:
|
||||
- Add correctness tests that compare baseline vs fast engine outcomes on small ranges (golden tests).
|
||||
- Add benchmark harness to quantify nonces/sec improvements.
|
||||
- Implement Montgomery multiplication in `pow-core` and switch `cpu-fast` to it for further speedups.
|
||||
Reference in New Issue
Block a user