Skip to content
Snippets Groups Projects
Commit 14493488 authored by Éloïs's avatar Éloïs
Browse files

Revert "Merge branch 'better_cpu_adjustement' into '1.6'"

This reverts merge request !1218
parent dd73c43b
No related branches found
No related tags found
2 merge requests!1222Add startup scripts,!1221Revert "Merge branch 'better_cpu_adjustement' into '1.6'"
const SAMPLING_PERIOD = 150 // milliseconds
const MAX_SAMPLES_DISTANCE = 20 * 1000000 // seconds
function getMicrosecondsTime() {
const [ seconds, nanoseconds ] = process.hrtime()
return seconds * 1000000 + nanoseconds / 1000
}
interface CpuUsage {
user: number
system:number
}
interface CpuUsageAt {
usage:number
at:number // microseconds timestamp
elapsed:number // microseconds elapsed for this result
}
export class ProcessCpuProfiler {
private cumulatedUsage: CpuUsage
private startedAt:number // microseconds timestamp
private samples:CpuUsageAt[] = []
constructor(samplingPeriod = SAMPLING_PERIOD) {
// Initial state
const start = getMicrosecondsTime()
this.startedAt = start
this.cumulatedUsage = process.cpuUsage()
this.samples.push({ usage: 0, at: start, elapsed: 1 })
// Periodic sample
setInterval(() => {
const newSampleAt = getMicrosecondsTime()
const newUsage:CpuUsage = process.cpuUsage()
const elapsed = newSampleAt - this.lastSampleAt
const userDiff = newUsage.user - this.cumulatedUsage.user
const usagePercent = userDiff / elapsed // The percent of time consumed by the process since last sample
this.samples.push({ usage: usagePercent, at: newSampleAt, elapsed })
while(this.samplesDistance > MAX_SAMPLES_DISTANCE) {
this.samples.shift()
}
this.cumulatedUsage = newUsage
// console.log('Time elapsed: %s microseconds, = %s %CPU', elapsed, (usagePercent*100).toFixed(2))
}, samplingPeriod)
}
private get lastSampleAt() {
return this.samples[this.samples.length - 1].at
}
private get samplesDistance() {
return this.samples[this.samples.length - 1].at - this.samples[0].at
}
cpuUsageOverLastMilliseconds(elapsedMilliseconds:number) {
return this.cpuUsageOverLastX(elapsedMilliseconds * 1000)
}
private cpuUsageOverLastX(nbMicrosecondsElapsed:number) {
return this.getSamplesResult(getMicrosecondsTime() - nbMicrosecondsElapsed)
}
private getSamplesResult(minTimestamp:number) {
const matchingSamples = this.samples.filter(s => s.at >= minTimestamp - SAMPLING_PERIOD * 1000)
const cumulativeElapsed = matchingSamples.reduce((sum, s) => sum + s.elapsed, 0)
return matchingSamples.reduce((cumulated, percent) => {
const weight = percent.elapsed / cumulativeElapsed
return cumulated + percent.usage * weight
}, 0)
}
}
\ No newline at end of file
...@@ -44,9 +44,6 @@ export class WorkerFarm { ...@@ -44,9 +44,6 @@ export class WorkerFarm {
}) })
} }
get nbWorkers() {
return this.theEngine.getNbWorkers()
}
changeCPU(cpu:any) { changeCPU(cpu:any) {
return this.theEngine.setConf({ cpu }) return this.theEngine.setConf({ cpu })
...@@ -178,6 +175,7 @@ export class BlockProver { ...@@ -178,6 +175,7 @@ export class BlockProver {
const start = Date.now(); const start = Date.now();
let result = await powFarm.askNewProof({ let result = await powFarm.askNewProof({
newPoW: { newPoW: {
turnDuration: os.arch().match(/arm/) ? CommonConstants.POW_TURN_DURATION_ARM : CommonConstants.POW_TURN_DURATION_PC,
conf: { conf: {
cpu: this.conf.cpu, cpu: this.conf.cpu,
prefix: this.conf.prefix, prefix: this.conf.prefix,
...@@ -196,10 +194,10 @@ export class BlockProver { ...@@ -196,10 +194,10 @@ export class BlockProver {
throw 'Proof-of-work computation canceled because block received'; throw 'Proof-of-work computation canceled because block received';
} else { } else {
const proof = result.block; const proof = result.block;
const testsCount = result.testsCount * powFarm.nbWorkers const testsCount = result.testsCount;
const duration = (Date.now() - start); const duration = (Date.now() - start);
const testsPerSecond = testsCount / (duration / 1000) const testsPerSecond = (testsCount / (duration / 1000)).toFixed(2);
this.logger.info('Done: #%s, %s in %ss (~%s tests, ~%s tests/s, using %s cores, CPU %s%)', block.number, proof.hash, (duration / 1000).toFixed(2), testsCount, testsPerSecond.toFixed(2), powFarm.nbWorkers, Math.floor(100*this.conf.cpu)) this.logger.info('Done: #%s, %s in %ss (%s tests, ~%s tests/s)', block.number, proof.hash, (duration / 1000).toFixed(2), testsCount, testsPerSecond);
this.logger.info('FOUND proof-of-work with %s leading zeros followed by [0-' + highMark + ']!', nbZeros); this.logger.info('FOUND proof-of-work with %s leading zeros followed by [0-' + highMark + ']!', nbZeros);
return BlockDTO.fromJSONObject(proof) return BlockDTO.fromJSONObject(proof)
} }
......
...@@ -25,10 +25,6 @@ export class PowEngine { ...@@ -25,10 +25,6 @@ export class PowEngine {
this.id = this.cluster.clusterId this.id = this.cluster.clusterId
} }
getNbWorkers() {
return this.cluster.nbWorkers
}
forceInit() { forceInit() {
return this.cluster.initCluster() return this.cluster.initCluster()
} }
...@@ -39,6 +35,9 @@ export class PowEngine { ...@@ -39,6 +35,9 @@ export class PowEngine {
await this.cluster.cancelWork() await this.cluster.cancelWork()
} }
if (os.arch().match(/arm/)) {
stuff.newPoW.conf.cpu /= 2; // Don't know exactly why is ARM so much saturated by PoW, so let's divide by 2
}
return await this.cluster.proveByWorkers(stuff) return await this.cluster.proveByWorkers(stuff)
} }
...@@ -47,6 +46,9 @@ export class PowEngine { ...@@ -47,6 +46,9 @@ export class PowEngine {
} }
setConf(value:any) { setConf(value:any) {
if (os.arch().match(/arm/) && value.cpu !== undefined) {
value.cpu /= 2; // Don't know exactly why is ARM so much saturated by PoW, so let's divide by 2
}
return this.cluster.changeConf(value) return this.cluster.changeConf(value)
} }
......
...@@ -195,6 +195,7 @@ export class Master { ...@@ -195,6 +195,7 @@ export class Master {
highMark: stuff.newPoW.highMark, highMark: stuff.newPoW.highMark,
pair: _.clone(stuff.newPoW.pair), pair: _.clone(stuff.newPoW.pair),
forcedTime: stuff.newPoW.forcedTime, forcedTime: stuff.newPoW.forcedTime,
turnDuration: stuff.newPoW.turnDuration,
conf: { conf: {
medianTimeBlocks: stuff.newPoW.conf.medianTimeBlocks, medianTimeBlocks: stuff.newPoW.conf.medianTimeBlocks,
avgGenTime: stuff.newPoW.conf.avgGenTime, avgGenTime: stuff.newPoW.conf.avgGenTime,
......
...@@ -6,11 +6,15 @@ import {Constants} from "./constants" ...@@ -6,11 +6,15 @@ import {Constants} from "./constants"
import {KeyGen} from "../../../lib/common-libs/crypto/keyring" import {KeyGen} from "../../../lib/common-libs/crypto/keyring"
import {dos2unix} from "../../../lib/common-libs/dos2unix" import {dos2unix} from "../../../lib/common-libs/dos2unix"
import {rawer} from "../../../lib/common-libs/index" import {rawer} from "../../../lib/common-libs/index"
import {ProcessCpuProfiler} from "../../../ProcessCpuProfiler"
const moment = require('moment'); const moment = require('moment');
const querablep = require('querablep'); const querablep = require('querablep');
const PAUSES_PER_TURN = 5;
// This value can be changed
let TURN_DURATION_IN_MILLISEC = 100;
let computing = querablep(Promise.resolve(null)); let computing = querablep(Promise.resolve(null));
let askedStop = false; let askedStop = false;
...@@ -86,6 +90,7 @@ function beginNewProofOfWork(stuff:any) { ...@@ -86,6 +90,7 @@ function beginNewProofOfWork(stuff:any) {
prefix *= 100 * Constants.NONCE_RANGE prefix *= 100 * Constants.NONCE_RANGE
} }
const highMark = stuff.highMark; const highMark = stuff.highMark;
const turnDuration = stuff.turnDuration || TURN_DURATION_IN_MILLISEC
let sigFunc = null; let sigFunc = null;
if (signatureFunc && lastSecret === pair.sec) { if (signatureFunc && lastSecret === pair.sec) {
sigFunc = signatureFunc; sigFunc = signatureFunc;
...@@ -103,17 +108,13 @@ function beginNewProofOfWork(stuff:any) { ...@@ -103,17 +108,13 @@ function beginNewProofOfWork(stuff:any) {
let testsCount = 0; let testsCount = 0;
let found = false; let found = false;
let score = 0;
let turn = 0; let turn = 0;
const profiler = new ProcessCpuProfiler(100)
let cpuUsage = profiler.cpuUsageOverLastMilliseconds(1)
// We limit the number of tests according to CPU usage
let testsPerRound = 1
let turnDuration = 20 // We initially goes quickly to the max speed = 50 reevaluations per second (1000 / 20)
while (!found && !askedStop) { while (!found && !askedStop) {
/***************** /*****************
* A TURN ~ 100ms * A TURN
****************/ ****************/
await Promise.race([ await Promise.race([
...@@ -124,9 +125,26 @@ function beginNewProofOfWork(stuff:any) { ...@@ -124,9 +125,26 @@ function beginNewProofOfWork(stuff:any) {
// II. Process the turn's PoW // II. Process the turn's PoW
(async () => { (async () => {
/*****************
* A TURN OF POW ~= 100ms by default
* --------------------
*
* The concept of "turn" is required to limit the CPU usage.
* We need a time reference to have the speed = nb tests / period of time.
* Here we have:
*
* - speed = testsCount / turn
*
* We have taken 1 turn = 100ms to control the CPU usage after 100ms of PoW. This means that during the
* very first 100ms of the PoW, CPU usage = 100%. Then it becomes controlled to the %CPU set.
****************/
// Prove // Prove
let i = 0; let i = 0;
const thisTurn = turn; const thisTurn = turn;
const pausePeriod = score ? score / PAUSES_PER_TURN : 10; // number of pauses per turn
// We limit the number of tests according to CPU usage
const testsPerRound = score ? Math.floor(score * currentCPU) : 1000 * 1000 * 1000
// Time is updated regularly during the proof // Time is updated regularly during the proof
block.time = getBlockTime(block, conf, forcedTime) block.time = getBlockTime(block, conf, forcedTime)
...@@ -178,7 +196,7 @@ function beginNewProofOfWork(stuff:any) { ...@@ -178,7 +196,7 @@ function beginNewProofOfWork(stuff:any) {
if (!found && !askedStop) { if (!found && !askedStop) {
i++; i++;
testsCount++; testsCount++;
if (i % testsPerRound === 0) { if (i % pausePeriod === 0) {
await countDown(0); // Very low pause, just the time to process eventual end of the turn await countDown(0); // Very low pause, just the time to process eventual end of the turn
} }
} }
...@@ -190,24 +208,12 @@ function beginNewProofOfWork(stuff:any) { ...@@ -190,24 +208,12 @@ function beginNewProofOfWork(stuff:any) {
if (!found) { if (!found) {
// CPU speed recording // CPU speed recording
if (turn > 0) { if (turn > 0 && !score) {
const oldTestsPerRound = testsPerRound score = testsCount;
cpuUsage = profiler.cpuUsageOverLastMilliseconds(turnDuration)
if (cpuUsage > currentCPU + 0.005 || cpuUsage < currentCPU - 0.005) {
let powVariationFactor
// powVariationFactor = currentCPU / (cpuUsage || 0.01) / 5 // divide by 2 to avoid extreme responses
if (currentCPU > cpuUsage) {
powVariationFactor = 1.01
testsPerRound = Math.max(1, Math.ceil(testsPerRound * powVariationFactor))
} else {
powVariationFactor = 0.99
testsPerRound = Math.max(1, Math.floor(testsPerRound * powVariationFactor))
}
}
} }
/***************** /*****************
* UNLOAD CPU CHARGE FOR THIS TURN * UNLOAD CPU CHARGE
****************/ ****************/
// We wait for a maximum time of `turnDuration`. // We wait for a maximum time of `turnDuration`.
// This will trigger the end of the turn by the concurrent race I. During that time, the proof.js script // This will trigger the end of the turn by the concurrent race I. During that time, the proof.js script
...@@ -220,9 +226,6 @@ function beginNewProofOfWork(stuff:any) { ...@@ -220,9 +226,6 @@ function beginNewProofOfWork(stuff:any) {
// Next turn // Next turn
turn++ turn++
turnDuration += 1
turnDuration = Math.min(turnDuration, 1000) // Max 1 second per turn
} }
/***************** /*****************
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment