feat : add user input engineWorkersNb
This commit is contained in:
@@ -18,35 +18,42 @@ export const STRONGEST_ENGINE: EngineName = EngineName.Stockfish17;
|
|||||||
|
|
||||||
export const ENGINE_LABELS: Record<
|
export const ENGINE_LABELS: Record<
|
||||||
EngineName,
|
EngineName,
|
||||||
{ small: string; full: string }
|
{ small: string; full: string; sizeMb: number }
|
||||||
> = {
|
> = {
|
||||||
[EngineName.Stockfish17]: {
|
[EngineName.Stockfish17]: {
|
||||||
full: "Stockfish 17 (75MB)",
|
full: "Stockfish 17 (75MB)",
|
||||||
small: "Stockfish 17",
|
small: "Stockfish 17",
|
||||||
|
sizeMb: 75,
|
||||||
},
|
},
|
||||||
[EngineName.Stockfish17Lite]: {
|
[EngineName.Stockfish17Lite]: {
|
||||||
full: "Stockfish 17 Lite (6MB)",
|
full: "Stockfish 17 Lite (6MB)",
|
||||||
small: "Stockfish 17 Lite",
|
small: "Stockfish 17 Lite",
|
||||||
|
sizeMb: 6,
|
||||||
},
|
},
|
||||||
[EngineName.Stockfish16_1]: {
|
[EngineName.Stockfish16_1]: {
|
||||||
full: "Stockfish 16.1 (64MB)",
|
full: "Stockfish 16.1 (64MB)",
|
||||||
small: "Stockfish 16.1",
|
small: "Stockfish 16.1",
|
||||||
|
sizeMb: 64,
|
||||||
},
|
},
|
||||||
[EngineName.Stockfish16_1Lite]: {
|
[EngineName.Stockfish16_1Lite]: {
|
||||||
full: "Stockfish 16.1 Lite (6MB)",
|
full: "Stockfish 16.1 Lite (6MB)",
|
||||||
small: "Stockfish 16.1 Lite",
|
small: "Stockfish 16.1 Lite",
|
||||||
|
sizeMb: 6,
|
||||||
},
|
},
|
||||||
[EngineName.Stockfish16NNUE]: {
|
[EngineName.Stockfish16NNUE]: {
|
||||||
full: "Stockfish 16 (40MB)",
|
full: "Stockfish 16 (40MB)",
|
||||||
small: "Stockfish 16",
|
small: "Stockfish 16",
|
||||||
|
sizeMb: 40,
|
||||||
},
|
},
|
||||||
[EngineName.Stockfish16]: {
|
[EngineName.Stockfish16]: {
|
||||||
full: "Stockfish 16 Lite (HCE)",
|
full: "Stockfish 16 Lite (HCE)",
|
||||||
small: "Stockfish 16 Lite",
|
small: "Stockfish 16 Lite",
|
||||||
|
sizeMb: 2,
|
||||||
},
|
},
|
||||||
[EngineName.Stockfish11]: {
|
[EngineName.Stockfish11]: {
|
||||||
full: "Stockfish 11 (HCE)",
|
full: "Stockfish 11 (HCE)",
|
||||||
small: "Stockfish 11",
|
small: "Stockfish 11",
|
||||||
|
sizeMb: 2,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,12 +12,17 @@ export const isWasmSupported = () =>
|
|||||||
|
|
||||||
export const isMultiThreadSupported = () => {
|
export const isMultiThreadSupported = () => {
|
||||||
try {
|
try {
|
||||||
return SharedArrayBuffer !== undefined;
|
return SharedArrayBuffer !== undefined && !isIosDevice();
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
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 => {
|
export const isEngineSupported = (name: EngineName): boolean => {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case EngineName.Stockfish17:
|
case EngineName.Stockfish17:
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ export class UciEngine {
|
|||||||
await engine.addNewWorker();
|
await engine.addNewWorker();
|
||||||
engine.isReady = true;
|
engine.isReady = true;
|
||||||
|
|
||||||
console.log(`${engineName} initialized`);
|
|
||||||
return engine;
|
return engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,11 +137,11 @@ export class UciEngine {
|
|||||||
for (const worker of this.workers) {
|
for (const worker of this.workers) {
|
||||||
this.terminateWorker(worker);
|
this.terminateWorker(worker);
|
||||||
}
|
}
|
||||||
|
this.workers = [];
|
||||||
console.log(`${this.name} shutdown`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private terminateWorker(worker: EngineWorker) {
|
private terminateWorker(worker: EngineWorker) {
|
||||||
|
console.log(`Terminating worker from ${this.enginePath}`);
|
||||||
worker.uci("quit");
|
worker.uci("quit");
|
||||||
worker.terminate?.();
|
worker.terminate?.();
|
||||||
worker.isReady = false;
|
worker.isReady = false;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { EngineWorker } from "@/types/engine";
|
import { EngineWorker } from "@/types/engine";
|
||||||
|
import { isIosDevice, isMobileDevice } from "./shared";
|
||||||
|
|
||||||
export const getEngineWorker = (enginePath: string): EngineWorker => {
|
export const getEngineWorker = (enginePath: string): EngineWorker => {
|
||||||
console.log(`Creating worker from ${enginePath}`);
|
console.log(`Creating worker from ${enginePath}`);
|
||||||
@@ -47,7 +48,7 @@ export const getRecommendedWorkersNb = (): number => {
|
|||||||
const maxWorkersNbFromThreads = Math.max(
|
const maxWorkersNbFromThreads = Math.max(
|
||||||
1,
|
1,
|
||||||
navigator.hardwareConcurrency - 4,
|
navigator.hardwareConcurrency - 4,
|
||||||
Math.ceil((navigator.hardwareConcurrency * 2) / 3)
|
Math.floor((navigator.hardwareConcurrency * 2) / 3)
|
||||||
);
|
);
|
||||||
|
|
||||||
const maxWorkersNbFromMemory =
|
const maxWorkersNbFromMemory =
|
||||||
@@ -55,5 +56,11 @@ export const getRecommendedWorkersNb = (): number => {
|
|||||||
? navigator.deviceMemory
|
? navigator.deviceMemory
|
||||||
: 4;
|
: 4;
|
||||||
|
|
||||||
return Math.min(maxWorkersNbFromThreads, maxWorkersNbFromMemory, 10);
|
const maxWorkersNbFromDevice = isIosDevice() ? 2 : isMobileDevice() ? 4 : 10;
|
||||||
|
|
||||||
|
return Math.min(
|
||||||
|
maxWorkersNbFromThreads,
|
||||||
|
maxWorkersNbFromMemory,
|
||||||
|
maxWorkersNbFromDevice
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
engineDepthAtom,
|
engineDepthAtom,
|
||||||
engineMultiPvAtom,
|
engineMultiPvAtom,
|
||||||
engineNameAtom,
|
engineNameAtom,
|
||||||
|
engineWorkersNbAtom,
|
||||||
evaluationProgressAtom,
|
evaluationProgressAtom,
|
||||||
gameAtom,
|
gameAtom,
|
||||||
gameEvalAtom,
|
gameEvalAtom,
|
||||||
@@ -19,12 +20,12 @@ import { useEffect, useCallback } from "react";
|
|||||||
import { usePlayersData } from "@/hooks/usePlayersData";
|
import { usePlayersData } from "@/hooks/usePlayersData";
|
||||||
import { Typography } from "@mui/material";
|
import { Typography } from "@mui/material";
|
||||||
import { useCurrentPosition } from "../hooks/useCurrentPosition";
|
import { useCurrentPosition } from "../hooks/useCurrentPosition";
|
||||||
import { getRecommendedWorkersNb } from "@/lib/engine/worker";
|
|
||||||
|
|
||||||
export default function AnalyzeButton() {
|
export default function AnalyzeButton() {
|
||||||
const engineName = useAtomValue(engineNameAtom);
|
const engineName = useAtomValue(engineNameAtom);
|
||||||
const engine = useEngine(engineName);
|
const engine = useEngine(engineName);
|
||||||
useCurrentPosition(engine);
|
useCurrentPosition(engine);
|
||||||
|
const engineWorkersNb = useAtomValue(engineWorkersNbAtom);
|
||||||
const [evaluationProgress, setEvaluationProgress] = useAtom(
|
const [evaluationProgress, setEvaluationProgress] = useAtom(
|
||||||
evaluationProgressAtom
|
evaluationProgressAtom
|
||||||
);
|
);
|
||||||
@@ -58,7 +59,7 @@ export default function AnalyzeButton() {
|
|||||||
white: white?.rating,
|
white: white?.rating,
|
||||||
black: black?.rating,
|
black: black?.rating,
|
||||||
},
|
},
|
||||||
workersNb: getRecommendedWorkersNb(),
|
workersNb: engineWorkersNb,
|
||||||
});
|
});
|
||||||
|
|
||||||
setEval(newGameEval);
|
setEval(newGameEval);
|
||||||
@@ -86,6 +87,7 @@ export default function AnalyzeButton() {
|
|||||||
}, [
|
}, [
|
||||||
engine,
|
engine,
|
||||||
engineName,
|
engineName,
|
||||||
|
engineWorkersNb,
|
||||||
game,
|
game,
|
||||||
engineDepth,
|
engineDepth,
|
||||||
engineMultiPv,
|
engineMultiPv,
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { DEFAULT_ENGINE } from "@/constants";
|
import { DEFAULT_ENGINE } from "@/constants";
|
||||||
|
import { getRecommendedWorkersNb } from "@/lib/engine/worker";
|
||||||
import { EngineName } from "@/types/enums";
|
import { EngineName } from "@/types/enums";
|
||||||
import { CurrentPosition, GameEval, SavedEvals } from "@/types/eval";
|
import { CurrentPosition, GameEval, SavedEvals } from "@/types/eval";
|
||||||
import { Chess } from "chess.js";
|
import { Chess } from "chess.js";
|
||||||
import { atom } from "jotai";
|
import { atom } from "jotai";
|
||||||
|
import { atomWithStorage } from "jotai/utils";
|
||||||
|
|
||||||
export const gameEvalAtom = atom<GameEval | undefined>(undefined);
|
export const gameEvalAtom = atom<GameEval | undefined>(undefined);
|
||||||
export const gameAtom = atom(new Chess());
|
export const gameAtom = atom(new Chess());
|
||||||
@@ -16,6 +18,10 @@ export const showPlayerMoveIconAtom = atom(true);
|
|||||||
export const engineNameAtom = atom<EngineName>(DEFAULT_ENGINE);
|
export const engineNameAtom = atom<EngineName>(DEFAULT_ENGINE);
|
||||||
export const engineDepthAtom = atom(14);
|
export const engineDepthAtom = atom(14);
|
||||||
export const engineMultiPvAtom = atom(3);
|
export const engineMultiPvAtom = atom(3);
|
||||||
|
export const engineWorkersNbAtom = atomWithStorage(
|
||||||
|
"engineWorkersNb",
|
||||||
|
getRecommendedWorkersNb()
|
||||||
|
);
|
||||||
export const evaluationProgressAtom = atom(0);
|
export const evaluationProgressAtom = atom(0);
|
||||||
|
|
||||||
export const savedEvalsAtom = atom<SavedEvals>({});
|
export const savedEvalsAtom = atom<SavedEvals>({});
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
engineNameAtom,
|
engineNameAtom,
|
||||||
engineDepthAtom,
|
engineDepthAtom,
|
||||||
engineMultiPvAtom,
|
engineMultiPvAtom,
|
||||||
|
engineWorkersNbAtom,
|
||||||
} from "../analysis/states";
|
} from "../analysis/states";
|
||||||
import ArrowOptions from "./arrowOptions";
|
import ArrowOptions from "./arrowOptions";
|
||||||
import { useAtomLocalStorage } from "@/hooks/useAtomLocalStorage";
|
import { useAtomLocalStorage } from "@/hooks/useAtomLocalStorage";
|
||||||
@@ -35,6 +36,7 @@ import {
|
|||||||
PIECE_SETS,
|
PIECE_SETS,
|
||||||
STRONGEST_ENGINE,
|
STRONGEST_ENGINE,
|
||||||
} from "@/constants";
|
} from "@/constants";
|
||||||
|
import { getRecommendedWorkersNb } from "@/lib/engine/worker";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -56,6 +58,7 @@ export default function EngineSettingsDialog({ open, onClose }: Props) {
|
|||||||
);
|
);
|
||||||
const [boardHue, setBoardHue] = useAtom(boardHueAtom);
|
const [boardHue, setBoardHue] = useAtom(boardHueAtom);
|
||||||
const [pieceSet, setPieceSet] = useAtom(pieceSetAtom);
|
const [pieceSet, setPieceSet] = useAtom(pieceSetAtom);
|
||||||
|
const [engineWorkersNb, setEngineWorkersNb] = useAtom(engineWorkersNbAtom);
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isDarkMode = theme.palette.mode === "dark";
|
const isDarkMode = theme.palette.mode === "dark";
|
||||||
@@ -72,12 +75,15 @@ export default function EngineSettingsDialog({ open, onClose }: Props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onClose={onClose} maxWidth="md" fullWidth>
|
<Dialog open={open} onClose={onClose} maxWidth="md" fullWidth>
|
||||||
<DialogTitle variant="h5">Settings</DialogTitle>
|
<DialogTitle variant="h5" sx={{ paddingBottom: 1 }}>
|
||||||
|
Settings
|
||||||
|
</DialogTitle>
|
||||||
<DialogContent sx={{ paddingBottom: 0 }}>
|
<DialogContent sx={{ paddingBottom: 0 }}>
|
||||||
<Grid
|
<Grid
|
||||||
container
|
container
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
|
paddingTop={1}
|
||||||
spacing={3}
|
spacing={3}
|
||||||
size={12}
|
size={12}
|
||||||
>
|
>
|
||||||
@@ -86,12 +92,14 @@ export default function EngineSettingsDialog({ open, onClose }: Props) {
|
|||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
size={{ xs: 12, sm: 7, md: 8 }}
|
size={{ xs: 12, sm: 7, md: 8 }}
|
||||||
>
|
>
|
||||||
<Typography>
|
<Typography variant="body2">
|
||||||
{ENGINE_LABELS[DEFAULT_ENGINE].small} is the default engine if
|
{ENGINE_LABELS[DEFAULT_ENGINE].small} is the default engine if
|
||||||
your device support its requirements. It offers the best balance
|
your device support its requirements. It offers the best balance
|
||||||
between speed and strength.{" "}
|
between speed and strength.{" "}
|
||||||
{ENGINE_LABELS[STRONGEST_ENGINE].small} is the strongest engine
|
{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.
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
@@ -162,6 +170,7 @@ export default function EngineSettingsDialog({ open, onClose }: Props) {
|
|||||||
<Grid
|
<Grid
|
||||||
container
|
container
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
|
alignItems="center"
|
||||||
size={{ xs: 12, sm: 4, md: 3 }}
|
size={{ xs: 12, sm: 4, md: 3 }}
|
||||||
>
|
>
|
||||||
<FormControl variant="outlined">
|
<FormControl variant="outlined">
|
||||||
@@ -194,6 +203,26 @@ export default function EngineSettingsDialog({ open, onClose }: Props) {
|
|||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<Grid container justifyContent="center" size={{ xs: 12, sm: 7 }}>
|
||||||
|
<Slider
|
||||||
|
label="Number of threads"
|
||||||
|
value={engineWorkersNb}
|
||||||
|
setValue={setEngineWorkersNb}
|
||||||
|
min={1}
|
||||||
|
max={10}
|
||||||
|
marksFilter={1}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid container justifyContent="center" size={{ xs: 12, sm: 5 }}>
|
||||||
|
<Typography variant="body2">
|
||||||
|
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()}.
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions sx={{ m: 1 }}>
|
<DialogActions sx={{ m: 1 }}>
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ export interface EngineWorker {
|
|||||||
isReady: boolean;
|
isReady: boolean;
|
||||||
uci(command: string): void;
|
uci(command: string): void;
|
||||||
listen: (data: string) => void;
|
listen: (data: string) => void;
|
||||||
terminate?: () => void;
|
terminate: () => void;
|
||||||
setNnueBuffer?: (data: Uint8Array, index?: number) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkerJob {
|
export interface WorkerJob {
|
||||||
|
|||||||
Reference in New Issue
Block a user