Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ Synchronization uses `AtomicCounter<uint64_t>` to signal job/memory/constant upd
| `sources/device/` | `DeviceManager` singleton + `Device` base + `DeviceNvidia`/`DeviceAmd` |
| `sources/resolver/amd/` | OpenCL resolvers — one per algorithm |
| `sources/resolver/nvidia/` | CUDA resolvers — one per algorithm |
| `sources/resolver/cpu/` | OpenMP CPU resolver |
| `sources/resolver/cpu/` | CPU resolver (std::thread pool, `--cpu_threads`/`--cpu_affinity`) |
| `sources/profiler/` | NVML (NVIDIA) / ADL (AMD) GPU monitoring |
| `sources/statistical/` | Hashrate and share counters |
| `sources/api/` | HTTP REST API (default port 8080) backed by Boost.ASIO |
Expand Down
41 changes: 29 additions & 12 deletions docker/Dockerfile.linux
Original file line number Diff line number Diff line change
@@ -1,34 +1,41 @@
# syntax=docker/dockerfile:1
#
# Build LuminousMiner natively for Linux (ELF) in a container. One image for AMD
# (OpenCL), NVIDIA (CUDA), BOTH, or CPU-only. Select with
# --build-arg GPU=amd|nvidia|both|cpu (default both). No GPU needed to build.
# Build LuminousMiner natively for Linux (ELF) in a container. GPU backend and the
# CPU resolver are independent axes:
# --build-arg GPU=amd|nvidia|both|none (default both) -> BUILD_AMD / BUILD_NVIDIA
# --build-arg CPU=ON|OFF (default OFF) -> BUILD_CPU
# Any combination is valid except GPU=none CPU=OFF (nothing to build). No GPU is
# needed to build.
#
# GPU=amd -> lean ubuntu:24.04 base
# GPU=nvidia -> nvidia/cuda devel base (nvcc), CUDA only
# GPU=both -> nvidia/cuda devel base, AMD + CUDA in one binary
# GPU=cpu -> lean ubuntu:24.04 base, CPU-only (used for arm64 via TARGETARCH)
# GPU=none -> lean ubuntu:24.04 base, no GPU backend
# CPU=ON -> also compile the CPU resolver into the same binary
# (multicore via an in-process std::thread pool; no OpenMP runtime)
#
# Build arch follows buildx TARGETARCH (amd64 default; arm64 for Apple Silicon).
#
# Usage:
# docker build -f docker/Dockerfile.linux --build-arg GPU=both \
# --target artifact -o dist/linux-both .
# docker buildx build --platform linux/arm64 --build-arg GPU=cpu \
# docker buildx build --platform linux/arm64 --build-arg GPU=none --build-arg CPU=ON \
# --target runtime -t lm:linux-arm64 --load .

# GPU must be declared in global scope (before the first FROM) so it can be used
# in the stage-selector FROM below.
# GPU and CPU must be declared in global scope (before the first FROM) so they can
# be used in the stage-selector FROMs below.
ARG GPU=both
ARG CPU=OFF
ARG BASE_AMD=ubuntu:24.04
ARG BASE_CUDA=nvidia/cuda:13.1.2-devel-ubuntu24.04

FROM ${BASE_CUDA} AS base-both
FROM ${BASE_CUDA} AS base-nvidia
FROM ${BASE_AMD} AS base-amd
FROM ${BASE_AMD} AS base-cpu
FROM ${BASE_AMD} AS base-none
FROM base-${GPU} AS build
ARG GPU
ARG CPU
ARG DEBIAN_FRONTEND=noninteractive
# Pin the vcpkg registry to the same commit as vcpkg.json's builtin-baseline so
# dependency versions are reproducible. Override at build time for a newer set.
Expand Down Expand Up @@ -60,6 +67,11 @@ RUN set -eux; \
| tar xz -C /opt; \
ln -sf /opt/cmake-${CMAKE_VERSION}-linux-${CM_ARCH}/bin/* /usr/local/bin/

# Use the system cmake/ninja (installed above) instead of letting vcpkg fetch its
# own prebuilt tools. vcpkg auto-enables this on arm64; on amd64 it otherwise
# downloads an x86_64 cmake whose extraction fails under Rosetta emulation. Forcing
# system binaries keeps the amd64 build working when built on an Apple Silicon host.
ENV VCPKG_FORCE_SYSTEM_BINARIES=1
ENV VCPKG_ROOT=/opt/vcpkg
RUN git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT" \
&& git -C "$VCPKG_ROOT" checkout "$VCPKG_REF" \
Expand All @@ -78,17 +90,22 @@ RUN --mount=type=cache,target=/root/.cache/vcpkg \
amd) AMD=ON; NV=OFF; FEAT="opencl;openssl"; PRESET=linux ;; \
nvidia) AMD=OFF; NV=ON; FEAT="openssl"; PRESET=linux ;; \
both) AMD=ON; NV=ON; FEAT="opencl;openssl"; PRESET=linux ;; \
cpu) AMD=OFF; NV=OFF; FEAT="openssl"; PRESET=linux-cpu ;; \
*) echo "GPU must be amd|nvidia|both|cpu" >&2; exit 2 ;; \
none) AMD=OFF; NV=OFF; FEAT="openssl"; PRESET=linux-cpu ;; \
*) echo "GPU must be amd|nvidia|both|none" >&2; exit 2 ;; \
esac; \
if [ "$AMD" = "OFF" ] && [ "$NV" = "OFF" ] && [ "$CPU" != "ON" ]; then \
echo "nothing to build: set CPU=ON and/or GPU=amd|nvidia|both" >&2; exit 2; \
fi; \
cmake --preset $PRESET \
-DBUILD_AMD=$AMD -DBUILD_NVIDIA=$NV \
-DBUILD_AMD=$AMD -DBUILD_NVIDIA=$NV -DBUILD_CPU=$CPU \
-DVCPKG_MANIFEST_FEATURES="$FEAT"; \
cmake --build --preset $PRESET

# Runnable image (native on the target arch). Used for linux-arm64 `docker run`.
# CMAKE_RUNTIME_OUTPUT_DIRECTORY is the repo-root bin/ (i.e. /src/bin) for every
# preset, so the binary + kernels land there regardless of GPU/cpu mode.
# preset, so the binary + kernels land there regardless of GPU/CPU mode. The CPU
# resolver parallelizes via an in-process std::thread pool, so there is no OpenMP
# runtime to stage.
FROM ubuntu:24.04 AS runtime
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
Expand Down
26 changes: 19 additions & 7 deletions docker/Dockerfile.windows-cross
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# syntax=docker/dockerfile:1
#
# Cross-compile LuminousMiner for WINDOWS from a Linux container, MSVC ABI
# (clang-cl + xwin). One toolchain for AMD (OpenCL), NVIDIA (CUDA), or BOTH in a
# single miner.exe. Select with --build-arg GPU=amd|nvidia|both (default both).
# (clang-cl + xwin). GPU backend and the CPU resolver are independent axes:
# --build-arg GPU=amd|nvidia|both|none (default both) -> BUILD_AMD / BUILD_NVIDIA
# --build-arg CPU=ON|OFF (default OFF) -> BUILD_CPU
# GPU=none CPU=OFF is rejected (nothing to build).
#
# GPU=amd -> lean ubuntu:24.04 base, no CUDA layers
# GPU=nvidia -> nvidia/cuda devel base, CUDA only
# GPU=both -> nvidia/cuda devel base, AMD + CUDA in one binary
# GPU=none -> lean ubuntu:24.04 base, no GPU backend
# CPU=ON -> also compile the CPU resolver (multicore via an in-process
# std::thread pool; no OpenMP runtime needed).
#
# AMD .cl kernels compile at runtime; NVIDIA .cu compile at build time via
# clang-CUDA (CMake can't drive clang+CUDA on Windows, issue #20776 -> see
Expand All @@ -21,16 +26,19 @@
# GPU must be declared in global scope (before the first FROM) so it can be used
# in the stage-selector FROM below.
ARG GPU=both
ARG CPU=OFF
ARG BASE_AMD=ubuntu:24.04
ARG BASE_CUDA=nvidia/cuda:13.1.2-devel-ubuntu24.04

# Map GPU -> concrete base image. amd is lean ubuntu; nvidia/both use the CUDA
# Map GPU -> concrete base image. amd/none are lean ubuntu; nvidia/both use the CUDA
# devel base. Both are Ubuntu 24.04, so the toolchain layers below are identical.
FROM ${BASE_CUDA} AS base-both
FROM ${BASE_CUDA} AS base-nvidia
FROM ${BASE_AMD} AS base-amd
FROM ${BASE_AMD} AS base-none
FROM base-${GPU} AS build
ARG GPU
ARG CPU
ARG DEBIAN_FRONTEND=noninteractive
# Pin the vcpkg registry to the same commit as vcpkg.json's builtin-baseline so
# dependency versions are reproducible. Override at build time for a newer set.
Expand Down Expand Up @@ -179,14 +187,18 @@ RUN --mount=type=cache,target=/root/.cache/vcpkg \
amd) AMD=ON; NV=OFF; CC=OFF ;; \
nvidia) AMD=OFF; NV=ON; CC=ON ;; \
both) AMD=ON; NV=ON; CC=ON ;; \
*) echo "GPU must be amd|nvidia|both" >&2; exit 2 ;; \
none) AMD=OFF; NV=OFF; CC=OFF ;; \
*) echo "GPU must be amd|nvidia|both|none" >&2; exit 2 ;; \
esac; \
if [ "$AMD" = "OFF" ] && [ "$NV" = "OFF" ] && [ "$CPU" != "ON" ]; then \
echo "nothing to build: set CPU=ON and/or GPU=amd|nvidia|both" >&2; exit 2; \
fi; \
OCL=""; \
if [ "$GPU" != "nvidia" ]; then \
if [ "$AMD" = "ON" ]; then \
OCL="-DOpenCL_INCLUDE_DIR=/opt/opencl-win/include -DOpenCL_LIBRARY=/opt/opencl-win/lib/OpenCL.lib"; \
fi; \
cmake --preset windows-cross \
-DBUILD_AMD=$AMD -DBUILD_NVIDIA=$NV -DUSE_CLANG_CUDA=$CC \
-DBUILD_AMD=$AMD -DBUILD_NVIDIA=$NV -DUSE_CLANG_CUDA=$CC -DBUILD_CPU=$CPU \
-DBUILD_EXE_UNIT_TEST=${BUILD_TESTS} \
-DBUILD_EXE_BENCHMARK=${BUILD_BENCH} \
-DVCPKG_MANIFEST_FEATURES="" $OCL; \
Expand All @@ -196,7 +208,7 @@ RUN --mount=type=cache,target=/root/.cache/vcpkg \
# never masked). OpenSSL always; CUDA DLLs only exist when NVIDIA was staged.
RUN set -eux; \
cp /opt/openssl-win/bin/*.dll /src/bin/; \
if [ "$GPU" != "amd" ]; then cp /opt/cuda-win/bin/*.dll /src/bin/; fi
if [ "$GPU" = "nvidia" ] || [ "$GPU" = "both" ]; then cp /opt/cuda-win/bin/*.dll /src/bin/; fi

FROM scratch AS artifact
COPY --from=build /src/bin /
4 changes: 2 additions & 2 deletions documentation/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# LuminousMiner — Architecture

> Version 0.12 · C++20 · NVIDIA (CUDA) · AMD (OpenCL) · CPU (OpenMP)
> Version 0.12 · C++20 · NVIDIA (CUDA) · AMD (OpenCL) · CPU (std::thread pool)

---

Expand Down Expand Up @@ -81,7 +81,7 @@ sources/
├── resolver/
│ ├── amd/ # OpenCL resolvers (one per algorithm)
│ ├── nvidia/ # CUDA resolvers (one per algorithm)
│ └── cpu/ # OpenMP CPU resolver
│ └── cpu/ # CPU resolver (std::thread pool)
├── statistical/ # Hashrate & share counters
├── stratum/ # Stratum protocol implementations
└── web/ # Web UI assets
Expand Down
59 changes: 59 additions & 0 deletions documentation/CPU_TUNING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# CPU Mining — Thread Count & Affinity Tuning

The CPU resolver scans nonces with a self-owned pool of persistent worker threads (one
batch of nonces fanned across the pool each iteration). Three flags control it:

| Flag | Default | Meaning |
|---|---|---|
| `--cpu` | `false` | Enable the CPU device. |
| `--cpu_threads` | all logical cores | Number of worker threads (pool size). |
| `--cpu_affinity` | none | Hex bitmask of logical cores to pin workers to (bit *i* = core *i*). |

`--cpu_affinity` accepts `0xFF` or `FF` (up to 64 cores). `--cpu_threads=0` is treated as
unset and falls back to all logical cores.

## How the two flags interact

| `--cpu_threads` | `--cpu_affinity` | Result |
|---|---|---|
| unset | unset | one worker per logical core, no pinning (default) |
| unset | set | N = popcount(mask); worker *k* is pinned to the *k*-th set bit |
| set | unset | N workers, no pinning |
| set | set | N workers; if N > set bits → pinned round-robin over them; if N < set bits → first N |

## Running CPU mining alongside a GPU

A GPU is not autonomous: its host-side feeder thread and the OpenCL/CUDA driver threads
(all inside the miner process) must launch kernels and read results continuously to keep
the GPU busy. If the CPU resolver spawns **one worker per core** (the default), those
workers saturate every core and starve the GPU's host threads — the GPU can drop to a
fraction of its solo hashrate while the CPU adds only a tiny amount.

> Example (16-core host + an RX 9070 XT, BLAKE3): GPU-only ≈ **1.70 GH/s**. With default
> all-core CPU mining also enabled, the GPU collapsed to ≈ **0.4 GH/s** — a far bigger
> loss than the ≈ 11 MH/s of CPU gained.

Lowering the thread *count* alone does **not** fix this: unpinned threads roam across all
cores and still bounce the GPU's feeder. The fix is to **reserve cores for the GPU with
affinity**:

```sh
# Pin the CPU pool to cores 0-7; leave cores 8-15 free for the GPU's host threads.
miner --algo=blake3 --host=<pool> --port=<port> --wallet=<addr> \
--amd=true --cpu=true --cpu_threads=8 --cpu_affinity=0xFF
```

On the example host this restored the GPU to its full ≈ 1.70 GH/s while still mining
≈ 7 MH/s on the CPU.

## Recommendations

- On a GPU rig, **GPU-only is usually best** — a modern GPU out-hashes the CPU by orders
of magnitude, so CPU mining rarely pays for the contention it adds.
- If you do want CPU hashes too, **reserve cores for the GPU** with `--cpu_threads` +
`--cpu_affinity`; leave a few cores out of the CPU mask for the GPU's host/driver
threads, and tune the split to taste.
- On a **CPU-only** machine, the defaults (all cores, no pinning) are correct.
- Process-level priority tweaks (e.g. forcing the whole miner to "below normal") do
**not** relieve GPU+CPU contention — they lower the GPU feeder thread too. The
reservation has to be per-core (affinity).
2 changes: 2 additions & 0 deletions documentation/PARAMETERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ N/A : No default value is set.
| `--nvidia` | ✅ | true | Enable or disable device nvidia. | `--nvidia=<true\|false>` |
| `--amd` | ✅ | true | Enable or disable device amd. | `--amd=<true\|false>` |
| `--cpu` | ✅ | false | Enable or disable device cpu. | `--cpu=<true\|false>` |
| `--cpu_threads` | ✅ | all cores | Number of CPU worker threads (pool size). | `--cpu_threads=8` |
| `--cpu_affinity` | ✅ | none | Hex bitmask of logical cores to pin CPU workers to (bit i = core i). See [CPU_TUNING.md](CPU_TUNING.md). | `--cpu_affinity=0xFF` |
| `--socks5` | ✅ | false | Enable pool connection through a SOCKS5 proxy server on localhost. | `--socks5=<true\|false>` |
| `--socks_port` | ✅ | 9050 | The port of the SOCKS5 proxy server on localhost. | `--socks_port=9050` |
| `--socks_host` | ✅ | 127.0.0.1 | The host address of the SOCKS5 proxy server. | `--socks_host=127.0.0.1` |
Expand Down
28 changes: 21 additions & 7 deletions documentation/build/BUILD_DOCKER_LINUX.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,41 @@
Build the Linux `miner` (ELF) in a container with **no local compilers or SDKs** —
only Docker (BuildKit) is required.

The Dockerfile selects backends with a `GPU` build-arg (`amd` | `nvidia` | `both`,
default `both`):
The Dockerfile exposes two independent axes — a `GPU` build-arg
(`amd` | `nvidia` | `both` | `none`, default `both`) and a `CPU` build-arg
(`ON` | `OFF`, default `OFF`):

| Dockerfile | Output | Backends |
|---|---|---|
| `docker/Dockerfile.linux` | `miner` (ELF) | AMD, NVIDIA, or both |
| `docker/Dockerfile.linux` | `miner` (ELF) | AMD, NVIDIA, both, or none — plus the CPU resolver when `CPU=ON` |

`GPU=amd` uses a lean `ubuntu:24.04` base; `GPU=nvidia`/`both` use
`nvidia/cuda:13.1.2-devel-ubuntu24.04`.
`GPU=amd`/`none` use a lean `ubuntu:24.04` base; `GPU=nvidia`/`both` use
`nvidia/cuda:13.1.2-devel-ubuntu24.04`. `CPU=ON` folds the CPU resolver into
the same binary and can combine with any `GPU` value. `GPU=none CPU=ON` is a CPU-only
build; `GPU=none CPU=OFF` is rejected (nothing to build).

## Helper script (PowerShell)
```powershell
scripts/docker-build.ps1 -Os linux -Gpu amd # AMD-only ELF
scripts/docker-build.ps1 -Os linux -Gpu amd # AMD-only ELF
scripts/docker-build.ps1 -Os linux -Gpu amd -Cpu # AMD + CPU in one ELF
scripts/docker-build.ps1 -Os linux -Gpu none -Cpu # CPU-only ELF
```
Binaries are extracted to `dist/<os>-<gpu>/` (e.g. `dist/linux-amd/`).
Binaries are extracted to `dist/<os>-<gpu>[-cpu]/` (e.g. `dist/linux-amd/`,
`dist/linux-amd-cpu/`, `dist/linux-none-cpu/`).

## Direct docker build
```sh
# Linux, AMD only -> dist/linux-amd/
DOCKER_BUILDKIT=1 docker build -f docker/Dockerfile.linux \
--build-arg GPU=amd --target artifact -o dist/linux-amd .

# Linux, AMD + CPU resolver -> dist/linux-amd-cpu/
DOCKER_BUILDKIT=1 docker build -f docker/Dockerfile.linux \
--build-arg GPU=amd --build-arg CPU=ON --target artifact -o dist/linux-amd-cpu .

# Linux, CPU-only -> dist/linux-none-cpu/
DOCKER_BUILDKIT=1 docker build -f docker/Dockerfile.linux \
--build-arg GPU=none --build-arg CPU=ON --target artifact -o dist/linux-none-cpu .
```
The artifact contains `miner`, the OpenCL `kernel/` directory, and the required
runtime files.
11 changes: 6 additions & 5 deletions documentation/build/BUILD_DOCKER_LINUX_ARM64.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ On an Apple Silicon Mac the arm64 image builds and runs **natively** (no emulati

```sh
docker buildx build --platform linux/arm64 -f docker/Dockerfile.linux \
--build-arg GPU=cpu --target runtime -t lm:linux-arm64 --load .
--build-arg GPU=none --build-arg CPU=ON --target runtime -t lm:linux-arm64 --load .
docker run --rm lm:linux-arm64 --help
```

The same `GPU=cpu` mode works for x86-64 (`--platform linux/amd64`). The existing
`GPU=amd|nvidia|both` GPU builds are unchanged. CI builds the arm64 image on a native
arm64 runner via `.github/workflows/miner_linux_arm64_vcpkg.yml`, and the x86-64
CPU path via `.github/workflows/miner_linux_x64_vcpkg.yml`.
The same `GPU=none CPU=ON` (CPU-only) mode works for x86-64 (`--platform linux/amd64`).
The `GPU=amd|nvidia|both` GPU builds are unchanged, and `CPU=ON` can be added to any of
them to fold in the CPU resolver. CI builds the arm64 image on a native arm64 runner via
`.github/workflows/miner_linux_arm64_vcpkg.yml`, and the x86-64 CPU path via
`.github/workflows/miner_linux_x64_vcpkg.yml`.
22 changes: 17 additions & 5 deletions documentation/build/BUILD_DOCKER_WINDOWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,36 @@ Build a Windows `miner.exe` (PE32+) with **no local compilers or SDKs** — only
(clang-cl + xwin), so Docker stays in **Linux container mode**; there is no engine-mode
switch and no Windows host needed.

The Dockerfile selects backends with a `GPU` build-arg (`amd` | `nvidia` | `both`,
default `both`):
The Dockerfile exposes two independent axes — a `GPU` build-arg
(`amd` | `nvidia` | `both` | `none`, default `both`) and a `CPU` build-arg
(`ON` | `OFF`, default `OFF`):

| Dockerfile | Output | Backends |
|---|---|---|
| `docker/Dockerfile.windows-cross` | `miner.exe` (PE32+) | AMD (OpenCL), NVIDIA (CUDA), or **both in one binary** |
| `docker/Dockerfile.windows-cross` | `miner.exe` (PE32+) | AMD (OpenCL), NVIDIA (CUDA), both, or none — plus the CPU resolver when `CPU=ON` |

`CPU=ON` folds the CPU resolver into `miner.exe` and combines with any `GPU` value;
`GPU=none CPU=ON` is a CPU-only `miner.exe`. The CPU resolver parallelizes via an
in-process `std::thread` pool, so the clang-cl build is fully multicore (no OpenMP or
libomp needed). `GPU=none CPU=OFF` is rejected (nothing to build).

## Helper script (PowerShell)
```powershell
scripts/docker-build.ps1 -Os windows-cross -Gpu both # combined AMD+NVIDIA miner.exe
scripts/docker-build.ps1 -Os windows-cross -Gpu both # combined AMD+NVIDIA miner.exe
scripts/docker-build.ps1 -Os windows-cross -Gpu none -Cpu # CPU-only miner.exe
```
Binaries are extracted to `dist/<os>-<gpu>/` (e.g. `dist/windows-cross-both/`).
Binaries are extracted to `dist/<os>-<gpu>[-cpu]/` (e.g. `dist/windows-cross-both/`,
`dist/windows-cross-none-cpu/`).

## Direct docker build
```sh
# Windows, combined AMD+NVIDIA -> dist/windows-cross-both/
DOCKER_BUILDKIT=1 docker build -f docker/Dockerfile.windows-cross \
--build-arg GPU=both --target artifact -o dist/windows-cross-both .

# Windows, CPU-only -> dist/windows-cross-none-cpu/
DOCKER_BUILDKIT=1 docker build -f docker/Dockerfile.windows-cross \
--build-arg GPU=none --build-arg CPU=ON --target artifact -o dist/windows-cross-none-cpu .
```
The artifact contains `miner.exe`, the OpenCL `kernel/` directory, and the required
OpenSSL + CUDA runtime DLLs. The combined `miner.exe` runs on a host that has only one
Expand Down
Loading
Loading