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):