From 373157db45c69a3b904709be449d8b5724d074b8 Mon Sep 17 00:00:00 2001 From: Biryuzovye Kleshni Date: Sun, 29 Jul 2018 04:38:05 +0000 Subject: [PATCH] Added POW settings to settings window --- src/bitmessageqt/__init__.py | 81 ++++++++++--- src/bitmessageqt/settings.ui | 226 +++++++++++++++++++++++++++-------- src/singleworker.py | 128 +++++++++++++------- src/workprover/__init__.py | 49 ++++---- src/workprover/fastsolver.py | 2 + src/workprover/gpusolver.py | 27 +++-- src/workprover/test.py | 2 +- 7 files changed, 376 insertions(+), 139 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 7f8a367c..250ed47f 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -55,6 +55,7 @@ import sound import re import bitmessage_icons_rc # Loads icon resources import workprover.utils +import singleworker try: @@ -2616,9 +2617,34 @@ class MyForm(settingsmixin.SMainWindow): BMConfigParser().set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(int(float( self.settingsDialogInstance.ui.lineEditSmallMessageDifficulty.text()) * defaults.networkDefaultPayloadLengthExtraBytes))) - if self.settingsDialogInstance.ui.comboBoxOpenCL.currentText().toUtf8() != BMConfigParser().safeGet("bitmessagesettings", "opencl"): - BMConfigParser().set('bitmessagesettings', 'opencl', str(self.settingsDialogInstance.ui.comboBoxOpenCL.currentText())) - queues.workerQueue.put(('resetPoW', '')) + if self.settingsDialogInstance.ui.radioButtonDumbSolver.isChecked(): + BMConfigParser().set("bitmessagesettings", "powsolver", "dumb") + elif self.settingsDialogInstance.ui.radioButtonForkingSolver.isChecked(): + BMConfigParser().set("bitmessagesettings", "powsolver", "forking") + + BMConfigParser().set( + "bitmessagesettings", + "processes", + str(self.settingsDialogInstance.ui.spinBoxForkingSolverParallelism.value()) + ) + elif self.settingsDialogInstance.ui.radioButtonFastSolver.isChecked(): + BMConfigParser().set("bitmessagesettings", "powsolver", "fast") + + BMConfigParser().set( + "bitmessagesettings", + "threads", + str(self.settingsDialogInstance.ui.spinBoxFastSolverParallelism.value()) + ) + elif self.settingsDialogInstance.ui.radioButtonGPUSolver.isChecked(): + BMConfigParser().set("bitmessagesettings", "powsolver", "gpu") + + BMConfigParser().set( + "bitmessagesettings", + "opencl", + str(self.settingsDialogInstance.ui.comboBoxGPUVendor.currentText().toUtf8()) + ) + + singleworker.setBestSolver() acceptableDifficultyChanged = False @@ -4547,18 +4573,43 @@ class settingsDialog(QtGui.QDialog): self.ui.lineEditMaxAcceptableSmallMessageDifficulty.setText(str((float(BMConfigParser().getint( 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes')) / defaults.networkDefaultPayloadLengthExtraBytes))) - # OpenCL - if openclpow.openclAvailable(): - self.ui.comboBoxOpenCL.setEnabled(True) - else: - self.ui.comboBoxOpenCL.setEnabled(False) - self.ui.comboBoxOpenCL.clear() - self.ui.comboBoxOpenCL.addItem("None") - self.ui.comboBoxOpenCL.addItems(openclpow.vendors) - self.ui.comboBoxOpenCL.setCurrentIndex(0) - for i in range(self.ui.comboBoxOpenCL.count()): - if self.ui.comboBoxOpenCL.itemText(i) == BMConfigParser().safeGet('bitmessagesettings', 'opencl'): - self.ui.comboBoxOpenCL.setCurrentIndex(i) + if "forking" not in singleworker.workProver.availableSolvers: + self.ui.radioButtonForkingSolver.setEnabled(False) + if "fast" not in singleworker.workProver.availableSolvers: + self.ui.radioButtonFastSolver.setEnabled(False) + if "gpu" not in singleworker.workProver.availableSolvers: + self.ui.radioButtonGPUSolver.setEnabled(False) + + solverName = BMConfigParser().safeGet("bitmessagesettings", "powsolver", "gpu") + forkingSolverParallelism = BMConfigParser().safeGetInt("bitmessagesettings", "processes") + fastSolverParallelism = BMConfigParser().safeGetInt("bitmessagesettings", "threads") + GPUVendor = BMConfigParser().safeGet("bitmessagesettings", "opencl") + + if solverName == "dumb": + self.ui.radioButtonDumbSolver.setChecked(True) + elif solverName == "forking": + self.ui.radioButtonForkingSolver.setChecked(True) + elif solverName == "fast": + self.ui.radioButtonFastSolver.setChecked(True) + elif solverName == "gpu": + self.ui.radioButtonGPUSolver.setChecked(True) + + self.ui.spinBoxForkingSolverParallelism.setValue(forkingSolverParallelism) + self.ui.spinBoxFastSolverParallelism.setValue(fastSolverParallelism) + + vendors = set(singleworker.workProver.availableSolvers["gpu"].vendors) + + if GPUVendor is not None: + vendors.add(GPUVendor) + + self.ui.comboBoxGPUVendor.clear() + self.ui.comboBoxGPUVendor.addItems(list(vendors)) + self.ui.comboBoxGPUVendor.setCurrentIndex(0) + + for i in range(self.ui.comboBoxGPUVendor.count()): + if self.ui.comboBoxGPUVendor.itemText(i) == GPUVendor: + self.ui.comboBoxGPUVendor.setCurrentIndex(i) + break # Namecoin integration tab diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui index 996e98d7..0596dd2c 100644 --- a/src/bitmessageqt/settings.ui +++ b/src/bitmessageqt/settings.ui @@ -6,7 +6,7 @@ 0 0 - 555 + 616 592 @@ -14,16 +14,6 @@ Settings - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - @@ -549,13 +539,13 @@ Max acceptable difficulty - - + + - Here you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable. + Maximum acceptable small message difficulty: - - true + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -585,6 +575,32 @@ + + + + Here you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable. + + + true + + + + + + + + 0 + 0 + + + + + 70 + 16777215 + + + + @@ -614,33 +630,142 @@ - - - - Maximum acceptable small message difficulty: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + Proof of work solver + + + + + There are several worker modules to solve POW: + + + + + + + + + Forking solver using multiple processes: + + + + + + + 1 + + + 4096 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Fast solver in C with multiple threads: + + + + + + + 1 + + + 4096 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Dumb solver + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + GPU solver: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + - - - - - 0 - 0 - - - - - 70 - 16777215 - - - - - + Qt::Vertical @@ -653,16 +778,6 @@ - - - - Hardware GPU acceleration (OpenCL): - - - - - - @@ -981,6 +1096,16 @@ + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + @@ -998,6 +1123,7 @@ checkBoxSocksListen buttonBox + buttonBox diff --git a/src/singleworker.py b/src/singleworker.py index d2ce6436..008bafa6 100644 --- a/src/singleworker.py +++ b/src/singleworker.py @@ -221,6 +221,66 @@ def disseminateObject(nonce, expiryTime, headlessPayload, objectType, stream, ta return inventoryHash, payload +workProver = workprover.WorkProver( + os.path.join(paths.codePath(), "workprover"), + helper_random.randomBytes(32), + lambda status: queues.UISignalQueue.put(("updateWorkProverStatus", status)), + queues.workerQueue +) + +debug.logger.info("Availabe solvers: %s", str(workProver.availableSolvers.keys())) + +if "fast" not in workProver.availableSolvers: + queues.UISignalQueue.put(("updateStatusBar", ( + tr._translate( + "proofofwork", + "C PoW module unavailable. Please build it." + ), 1 + ))) + +def setBestSolver(): + solverName = bmconfigparser.BMConfigParser().safeGet("bitmessagesettings", "powsolver", "gpu") + forkingSolverParallelism = bmconfigparser.BMConfigParser().safeGetInt("bitmessagesettings", "processes") + fastSolverParallelism = bmconfigparser.BMConfigParser().safeGetInt("bitmessagesettings", "threads") + GPUVendor = bmconfigparser.BMConfigParser().safeGet("bitmessagesettings", "opencl") + + if forkingSolverParallelism < 1: + forkingSolverParallelism = workProver.defaultParallelism + + if fastSolverParallelism < 1: + fastSolverParallelism = workProver.defaultParallelism + + maxcores = bmconfigparser.BMConfigParser().safeGetInt("bitmessagesettings", "maxcores", None) + + if maxcores is not None: + forkingSolverParallelism = min(maxcores, forkingSolverParallelism) + fastSolverParallelism = min(maxcores, fastSolverParallelism) + + if solverName == "gpu" and GPUVendor is None: + solverName = "fast" + + while solverName not in workProver.availableSolvers: + if solverName == "gpu": + solverName = "fast" + elif solverName == "fast": + solverName = "forking" + elif solverName == "forking": + solverName = "dumb" + + bmconfigparser.BMConfigParser().set("bitmessagesettings", "powsolver", solverName) + bmconfigparser.BMConfigParser().set("bitmessagesettings", "processes", str(forkingSolverParallelism)) + bmconfigparser.BMConfigParser().set("bitmessagesettings", "threads", str(fastSolverParallelism)) + bmconfigparser.BMConfigParser().save() + + if solverName in ["dumb", "gpu"]: + workProver.commandsQueue.put(("setSolver", solverName, None)) + elif solverName == "forking": + workProver.commandsQueue.put(("setSolver", "forking", forkingSolverParallelism)) + elif solverName == "fast": + workProver.commandsQueue.put(("setSolver", "fast", fastSolverParallelism)) + +setBestSolver() + class singleWorker(threading.Thread, helper_threading.StoppableThread): name = "singleWorker" @@ -235,41 +295,7 @@ class singleWorker(threading.Thread, helper_threading.StoppableThread): super(self.__class__, self).stopThread() def run(self): - GPUVendor = bmconfigparser.BMConfigParser().safeGet("bitmessagesettings", "opencl") - - self.workProver = workprover.WorkProver( - os.path.join(paths.codePath(), "workprover"), - GPUVendor, - helper_random.randomBytes(32), - lambda status: queues.UISignalQueue.put(("updateWorkProverStatus", status)), - queues.workerQueue - ) - - self.workProver.start() - - parallelism = bmconfigparser.BMConfigParser().safeGetInt("bitmessagesettings", "maxcores") - - if parallelism < 1: - parallelism = self.workProver.defaultParallelism - - debug.logger.info("Availabe solvers: %s", str(self.workProver.availableSolvers.keys())) - - if "gpu" in self.workProver.availableSolvers and GPUVendor is not None: - self.workProver.commandsQueue.put(("setSolver", "gpu", None)) - elif "fast" in self.workProver.availableSolvers: - self.workProver.commandsQueue.put(("setSolver", "fast", parallelism)) - elif "forking" in self.workProver.availableSolvers: - self.workProver.commandsQueue.put(("setSolver", "forking", parallelism)) - else: - self.workProver.commandsQueue.put(("setSolver", "dumb", None)) - - if "fast" not in self.workProver.availableSolvers: - queues.UISignalQueue.put(("updateStatusBar", ( - tr._translate( - "proofofwork", - "C PoW module unavailable. Please build it." - ), 1 - ))) + workProver.start() self.startedWorks = {} @@ -299,16 +325,34 @@ class singleWorker(threading.Thread, helper_threading.StoppableThread): self.requestPubkey(*arguments) elif command == "resetPoW": pass + elif command == "GPUError": + self.handleGPUError(*arguments) elif command == "taskDone": self.workDone(*arguments) elif command == "stopThread": - self.workProver.commandsQueue.put(("shutdown", )) - self.workProver.join() + workProver.commandsQueue.put(("shutdown", )) + workProver.join() break debug.logger.info("Quitting...") + def handleGPUError(self): + bmconfigparser.BMConfigParser().set("bitmessagesettings", "powsolver", "dumb") + + workProver.commandsQueue.put(("setSolver", "dumb", None)) + + debug.logger.error( + "Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers" + ) + + queues.UISignalQueue.put(("updateStatusBar", ( + tr._translate( + "MainWindow", + "Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers." + ), 1 + ))) + def startWork(self, ID, headlessPayload, TTL, expiryTime, byteDifficulty, lengthExtension, logPrefix, callback): debug.logger.info( "%s Starting work %s, payload length = %s, TTL = %s", @@ -317,7 +361,7 @@ class singleWorker(threading.Thread, helper_threading.StoppableThread): self.startedWorks[ID] = callback - self.workProver.commandsQueue.put(( + workProver.commandsQueue.put(( "addTask", ID, headlessPayload, TTL, expiryTime, byteDifficulty, lengthExtension )) @@ -588,7 +632,7 @@ class singleWorker(threading.Thread, helper_threading.StoppableThread): if ID in self.startedWorks: del self.startedWorks[ID] - self.workProver.commandsQueue.put(("cancelTask", ID)) + workProver.commandsQueue.put(("cancelTask", ID)) helper_sql.sqlExecute(""" UPDATE "sent" SET "status" = 'broadcastcanceled' @@ -906,14 +950,14 @@ class singleWorker(threading.Thread, helper_threading.StoppableThread): if ID in self.startedWorks: del self.startedWorks[ID] - self.workProver.commandsQueue.put(("cancelTask", ID)) + workProver.commandsQueue.put(("cancelTask", ID)) ID = "message", ackData if ID in self.startedWorks: del self.startedWorks[ID] - self.workProver.commandsQueue.put(("cancelTask", ID)) + workProver.commandsQueue.put(("cancelTask", ID)) state.watchedAckData -= {ackData} @@ -933,7 +977,7 @@ class singleWorker(threading.Thread, helper_threading.StoppableThread): if ID in self.startedWorks: del self.startedWorks[ID] - self.workProver.commandsQueue.put(("cancelTask", ID)) + workProver.commandsQueue.put(("cancelTask", ID)) status, version, stream, ripe = addresses.decodeAddress(destination) diff --git a/src/workprover/__init__.py b/src/workprover/__init__.py index 4ea0a74c..f7fc6b20 100644 --- a/src/workprover/__init__.py +++ b/src/workprover/__init__.py @@ -30,10 +30,12 @@ class Task(object): Status = collections.namedtuple("Status", ["solverName", "solverStatus", "speed", "tasksCount", "difficulty"]) +# Only one instance allowed + class WorkProver(threading.Thread): # Seed must be 32 bytes - def __init__(self, codePath, GPUVendor, seed, statusUpdated, resultsQueue): + def __init__(self, codePath, seed, statusUpdated, resultsQueue): super(self.__class__, self).__init__() self.availableSolvers = { @@ -56,7 +58,7 @@ class WorkProver(threading.Thread): pass try: - self.availableSolvers["gpu"] = gpusolver.GPUSolver(codePath, GPUVendor) + self.availableSolvers["gpu"] = gpusolver.GPUSolver(codePath) except gpusolver.GPUSolverError: pass @@ -99,23 +101,29 @@ class WorkProver(threading.Thread): self.statusUpdated(Status(self.solverName, status, self.speed, len(self.tasks), self.totalDifficulty)) def setSolver(self, name, configuration): - if name is None and self.solverName is None: - pass - elif name == self.solverName: - self.solver.setConfiguration(configuration) - else: - if self.solver is not None: - self.solver.setConfiguration(None) - self.solverName = None - self.solver = None - - if name is not None: - if name not in self.availableSolvers: - name, configuration = "dumb", None - - self.solverName = name - self.solver = self.availableSolvers[name] + try: + if name is None and self.solverName is None: + pass + elif name == self.solverName: self.solver.setConfiguration(configuration) + else: + if self.solver is not None: + self.solver.setConfiguration(None) + self.solverName = None + self.solver = None + + if name is not None: + if name not in self.availableSolvers: + name, configuration = "dumb", None + + self.solverName = name + self.solver = self.availableSolvers[name] + self.solver.setConfiguration(configuration) + except GPUSolverError: + self.solverName = None + self.solver = None + + self.resultsQueue.put(("GPUError", )) self.notifyStatus() @@ -229,8 +237,9 @@ class WorkProver(threading.Thread): try: nonce, iterationsCount = self.solver.search(initialHash, task.target, appendedSeed, timeout) except gpusolver.GPUSolverError: - self.setSolver("dumb", 1) - self.availableSolvers.pop("gpu") + self.setSolver(None, None) + + self.resultsQueue.put(("GPUError", )) nonce, iterationsCount = None, 0 diff --git a/src/workprover/fastsolver.py b/src/workprover/fastsolver.py index e328094d..4fe26fc7 100644 --- a/src/workprover/fastsolver.py +++ b/src/workprover/fastsolver.py @@ -37,6 +37,8 @@ def loadFastSolver(codePath): except: raise FastSolverError() +# Only one instance allowed + class FastSolver(object): def __init__(self, codePath): self.libfastsolver = loadFastSolver(codePath) diff --git a/src/workprover/gpusolver.py b/src/workprover/gpusolver.py index b1ed9c56..7b5d09fd 100644 --- a/src/workprover/gpusolver.py +++ b/src/workprover/gpusolver.py @@ -11,7 +11,7 @@ class GPUSolverError(Exception): pass class GPUSolver(object): - def __init__(self, codePath, vendor = None): + def __init__(self, codePath): global pyopencl try: @@ -19,17 +19,15 @@ class GPUSolver(object): except ImportError: raise GPUSolverError() - for i in pyopencl.get_platforms(): - if vendor is not None and i.vendor != vendor: - continue + self.vendors = {} + for i in pyopencl.get_platforms(): devices = i.get_devices(device_type = pyopencl.device_type.GPU) if len(devices) != 0: - self.device = devices[0] + self.vendors[i.vendor] = devices[0] - break - else: + if len(self.vendors) == 0: raise GPUSolverError() with open(os.path.join(codePath, "gpusolver.cl")) as file: @@ -88,14 +86,21 @@ class GPUSolver(object): import numpy - context = pyopencl.Context(devices = [self.device]) + if configuration is None: + configuration = self.vendors.keys()[0] - computeUnitsCount = self.device.get_info(pyopencl.device_info.MAX_COMPUTE_UNITS) - workGroupSize = self.device.get_info(pyopencl.device_info.MAX_WORK_GROUP_SIZE) + if configuration not in self.vendors: + raise GPUSolverError() + + device = self.vendors[configuration] + context = pyopencl.Context(devices = [device]) + + computeUnitsCount = device.get_info(pyopencl.device_info.MAX_COMPUTE_UNITS) + workGroupSize = device.get_info(pyopencl.device_info.MAX_WORK_GROUP_SIZE) self.batchSize = workGroupSize * computeUnitsCount * 256 - self.queue = pyopencl.CommandQueue(context, self.device) + self.queue = pyopencl.CommandQueue(context, device) program = pyopencl.Program(context, self.source).build() diff --git a/src/workprover/test.py b/src/workprover/test.py index e2cba67e..dc0cf0a1 100755 --- a/src/workprover/test.py +++ b/src/workprover/test.py @@ -122,7 +122,7 @@ class TestGPUSolver(TestSolver): class TestWorkProver(unittest.TestCase): def setUp(self): - self.thread = __init__.WorkProver(codePath, None, seed, None, None) + self.thread = __init__.WorkProver(codePath, seed, None, None) self.thread.start() def checkTaskLinks(self):