From cc9a45a45d6c13c16d811caf3a7d0eddd39ed33c Mon Sep 17 00:00:00 2001 From: GuillaumeSD Date: Thu, 15 May 2025 00:32:05 +0200 Subject: [PATCH] feat : add user input engineWorkersNb --- src/constants.ts | 9 ++++- src/lib/engine/shared.ts | 7 +++- src/lib/engine/uciEngine.ts | 5 ++- src/lib/engine/worker.ts | 11 ++++-- .../analysis/panelHeader/analyzeButton.tsx | 6 ++-- src/sections/analysis/states.ts | 6 ++++ .../engineSettings/engineSettingsDialog.tsx | 35 +++++++++++++++++-- src/types/engine.ts | 3 +- 8 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index f46afbe..eee8733 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -18,35 +18,42 @@ export const STRONGEST_ENGINE: EngineName = EngineName.Stockfish17; export const ENGINE_LABELS: Record< EngineName, - { small: string; full: string } + { small: string; full: string; sizeMb: number } > = { [EngineName.Stockfish17]: { full: "Stockfish 17 (75MB)", small: "Stockfish 17", + sizeMb: 75, }, [EngineName.Stockfish17Lite]: { full: "Stockfish 17 Lite (6MB)", small: "Stockfish 17 Lite", + sizeMb: 6, }, [EngineName.Stockfish16_1]: { full: "Stockfish 16.1 (64MB)", small: "Stockfish 16.1", + sizeMb: 64, }, [EngineName.Stockfish16_1Lite]: { full: "Stockfish 16.1 Lite (6MB)", small: "Stockfish 16.1 Lite", + sizeMb: 6, }, [EngineName.Stockfish16NNUE]: { full: "Stockfish 16 (40MB)", small: "Stockfish 16", + sizeMb: 40, }, [EngineName.Stockfish16]: { full: "Stockfish 16 Lite (HCE)", small: "Stockfish 16 Lite", + sizeMb: 2, }, [EngineName.Stockfish11]: { full: "Stockfish 11 (HCE)", small: "Stockfish 11", + sizeMb: 2, }, }; diff --git a/src/lib/engine/shared.ts b/src/lib/engine/shared.ts index 9b40c8c..328366a 100644 --- a/src/lib/engine/shared.ts +++ b/src/lib/engine/shared.ts @@ -12,12 +12,17 @@ export const isWasmSupported = () => export const isMultiThreadSupported = () => { try { - return SharedArrayBuffer !== undefined; + return SharedArrayBuffer !== undefined && !isIosDevice(); } catch { return false; } }; +export const isIosDevice = () => /iPhone|iPad|iPod/i.test(navigator.userAgent); + +export const isMobileDevice = () => + isIosDevice() || /Android|Opera Mini/i.test(navigator.userAgent); + export const isEngineSupported = (name: EngineName): boolean => { switch (name) { case EngineName.Stockfish17: diff --git a/src/lib/engine/uciEngine.ts b/src/lib/engine/uciEngine.ts index 49402e9..37754f5 100644 --- a/src/lib/engine/uciEngine.ts +++ b/src/lib/engine/uciEngine.ts @@ -49,7 +49,6 @@ export class UciEngine { await engine.addNewWorker(); engine.isReady = true; - console.log(`${engineName} initialized`); return engine; } @@ -138,11 +137,11 @@ export class UciEngine { for (const worker of this.workers) { this.terminateWorker(worker); } - - console.log(`${this.name} shutdown`); + this.workers = []; } private terminateWorker(worker: EngineWorker) { + console.log(`Terminating worker from ${this.enginePath}`); worker.uci("quit"); worker.terminate?.(); worker.isReady = false; diff --git a/src/lib/engine/worker.ts b/src/lib/engine/worker.ts index b3e3e9f..81b07b3 100644 --- a/src/lib/engine/worker.ts +++ b/src/lib/engine/worker.ts @@ -1,4 +1,5 @@ import { EngineWorker } from "@/types/engine"; +import { isIosDevice, isMobileDevice } from "./shared"; export const getEngineWorker = (enginePath: string): EngineWorker => { console.log(`Creating worker from ${enginePath}`); @@ -47,7 +48,7 @@ export const getRecommendedWorkersNb = (): number => { const maxWorkersNbFromThreads = Math.max( 1, navigator.hardwareConcurrency - 4, - Math.ceil((navigator.hardwareConcurrency * 2) / 3) + Math.floor((navigator.hardwareConcurrency * 2) / 3) ); const maxWorkersNbFromMemory = @@ -55,5 +56,11 @@ export const getRecommendedWorkersNb = (): number => { ? navigator.deviceMemory : 4; - return Math.min(maxWorkersNbFromThreads, maxWorkersNbFromMemory, 10); + const maxWorkersNbFromDevice = isIosDevice() ? 2 : isMobileDevice() ? 4 : 10; + + return Math.min( + maxWorkersNbFromThreads, + maxWorkersNbFromMemory, + maxWorkersNbFromDevice + ); }; diff --git a/src/sections/analysis/panelHeader/analyzeButton.tsx b/src/sections/analysis/panelHeader/analyzeButton.tsx index cc6d808..7eeb627 100644 --- a/src/sections/analysis/panelHeader/analyzeButton.tsx +++ b/src/sections/analysis/panelHeader/analyzeButton.tsx @@ -3,6 +3,7 @@ import { engineDepthAtom, engineMultiPvAtom, engineNameAtom, + engineWorkersNbAtom, evaluationProgressAtom, gameAtom, gameEvalAtom, @@ -19,12 +20,12 @@ import { useEffect, useCallback } from "react"; import { usePlayersData } from "@/hooks/usePlayersData"; import { Typography } from "@mui/material"; import { useCurrentPosition } from "../hooks/useCurrentPosition"; -import { getRecommendedWorkersNb } from "@/lib/engine/worker"; export default function AnalyzeButton() { const engineName = useAtomValue(engineNameAtom); const engine = useEngine(engineName); useCurrentPosition(engine); + const engineWorkersNb = useAtomValue(engineWorkersNbAtom); const [evaluationProgress, setEvaluationProgress] = useAtom( evaluationProgressAtom ); @@ -58,7 +59,7 @@ export default function AnalyzeButton() { white: white?.rating, black: black?.rating, }, - workersNb: getRecommendedWorkersNb(), + workersNb: engineWorkersNb, }); setEval(newGameEval); @@ -86,6 +87,7 @@ export default function AnalyzeButton() { }, [ engine, engineName, + engineWorkersNb, game, engineDepth, engineMultiPv, diff --git a/src/sections/analysis/states.ts b/src/sections/analysis/states.ts index 17b5203..a294bdf 100644 --- a/src/sections/analysis/states.ts +++ b/src/sections/analysis/states.ts @@ -1,8 +1,10 @@ import { DEFAULT_ENGINE } from "@/constants"; +import { getRecommendedWorkersNb } from "@/lib/engine/worker"; import { EngineName } from "@/types/enums"; import { CurrentPosition, GameEval, SavedEvals } from "@/types/eval"; import { Chess } from "chess.js"; import { atom } from "jotai"; +import { atomWithStorage } from "jotai/utils"; export const gameEvalAtom = atom(undefined); export const gameAtom = atom(new Chess()); @@ -16,6 +18,10 @@ export const showPlayerMoveIconAtom = atom(true); export const engineNameAtom = atom(DEFAULT_ENGINE); export const engineDepthAtom = atom(14); export const engineMultiPvAtom = atom(3); +export const engineWorkersNbAtom = atomWithStorage( + "engineWorkersNb", + getRecommendedWorkersNb() +); export const evaluationProgressAtom = atom(0); export const savedEvalsAtom = atom({}); diff --git a/src/sections/engineSettings/engineSettingsDialog.tsx b/src/sections/engineSettings/engineSettingsDialog.tsx index 6792bfb..0ad94f5 100644 --- a/src/sections/engineSettings/engineSettingsDialog.tsx +++ b/src/sections/engineSettings/engineSettingsDialog.tsx @@ -20,6 +20,7 @@ import { engineNameAtom, engineDepthAtom, engineMultiPvAtom, + engineWorkersNbAtom, } from "../analysis/states"; import ArrowOptions from "./arrowOptions"; import { useAtomLocalStorage } from "@/hooks/useAtomLocalStorage"; @@ -35,6 +36,7 @@ import { PIECE_SETS, STRONGEST_ENGINE, } from "@/constants"; +import { getRecommendedWorkersNb } from "@/lib/engine/worker"; interface Props { open: boolean; @@ -56,6 +58,7 @@ export default function EngineSettingsDialog({ open, onClose }: Props) { ); const [boardHue, setBoardHue] = useAtom(boardHueAtom); const [pieceSet, setPieceSet] = useAtom(pieceSetAtom); + const [engineWorkersNb, setEngineWorkersNb] = useAtom(engineWorkersNbAtom); const theme = useTheme(); const isDarkMode = theme.palette.mode === "dark"; @@ -72,12 +75,15 @@ export default function EngineSettingsDialog({ open, onClose }: Props) { return ( - Settings + + Settings + @@ -86,12 +92,14 @@ export default function EngineSettingsDialog({ open, onClose }: Props) { justifyContent="center" size={{ xs: 12, sm: 7, md: 8 }} > - + {ENGINE_LABELS[DEFAULT_ENGINE].small} is the default engine if your device support its requirements. It offers the best balance between speed and strength.{" "} {ENGINE_LABELS[STRONGEST_ENGINE].small} is the strongest engine - available, note that it requires a one time download of 75MB. + available, note that it requires a one time download of{" "} + {ENGINE_LABELS[STRONGEST_ENGINE].sizeMb}MB and is much more + compute intensive. @@ -162,6 +170,7 @@ export default function EngineSettingsDialog({ open, onClose }: Props) { @@ -194,6 +203,26 @@ export default function EngineSettingsDialog({ open, onClose }: Props) { + + + + + + + + More threads means quicker analysis but only if your device can + handle them, otherwise it may have the opposite effect. The + estimated best value for your device is{" "} + {getRecommendedWorkersNb()}. + + diff --git a/src/types/engine.ts b/src/types/engine.ts index 4c664e3..0caba25 100644 --- a/src/types/engine.ts +++ b/src/types/engine.ts @@ -2,8 +2,7 @@ export interface EngineWorker { isReady: boolean; uci(command: string): void; listen: (data: string) => void; - terminate?: () => void; - setNnueBuffer?: (data: Uint8Array, index?: number) => void; + terminate: () => void; } export interface WorkerJob {