/// <summary> /// Startup method for Ceres UCI chess engine and supplemental features. /// </summary> /// <param name="args"></param> static void Main(string[] args) { #if DEBUG Console.WriteLine(); ConsoleUtils.WriteLineColored(ConsoleColor.Red, "*** WARNING: Ceres binaries built in Debug mode and will run much more slowly than Release"); #endif OutputBanner(); CheckRecursiveOverflow(); HardwareManager.VerifyHardwareSoftwareCompatability(); // Load (or cause to be created) a settings file. if (!CeresUserSettingsManager.DefaultConfigFileExists) { Console.WriteLine(); ConsoleUtils.WriteLineColored(ConsoleColor.Red, $"*** NOTE: Configuration file {CeresUserSettingsManager.DefaultCeresConfigFileName} not found in working directory."); Console.WriteLine(); Console.WriteLine($"Prompting to for required values to initialize:"); CeresUserSettingsManager.DoSetupInitialize(); } // Configure logging level const bool LOG = false; CeresEnvironment.MONITORING_EVENTS = LOG; LogLevel logLevel = LOG ? LogLevel.Information : LogLevel.Critical; LoggerTypes loggerTypes = LoggerTypes.WinDebugLogger | LoggerTypes.ConsoleLogger; CeresEnvironment.Initialize(loggerTypes, logLevel); CeresEnvironment.MONITORING_METRICS = CeresUserSettingsManager.Settings.LaunchMonitor; // if (CeresUserSettingsManager.Settings.DirLC0Networks != null) // NNWeightsFilesLC0.RegisterDirectory(CeresUserSettingsManager.Settings.DirLC0Networks); MCTSEngineInitialization.BaseInitialize(); Console.WriteLine(); #if DEBUG CheckDebugAllowed(); #endif if (args.Length > 0 && args[0].ToUpper() == "CUSTOM") { TournamentTest.Test(); return; // SuiteTest.RunSuiteTest(); return; } StringBuilder allArgs = new StringBuilder(); for (int i = 0; i < args.Length; i++) { allArgs.Append(args[i] + " "); } string allArgsString = allArgs.ToString(); DispatchCommands.ProcessCommand(allArgsString); // Win32.WriteCrashdumpFile(@"d:\temp\dump.dmp"); }
static GameEngine GetEngine(GameEngineUCISpec engineSpec, string suffix, NNEvaluatorDef evaluatorDef, ParamsSearch paramsSearch, ParamsSelect paramsSelect, IManagerGameLimit timeManager) { bool resetMovesBetweenMoves = !paramsSearch.TreeReuseEnabled; bool enableTranpsositions = paramsSearch.Execution.TranspositionMode != TranspositionMode.None; // Create requested type of engine if (engineSpec == null) { return(new GameEngineCeresInProcess("Ceres_" + suffix, evaluatorDef, paramsSearch, paramsSelect, timeManager, null)); } else if (engineSpec.Name == "LC0") { if (evaluatorDef == null) { throw new Exception("EvaluatorDef must be specified when running LC0 engine"); } // TODO: do we really want to emulate always here? probably not // WARNING: above. bool forceDisableSmartPruning = false; return(new GameEngineLC0("LZ0_" + suffix, evaluatorDef.Nets[0].Net.NetworkID, forceDisableSmartPruning, false, paramsSearch, paramsSelect, evaluatorDef, null, CeresUserSettingsManager.GetLC0ExecutableFileName())); } else { return(engineSpec.CreateEngine()); } }
public static void ProcessCommand(string cmd) { cmd = StringUtils.WhitespaceRemoved(cmd).TrimEnd(); string[] parts = cmd.Split(" "); if (cmd == "") { // No arguments at all LaunchUCI(""); Environment.Exit(0); } else if (parts.Length > 0 && parts[0].ToUpper() == "UCI") { // First argument explicit UCI LaunchUCI(cmd.Substring(cmd.IndexOf("UCI ") + 4)); Environment.Exit(0); } else if (parts.Length > 0 && parts[0].Contains("=")) { // No command, just some immediate key-value pairs LaunchUCI(cmd); Environment.Exit(0); } if (cmd.ToLower().EndsWith("-h") || cmd.ToLower().EndsWith("help")) { HelpCommands.ProcessHelpCommand(cmd); } // Command consists command name, then options (sequence of key=value pairs), sometimes followed by a FEN (string featureName, string args) = SplitLeft(cmd); string keyValueArgs = ""; string fen = null; // Default feature (if not specified) is UCI if (featureName == null) { featureName = "UCI"; } featureName = featureName.ToUpper(); // Separate key/value pairs and FEN (if any) if ((featureName == "ANALYZE" || featureName == "TOURN") && args != "") { int posLastEquals = args.LastIndexOf("="); if (posLastEquals == -1) { fen = args; } else { int indexEndValue = args.Substring(posLastEquals).IndexOf(" "); if (indexEndValue != -1) { fen = args.Substring(1 + posLastEquals + indexEndValue); keyValueArgs = args.Substring(0, posLastEquals + indexEndValue); } else { keyValueArgs = args; } } } else { keyValueArgs = args; } if (fen != null && fen.ToLower() == "startpos") { fen = Position.StartPosition.FEN; } // Extract the feature name // Extract and parse options string as sequence of key=value pairs //string options = keyValueArgs.Contains(" ") ? keyValueArgs.Substring(keyValueArgs.IndexOf(" ") + 1) : ""; //KeyValueSetParsed keyValues = options == "" ? null : new KeyValueSetParsed(options); if (featureName == "SUITE") { FeatureSuiteParams suiteParams = FeatureSuiteParams.ParseSuiteCommand(fen, keyValueArgs); FeatureSuiteParams.RunSuiteTest(suiteParams); } else if (featureName == "SETOPT") { if (!keyValueArgs.Contains("=")) { SetoptError(); } KeyValueSetParsed keyValues = new KeyValueSetParsed(keyValueArgs, null); foreach ((string key, string value) in keyValues.KeyValuePairs) { string keyLower = key.ToLower(); if (keyLower == "network") { CeresUserSettingsManager.Settings.DefaultNetworkSpecString = value; } else if (keyLower == "device") { CeresUserSettingsManager.Settings.DefaultDeviceSpecString = value; } else if (keyLower == "dir-epd") { CeresUserSettingsManager.Settings.DirEPD = value; } else if (keyLower == "dir-pgn") { CeresUserSettingsManager.Settings.DirPGN = value; } else if (keyLower == "dir-lc0-networks") { CeresUserSettingsManager.Settings.DirLC0Networks = value; } else if (keyLower == "dir-tablebases") { CeresUserSettingsManager.Settings.DirTablebases = value; } else if (keyLower == "launch-monitor") { CeresUserSettingsManager.Settings.LaunchMonitor = bool.Parse(value); } else if (keyLower == "log-info") { CeresUserSettingsManager.Settings.LogInfo = bool.Parse(value); } else if (keyLower == "log-warn") { CeresUserSettingsManager.Settings.LogWarn = bool.Parse(value); } else { SetoptError(); } Console.WriteLine($"Set {key} to {value}"); } Console.WriteLine($"Updating default Ceres settings file {CeresUserSettingsManager.DefaultCeresConfigFileName}"); CeresUserSettingsManager.SaveToDefaultFile(); } else if (featureName == "TOURN") { FeatureTournParams tournParams = FeatureTournParams.ParseTournCommand(fen, keyValueArgs); FeatureTournParams.RunTournament(tournParams, fen); Console.WriteLine(tournParams.ToString()); } else if (featureName == "ANALYZE") { FeatureAnalyzeParams analyzeParams = FeatureAnalyzeParams.ParseAnalyzeCommand(fen, keyValueArgs); analyzeParams.Execute(fen); } else if (featureName == "SETUP") { if (File.Exists(CeresUserSettingsManager.DefaultCeresConfigFileName)) { Console.WriteLine(); ConsoleUtils.WriteLineColored(ConsoleColor.Red, "WARNING: This action will overwrite the Ceres.json file in the current directory."); Console.WriteLine(); } CeresUserSettingsManager.DoSetupInitialize(); } else if (featureName == "SYSBENCH") { FeatureBenchmark.DumpBenchmark(); } else { ShowErrorExit("Expected argument to begin with one of the features UCI, ANALYZE, SUITE, TOURN, SYSBENCH or SETOPT"); } }
/// <summary> /// Returns the executable location and program arguments appropriate /// for LC0 given specified Ceres parameters /// (optionally emulating them to the degree possible). /// </summary> /// <param name="paramsSearch"></param> /// <param name="paramsSelect"></param> /// <param name="evaluatorDef"></param> /// <param name="network"></param> /// <param name="emulateCeresOptions"></param> /// <param name="verboseOutput"></param> /// <param name="overrideEXE"></param> /// <param name="forceDisableSmartPruning"></param> /// <param name="alwaysFillHistory"></param> /// <returns></returns> public static (string, string) GetLC0EngineOptions(ParamsSearch paramsSearch, ParamsSelect paramsSelect, NNEvaluatorDef evaluatorDef, INNWeightsFileInfo network, bool emulateCeresOptions, bool verboseOutput, string overrideEXE = null, bool forceDisableSmartPruning = false, bool alwaysFillHistory = false) { if (paramsSearch == null) { paramsSearch = new ParamsSearch(); } if (paramsSelect == null) { paramsSelect = new ParamsSelect(); } //fail int8 string precisionStr = MCTSParams.PRECISION == WFEvalNetTensorRT.TRTPrecision.Int8 ? "trt-int8" : "cudnn-fp16"; // Must reverse values to conform to LZ0 convention float FPU_MULTIPLIER = paramsSelect.FPUMode == ParamsSelect.FPUType.Reduction ? -1.0f : 1.0f; // TODO: plug in both versions of Centiapwn to low level Leela code string fpuVals = $"--fpu-value={FPU_MULTIPLIER * paramsSelect.FPUValue} --fpu-value-at-root={FPU_MULTIPLIER * paramsSelect.FPUValueAtRoot} "; string strategyStr = paramsSelect.FPUMode == ParamsSelect.FPUType.Absolute ? "absolute " : "reduction "; string fpuStr = fpuVals + "--fpu-strategy=" + strategyStr; string strategyAtRootStr = paramsSelect.FPUModeAtRoot == ParamsSelect.FPUType.Absolute ? "absolute " : "reduction "; string fpuStrRoot = paramsSelect.FPUModeAtRoot == ParamsSelect.FPUType.Same ? "--fpu-strategy-at-root=same " : "--fpu-strategy-at-root=" + strategyAtRootStr; string netSourceFile = network.FileName; int minibatchSize = 256; // LC0 default // If GPUs have equal fractions then we use demux where only 2 threads needed, // otherwise we use roundrobin and best is 1 + number of GPUS // Note that with increasing threads Leela plays significantly non-deterministically and somewhat worse int NUM_THREADS = evaluatorDef.EqualFractions ? 2 : evaluatorDef.Devices.Length + 1; string lzOptions = "--nodes-as-playouts "; // essential to get same behavior as Ceres with go nodes command lzOptions += "--multi-gather "; // greatly improves search speed if (forceDisableSmartPruning || (emulateCeresOptions && !paramsSearch.FutilityPruningStopSearchEnabled)) { lzOptions += " --smart-pruning-factor=0 "; } // Default nncache is only 200_000 but big tournaments (TCEC 19) have used as high as 20_000_000. // To keep memory requires reasonable for typical systems we default to a value in between. // However note that for very small nets such as 128x10 it may be faster to uze zero nncache. const int LC0_CACHE_SIZE = 5_000_000; int MOVE_OVERHEAD = (int)(new ParamsSearch().MoveOverheadSeconds * 1000); lzOptions += $"--move-overhead={MOVE_OVERHEAD} "; if (USE_LC0_SMALL_SEARCH_SETTINGS) { // Works better for smaller searchs (such as 1000 nodes) lzOptions += " --max-collision-visits=32 "; } else { // Much faster for large searches with multigather enabled. lzOptions += " --max-out-of-order-evals-factor=2.4 --max-collision-events=500 --max-collision-visits=500 "; } if (alwaysFillHistory) { lzOptions += $" --history-fill=always "; } if (emulateCeresOptions) { bool useNNCache = evaluatorDef.CacheMode > PositionEvalCache.CacheMode.None || paramsSearch.Execution.TranspositionMode > TranspositionMode.None; int cacheSize = useNNCache ? LC0_CACHE_SIZE : 0; lzOptions += $@"-w {netSourceFile} -t {NUM_THREADS} --minibatch-size={minibatchSize} " + // " --policy-softmax-temp=2.2 --backend=cudnn-fp16 "; $" --policy-softmax-temp={paramsSelect.PolicySoftmax} --cache-history-length={evaluatorDef.NumCacheHashPositions - 1} " + // $" --score-type=win_percentage" + BackendArgumentsString(evaluatorDef) + //"--backend=multiplexing --backend-opts=(backend=cudnn-fp16,gpu=0),(backend=cudnn-fp16,gpu=1),(backend=cudnn-fp16,gpu=2),(backend=cudnn-fp16,gpu=3) " + // $"--backend={precisionStr} --backend-opts=gpu={SearchParamsNN.GPU_ID_LEELA_UCI} " + $"{fpuStr} {fpuStrRoot} " + $" --no-sticky-endgames "; // + --no-out-of-order-eval"; // *** NOTE: if we add this flag, LZ0 seems to play a little different and better. TODO: study this, should we adopt? lzOptions += $" --cpuct-factor={paramsSelect.CPUCTFactor} --cpuct-base={paramsSelect.CPUCTBase} --cpuct={paramsSelect.CPUCT} --nncache={cacheSize} "; // lzOptions += $" --max-collision-visits={paramsSearch.MAX_COLLISIONS + 1 }"; // Must increment by 1 to make comparable (also, LC0 hangs at value ot zero) } else { // Mostly we let Leela use default options, except to make it fair // we use a large nncache and number of threads appropriate for the number of GPUs in use // lzOptions = $@"-w {weightsDir}\{netSourceFile} --minibatch-size={minibatchSize} -t {paramsNN.NNEVAL_NUM_GPUS + 1} " + lzOptions += $@"-w {netSourceFile} -t {NUM_THREADS} " + // $"--score-type=win_percentage " + $"--nncache={LC0_CACHE_SIZE} " + // like TCEC 10, only 5% benefit $"--max-prefetch=160 --max-collision-events=917 " + BackendArgumentsString(evaluatorDef); } string tbPath = CeresUserSettingsManager.Settings.TablebaseDirectory; if (paramsSearch.EnableTablebases) { lzOptions += (@$ " --syzygy-paths=#{tbPath}# ").Replace("#", "\""); } if (verboseOutput) { lzOptions += " --verbose-move-stats "; } string EXE = CeresUserSettingsManager.GetLC0ExecutableFileName(); #if EXPERIMENTAL const bool LZ_USE_TRT = false; // NOTE: if true, the GPU is seemingly currently hardcoded to 3. The max batch size is 512 if (LZ_USE_TRT) { if (network.NetworkID == "59999") { EXE = @"C:\dev\lc0\19May\lc0\build\lc0_59999.exe"; } else if (network.NetworkID == "42767") { EXE = @"C:\dev\lc0\19May\lc0\build\lc0_42767.exe"; } else { throw new Exception("Unknown net for EXE " + network.NetworkID); } if (evaluatorDef.Nets[0].Net.Precision == NNEvaluatorPrecision.Int8) { lzOptions = lzOptions.Replace("cudnn-fp16", "trt-int8"); } else if (evaluatorDef.Nets[0].Net.Precision == NNEvaluatorPrecision.FP16) { lzOptions = lzOptions.Replace("cudnn-fp16", "trt-fp16"); } else { throw new NotImplementedException(); } } #endif if (overrideEXE != null) { EXE = overrideEXE; } return(EXE, lzOptions); }