Skip to content

Commit

Permalink
Add a standardized benchmark command speedtest.
Browse files Browse the repository at this point in the history
`speedtest [threads] [hash_MiB] [time_s]`. `threads` default to system concurrency. `hash_MiB` defaults to `threads*128`. `time_s` defaults to 150.

Intended to be used with default parameters, as a stable hardware benchmark.

Example:
```
C:\dev\stockfish-master\src>stockfish.exe speedtest
Stockfish dev-20240928-nogit by the Stockfish developers (see AUTHORS file)
info string Using 16 threads
Warmup position 3/3
Position 258/258
===========================
Version                    : Stockfish dev-20240928-nogit
Compiled by                : g++ (GNUC) 13.2.0 on MinGW64
Compilation architecture   : x86-64-vnni256
Compilation settings       : 64bit VNNI BMI2 AVX2 SSE41 SSSE3 SSE2 POPCNT
Compiler __VERSION__ macro : 13.2.0
Large pages                : yes
User invocation            : speedtest
Filled invocation          : speedtest 16 2048 150
Available processors       : 0-15
Thread count               : 16
Thread binding             : none
TT size [MiB]              : 2048
Hash max, avg [per mille]  :
    single search          : 40, 21
    single game            : 631, 428
Total nodes searched       : 2099917842
Total search time [s]      : 153.937
Nodes/second               : 13641410
```

-------------------------------

Small unrelated tweaks:
 - Network verification output is now handled as a callback.
 - TT hashfull queries allow specifying maximum entry age.

closes official-stockfish#5354

No functional change.
  • Loading branch information
Sopel97 authored and vondele committed Sep 28, 2024
1 parent aff1f67 commit cd24c45
Show file tree
Hide file tree
Showing 15 changed files with 664 additions and 53 deletions.
349 changes: 349 additions & 0 deletions src/benchmark.cpp

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions src/benchmark.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ namespace Stockfish::Benchmark {

std::vector<std::string> setup_bench(const std::string&, std::istream&);

struct BenchmarkSetup {
int ttSize;
int threads;
std::vector<std::string> commands;
std::string originalInvocation;
std::string filledInvocation;
};

BenchmarkSetup setup_benchmark(std::istream&);

} // namespace Stockfish

#endif // #ifndef BENCHMARK_H_INCLUDED
37 changes: 27 additions & 10 deletions src/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,13 @@ Engine::Engine(std::optional<std::string> path) :

options["NumaPolicy"] << Option("auto", [this](const Option& o) {
set_numa_config_from_option(o);
return numa_config_information_as_string() + "\n" + thread_binding_information_as_string();
return numa_config_information_as_string() + "\n"
+ thread_allocation_information_as_string();
});

options["Threads"] << Option(1, 1, 1024, [this](const Option&) {
resize_threads();
return thread_binding_information_as_string();
return thread_allocation_information_as_string();
});

options["Hash"] << Option(16, 1, MaxHashMB, [this](const Option& o) {
Expand Down Expand Up @@ -156,6 +157,10 @@ void Engine::set_on_bestmove(std::function<void(std::string_view, std::string_vi
updateContext.onBestmove = std::move(f);
}

void Engine::set_on_verify_networks(std::function<void(std::string_view)>&& f) {
onVerifyNetworks = std::move(f);
}

void Engine::wait_for_search_finished() { threads.main_thread()->wait_for_search_finished(); }

void Engine::set_position(const std::string& fen, const std::vector<std::string>& moves) {
Expand Down Expand Up @@ -226,8 +231,8 @@ void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; }
// network related

void Engine::verify_networks() const {
networks->big.verify(options["EvalFile"]);
networks->small.verify(options["EvalFileSmall"]);
networks->big.verify(options["EvalFile"], onVerifyNetworks);
networks->small.verify(options["EvalFileSmall"], onVerifyNetworks);
}

void Engine::load_networks() {
Expand Down Expand Up @@ -285,6 +290,8 @@ std::string Engine::visualize() const {
return ss.str();
}

int Engine::get_hashfull(int maxAge) const { return tt.hashfull(maxAge); }

std::vector<std::pair<size_t, size_t>> Engine::get_bound_thread_count_by_numa_node() const {
auto counts = threads.get_bound_thread_count_by_numa_node();
const NumaConfig& cfg = numaContext.get_numa_config();
Expand All @@ -310,15 +317,9 @@ std::string Engine::numa_config_information_as_string() const {
std::string Engine::thread_binding_information_as_string() const {
auto boundThreadsByNode = get_bound_thread_count_by_numa_node();
std::stringstream ss;

size_t threadsSize = threads.size();
ss << "Using " << threadsSize << (threadsSize > 1 ? " threads" : " thread");

if (boundThreadsByNode.empty())
return ss.str();

ss << " with NUMA node thread binding: ";

bool isFirst = true;

for (auto&& [current, total] : boundThreadsByNode)
Expand All @@ -332,4 +333,20 @@ std::string Engine::thread_binding_information_as_string() const {
return ss.str();
}

std::string Engine::thread_allocation_information_as_string() const {
std::stringstream ss;

size_t threadsSize = threads.size();
ss << "Using " << threadsSize << (threadsSize > 1 ? " threads" : " thread");

auto boundThreadsByNodeStr = thread_binding_information_as_string();
if (boundThreadsByNodeStr.empty())
return ss.str();

ss << " with NUMA node thread binding: ";
ss << boundThreadsByNodeStr;

return ss.str();
}

}
7 changes: 6 additions & 1 deletion src/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class Engine {
void set_on_update_full(std::function<void(const InfoFull&)>&&);
void set_on_iter(std::function<void(const InfoIter&)>&&);
void set_on_bestmove(std::function<void(std::string_view, std::string_view)>&&);
void set_on_verify_networks(std::function<void(std::string_view)>&&);

// network related

Expand All @@ -97,12 +98,15 @@ class Engine {
const OptionsMap& get_options() const;
OptionsMap& get_options();

int get_hashfull(int maxAge = 0) const;

std::string fen() const;
void flip();
std::string visualize() const;
std::vector<std::pair<size_t, size_t>> get_bound_thread_count_by_numa_node() const;
std::string get_numa_config_as_string() const;
std::string numa_config_information_as_string() const;
std::string thread_allocation_information_as_string() const;
std::string thread_binding_information_as_string() const;

private:
Expand All @@ -119,7 +123,8 @@ class Engine {
TranspositionTable tt;
LazyNumaReplicated<Eval::NNUE::Networks> networks;

Search::SearchManager::UpdateContext updateContext;
Search::SearchManager::UpdateContext updateContext;
std::function<void(std::string_view)> onVerifyNetworks;
};

} // namespace Stockfish
Expand Down
31 changes: 31 additions & 0 deletions src/memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,37 @@ void* aligned_large_pages_alloc(size_t allocSize) {

#endif

bool has_large_pages() {

#if defined(_WIN32)

constexpr size_t page_size = 2 * 1024 * 1024; // 2MB page size assumed
void* mem = aligned_large_pages_alloc_windows(page_size);
if (mem == nullptr)
{
return false;
}
else
{
aligned_large_pages_free(mem);
return true;
}

#elif defined(__linux__)

#if defined(MADV_HUGEPAGE)
return true;
#else
return false;
#endif

#else

return false;

#endif
}


// aligned_large_pages_free() will free the previously memory allocated
// by aligned_large_pages_alloc(). The effect is a nop if mem == nullptr.
Expand Down
2 changes: 2 additions & 0 deletions src/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ void std_aligned_free(void* ptr);
void* aligned_large_pages_alloc(size_t size);
void aligned_large_pages_free(void* mem);

bool has_large_pages();

// Frees memory which was placed there with placement new.
// Works for both single objects and arrays of unknown bound.
template<typename T, typename FREE_FUNC>
Expand Down
11 changes: 7 additions & 4 deletions src/misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class Logger {
//
// For releases (non-dev builds) we only include the version number:
// Stockfish version
std::string engine_info(bool to_uci) {
std::string engine_version_info() {
std::stringstream ss;
ss << "Stockfish " << version << std::setfill('0');

Expand Down Expand Up @@ -151,11 +151,14 @@ std::string engine_info(bool to_uci) {
#endif
}

ss << (to_uci ? "\nid author " : " by ") << "the Stockfish developers (see AUTHORS file)";

return ss.str();
}

std::string engine_info(bool to_uci) {
return engine_version_info() + (to_uci ? "\nid author " : " by ")
+ "the Stockfish developers (see AUTHORS file)";
}


// Returns a string trying to describe the compiler we use
std::string compiler_info() {
Expand Down Expand Up @@ -451,7 +454,7 @@ void remove_whitespace(std::string& s) {
s.erase(std::remove_if(s.begin(), s.end(), [](char c) { return std::isspace(c); }), s.end());
}

bool is_whitespace(const std::string& s) {
bool is_whitespace(std::string_view s) {
return std::all_of(s.begin(), s.end(), [](char c) { return std::isspace(c); });
}

Expand Down
8 changes: 5 additions & 3 deletions src/misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@
#include <iosfwd>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

#define stringify2(x) #x
#define stringify(x) stringify2(x)

namespace Stockfish {

std::string engine_version_info();
std::string engine_info(bool to_uci = false);
std::string compiler_info();

Expand Down Expand Up @@ -79,8 +81,8 @@ inline TimePoint now() {
.count();
}

inline std::vector<std::string> split(const std::string& s, const std::string& delimiter) {
std::vector<std::string> res;
inline std::vector<std::string_view> split(std::string_view s, std::string_view delimiter) {
std::vector<std::string_view> res;

if (s.empty())
return res;
Expand All @@ -102,7 +104,7 @@ inline std::vector<std::string> split(const std::string& s, const std::string& d
}

void remove_whitespace(std::string& s);
bool is_whitespace(const std::string& s);
bool is_whitespace(std::string_view s);

enum SyncCout {
IO_LOCK,
Expand Down
51 changes: 30 additions & 21 deletions src/nnue/network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,35 +234,44 @@ Network<Arch, Transformer>::evaluate(const Position& pos


template<typename Arch, typename Transformer>
void Network<Arch, Transformer>::verify(std::string evalfilePath) const {
void Network<Arch, Transformer>::verify(std::string evalfilePath,
const std::function<void(std::string_view)>& f) const {
if (evalfilePath.empty())
evalfilePath = evalFile.defaultName;

if (evalFile.current != evalfilePath)
{
std::string msg1 =
"Network evaluation parameters compatible with the engine must be available.";
std::string msg2 = "The network file " + evalfilePath + " was not loaded successfully.";
std::string msg3 = "The UCI option EvalFile might need to specify the full path, "
"including the directory name, to the network file.";
std::string msg4 = "The default net can be downloaded from: "
"https://tests.stockfishchess.org/api/nn/"
+ evalFile.defaultName;
std::string msg5 = "The engine will be terminated now.";

sync_cout << "info string ERROR: " << msg1 << sync_endl;
sync_cout << "info string ERROR: " << msg2 << sync_endl;
sync_cout << "info string ERROR: " << msg3 << sync_endl;
sync_cout << "info string ERROR: " << msg4 << sync_endl;
sync_cout << "info string ERROR: " << msg5 << sync_endl;
if (f)
{
std::string msg1 =
"Network evaluation parameters compatible with the engine must be available.";
std::string msg2 = "The network file " + evalfilePath + " was not loaded successfully.";
std::string msg3 = "The UCI option EvalFile might need to specify the full path, "
"including the directory name, to the network file.";
std::string msg4 = "The default net can be downloaded from: "
"https://tests.stockfishchess.org/api/nn/"
+ evalFile.defaultName;
std::string msg5 = "The engine will be terminated now.";

std::string msg = "ERROR: " + msg1 + '\n' + "ERROR: " + msg2 + '\n' + "ERROR: " + msg3
+ '\n' + "ERROR: " + msg4 + '\n' + "ERROR: " + msg5 + '\n';

f(msg);
}

exit(EXIT_FAILURE);
}

size_t size = sizeof(*featureTransformer) + sizeof(Arch) * LayerStacks;
sync_cout << "info string NNUE evaluation using " << evalfilePath << " ("
<< size / (1024 * 1024) << "MiB, (" << featureTransformer->InputDimensions << ", "
<< network[0].TransformedFeatureDimensions << ", " << network[0].FC_0_OUTPUTS << ", "
<< network[0].FC_1_OUTPUTS << ", 1))" << sync_endl;
if (f)
{
size_t size = sizeof(*featureTransformer) + sizeof(Arch) * LayerStacks;
f("info string NNUE evaluation using " + evalfilePath + " ("
+ std::to_string(size / (1024 * 1024)) + "MiB, ("
+ std::to_string(featureTransformer->InputDimensions) + ", "
+ std::to_string(network[0].TransformedFeatureDimensions) + ", "
+ std::to_string(network[0].FC_0_OUTPUTS) + ", " + std::to_string(network[0].FC_1_OUTPUTS)
+ ", 1))");
}
}


Expand Down
4 changes: 3 additions & 1 deletion src/nnue/network.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
#define NETWORK_H_INCLUDED

#include <cstdint>
#include <functional>
#include <iostream>
#include <optional>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>

Expand Down Expand Up @@ -68,7 +70,7 @@ class Network {
void hint_common_access(const Position& pos,
AccumulatorCaches::Cache<FTDimensions>* cache) const;

void verify(std::string evalfilePath) const;
void verify(std::string evalfilePath, const std::function<void(std::string_view)>&) const;
NnueEvalTrace trace_evaluate(const Position& pos,
AccumulatorCaches::Cache<FTDimensions>* cache) const;

Expand Down
11 changes: 6 additions & 5 deletions src/numa.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <atomic>
#include <cstdint>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
Expand Down Expand Up @@ -653,7 +654,7 @@ class NumaConfig {
NumaIndex n = 0;
for (auto&& nodeStr : split(s, ":"))
{
auto indices = indices_from_shortened_string(nodeStr);
auto indices = indices_from_shortened_string(std::string(nodeStr));
if (!indices.empty())
{
for (auto idx : indices)
Expand Down Expand Up @@ -1015,21 +1016,21 @@ class NumaConfig {
if (s.empty())
return indices;

for (const std::string& ss : split(s, ","))
for (const auto& ss : split(s, ","))
{
if (ss.empty())
continue;

auto parts = split(ss, "-");
if (parts.size() == 1)
{
const CpuIndex c = CpuIndex{str_to_size_t(parts[0])};
const CpuIndex c = CpuIndex{str_to_size_t(std::string(parts[0]))};
indices.emplace_back(c);
}
else if (parts.size() == 2)
{
const CpuIndex cfirst = CpuIndex{str_to_size_t(parts[0])};
const CpuIndex clast = CpuIndex{str_to_size_t(parts[1])};
const CpuIndex cfirst = CpuIndex{str_to_size_t(std::string(parts[0]))};
const CpuIndex clast = CpuIndex{str_to_size_t(std::string(parts[1]))};
for (size_t c = cfirst; c <= clast; ++c)
{
indices.emplace_back(c);
Expand Down
Loading

0 comments on commit cd24c45

Please sign in to comment.