Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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