2 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3 Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
4 Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
5 Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
7 Stockfish is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 Stockfish is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
38 static int world_rank = MPI_PROC_NULL;
39 static int world_size = 0;
40 static bool stop_signal = false;
41 static MPI_Request reqStop = MPI_REQUEST_NULL;
43 static MPI_Comm InputComm = MPI_COMM_NULL;
44 static MPI_Comm TTComm = MPI_COMM_NULL;
45 static MPI_Comm MoveComm = MPI_COMM_NULL;
46 static MPI_Comm StopComm = MPI_COMM_NULL;
48 static MPI_Datatype TTEntryDatatype = MPI_DATATYPE_NULL;
49 static std::vector<KeyedTTEntry> TTBuff;
51 static MPI_Datatype MIDatatype = MPI_DATATYPE_NULL;
55 constexpr std::array<int, 7> TTblocklens = {1, 1, 1, 1, 1, 1, 1};
56 const std::array<MPI_Aint, 7> TTdisps = {offsetof(KeyedTTEntry, first),
57 offsetof(KeyedTTEntry, second) + offsetof(TTEntry, key16),
58 offsetof(KeyedTTEntry, second) + offsetof(TTEntry, move16),
59 offsetof(KeyedTTEntry, second) + offsetof(TTEntry, value16),
60 offsetof(KeyedTTEntry, second) + offsetof(TTEntry, eval16),
61 offsetof(KeyedTTEntry, second) + offsetof(TTEntry, genBound8),
62 offsetof(KeyedTTEntry, second) + offsetof(TTEntry, depth8)};
63 const std::array<MPI_Datatype, 7> TTtypes = {MPI_UINT64_T,
70 const std::array<MPI_Aint, 4> MIdisps = {offsetof(MoveInfo, move),
71 offsetof(MoveInfo, depth),
72 offsetof(MoveInfo, score),
73 offsetof(MoveInfo, rank)};
75 MPI_Init_thread(nullptr, nullptr, MPI_THREAD_MULTIPLE, &thread_support);
76 if (thread_support < MPI_THREAD_MULTIPLE)
78 std::cerr << "Stockfish requires support for MPI_THREAD_MULTIPLE."
80 std::exit(EXIT_FAILURE);
83 MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
84 MPI_Comm_size(MPI_COMM_WORLD, &world_size);
86 TTBuff.resize(TTSendBufferSize * world_size);
88 MPI_Type_create_struct(7, TTblocklens.data(), TTdisps.data(), TTtypes.data(),
90 MPI_Type_commit(&TTEntryDatatype);
92 MPI_Type_create_hindexed_block(4, 1, MIdisps.data(), MPI_INT, &MIDatatype);
93 MPI_Type_commit(&MIDatatype);
95 MPI_Comm_dup(MPI_COMM_WORLD, &InputComm);
96 MPI_Comm_dup(MPI_COMM_WORLD, &TTComm);
97 MPI_Comm_dup(MPI_COMM_WORLD, &MoveComm);
98 MPI_Comm_dup(MPI_COMM_WORLD, &StopComm);
105 bool getline(std::istream& input, std::string& str) {
107 std::vector<char> vec;
112 state = static_cast<bool>(std::getline(input, str));
113 vec.assign(str.begin(), str.end());
117 // Some MPI implementations use busy-wait pooling, while we need yielding
118 static MPI_Request reqInput = MPI_REQUEST_NULL;
119 MPI_Ibcast(&size, 1, MPI_INT, 0, InputComm, &reqInput);
121 MPI_Wait(&reqInput, MPI_STATUS_IGNORE);
125 MPI_Test(&reqInput, &flag, MPI_STATUS_IGNORE);
129 std::this_thread::sleep_for(std::chrono::milliseconds(10));
135 MPI_Bcast(vec.data(), size, MPI_CHAR, 0, InputComm);
137 str.assign(vec.begin(), vec.end());
138 MPI_Bcast(&state, 1, MPI_CXX_BOOL, 0, InputComm);
145 // Start listening to stop signal
147 MPI_Ibarrier(StopComm, &reqStop);
152 if (!stop_signal && Threads.stop) {
153 // Signal the cluster about stopping
155 MPI_Ibarrier(StopComm, &reqStop);
156 MPI_Wait(&reqStop, MPI_STATUS_IGNORE);
161 // Check if we've received any stop signal
162 MPI_Test(&reqStop, &flagStop, MPI_STATUS_IGNORE);
176 void save(Thread* thread, TTEntry* tte,
177 Key k, Value v, Bound b, Depth d, Move m, Value ev) {
179 tte->save(k, v, b, d, m, ev);
183 // Try to add to thread's send buffer
185 std::lock_guard<Mutex> lk(thread->ttBuffer.mutex);
186 thread->ttBuffer.buffer.replace(KeyedTTEntry(k,*tte));
189 // Communicate on main search thread
190 if (thread == Threads.main()) {
191 static MPI_Request req = MPI_REQUEST_NULL;
192 static TTSendBuffer<TTSendBufferSize> send_buff = {};
195 TTEntry* replace_tte;
197 // Test communication status
198 MPI_Test(&req, &flag, MPI_STATUS_IGNORE);
200 // Current communication is complete
202 // Save all recieved entries
203 for (auto&& e : TTBuff) {
204 replace_tte = TT.probe(e.first, found);
205 replace_tte->save(e.first, e.second.value(), e.second.bound(), e.second.depth(),
206 e.second.move(), e.second.eval());
212 // Build up new send buffer: best 16 found across all threads
213 for (auto&& th : Threads) {
214 std::lock_guard<Mutex> lk(th->ttBuffer.mutex);
215 for (auto&& e : th->ttBuffer.buffer)
216 send_buff.replace(e);
217 // Reset thread's send buffer
218 th->ttBuffer.buffer = {};
221 // Start next communication
222 MPI_Iallgather(send_buff.data(), send_buff.size(), TTEntryDatatype,
223 TTBuff.data(), TTSendBufferSize, TTEntryDatatype,
230 void pick_moves(MoveInfo& mi) {
231 MoveInfo* pMoveInfo = NULL;
233 pMoveInfo = (MoveInfo*)malloc(sizeof(MoveInfo) * size());
235 MPI_Gather(&mi, 1, MIDatatype, pMoveInfo, 1, MIDatatype, 0, MoveComm);
237 std::map<int, int> votes;
238 int minScore = pMoveInfo[0].score;
239 for (int i = 0; i < size(); i++) {
240 minScore = std::min(minScore, pMoveInfo[i].score);
241 votes[pMoveInfo[i].move] = 0;
243 for (int i = 0; i < size(); i++) {
244 votes[pMoveInfo[i].move] += pMoveInfo[i].score - minScore + pMoveInfo[i].depth;
246 int bestVote = votes[pMoveInfo[0].move];
247 for (int i = 0; i < size(); i++) {
248 if (votes[pMoveInfo[i].move] > bestVote) {
249 bestVote = votes[pMoveInfo[i].move];
255 MPI_Bcast(&mi, 1, MIDatatype, 0, MoveComm);