Subversion Repositories Games.Chess Giants

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.     Texel - A UCI chess engine.
  3.     Copyright (C) 2014  Peter Ă–sterlund, peterosterlund2@gmail.com
  4.  
  5.     This program is free software: you can redistribute it and/or modify
  6.     it under the terms of the GNU General Public License as published by
  7.     the Free Software Foundation, either version 3 of the License, or
  8.     (at your option) any later version.
  9.  
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.  
  15.     You should have received a copy of the GNU General Public License
  16.     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17. */
  18.  
  19. /*
  20.  * numa.cpp
  21.  *
  22.  *  Created on: Jul 24, 2014
  23.  *      Author: petero
  24.  */
  25.  
  26. #include "numa.hpp"
  27. #include "util/util.hpp"
  28. #include "util/logger.hpp"
  29. #include "bitBoard.hpp"
  30.  
  31. #include <map>
  32. #include <set>
  33. #include <algorithm>
  34. #include <fstream>
  35. #include <iostream>
  36.  
  37. #ifdef NUMA
  38. #ifdef _WIN32
  39. #include <windows.h>
  40. #else
  41. #include <numa.h>
  42. #endif
  43. #endif
  44.  
  45. Numa&
  46. Numa::instance() {
  47.     static Numa numa;
  48.     return numa;
  49. }
  50.  
  51. Numa::Numa() {
  52. #ifdef NUMA
  53. #ifdef _WIN32
  54.     SYSTEM_LOGICAL_PROCESSOR_INFORMATION* buffer = nullptr;
  55.     DWORD returnLength = 0;
  56.     while (true) {
  57.         if (GetLogicalProcessorInformation(buffer, &returnLength))
  58.             break;
  59.         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
  60.             free(buffer);
  61.             buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(returnLength);
  62.             if (!buffer)
  63.                 return;
  64.         } else {
  65.             free(buffer);
  66.             return;
  67.         }
  68.     }
  69.  
  70.     int threads = 0;
  71.     int nodes = 0;
  72.     int cores = 0;
  73.     DWORD byteOffset = 0;
  74.     SYSTEM_LOGICAL_PROCESSOR_INFORMATION* ptr = buffer;
  75.     while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) {
  76.         switch (ptr->Relationship) {
  77.         case RelationNumaNode:
  78.             nodes++;
  79.             break;
  80.         case RelationProcessorCore:
  81.             cores++;
  82.             threads += BitBoard::bitCount(ptr->ProcessorMask);
  83.             break;
  84.         default:
  85.             break;
  86.         }
  87.         byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
  88.         ptr++;
  89.     }
  90.     free(buffer);
  91.  
  92.     for (int n = 0; n < nodes; n++)
  93.         for (int i = 0; i < cores / nodes; i++)
  94.             threadToNode.push_back(n);
  95.     for (int t = 0; t < threads - cores; t++)
  96.         threadToNode.push_back(t % nodes);
  97. #else
  98.     if (numa_available() == -1)
  99.         return;
  100.  
  101.     const int maxNode = numa_max_node();
  102.     if (maxNode == 0)
  103.         return;
  104.  
  105.     std::set<int> nodesToUse;
  106.     bitmask* runNodes = numa_get_run_node_mask();
  107.     int nBits = numa_bitmask_nbytes(runNodes) * 8;
  108.     for (int i = 0; i < nBits; i++)
  109.         if (numa_bitmask_isbitset(runNodes, i))
  110.             nodesToUse.insert(i);
  111.  
  112.     std::map<int, NodeInfo> nodeInfo;
  113.     std::string baseDir("/sys/devices/system/cpu");
  114.     for (int i = 0; ; i++) {
  115.         std::string cpuDir(baseDir + "/cpu" + num2Str(i));
  116.         if (i > 0) {
  117.             std::ifstream is(cpuDir + "/online");
  118.             if (!is)
  119.                 break;
  120.             std::string line;
  121.             std::getline(is, line);
  122.             if (!is || is.eof() || (line != "1"))
  123.                 continue;
  124.         }
  125.  
  126.         int node = -1;
  127.         for (int n = 0; n <= maxNode; n++) {
  128.             std::ifstream is(cpuDir + "/node" + num2Str(n));
  129.             if (is) {
  130.                 node = n;
  131.                 break;
  132.             }
  133.         }
  134.         if (node < 0)
  135.             continue;
  136.  
  137.         nodeInfo[node].node = node;
  138.         nodeInfo[node].numThreads++;
  139.  
  140.         std::ifstream is(cpuDir + "/topology/thread_siblings_list");
  141.         if (is) {
  142.             std::string line;
  143.             std::getline(is, line);
  144.             if (is && !is.eof()) {
  145.                 auto pos = line.find_first_of(",-");
  146.                 if (pos != std::string::npos)
  147.                     line = line.substr(0, pos);
  148.                 int num;
  149.                 if (str2Num(line, num)) {
  150.                     if (i == num)
  151.                         nodeInfo[node].numCores++;
  152.                 }
  153.             }
  154.         }
  155.     }
  156.  
  157.     std::vector<NodeInfo> nodes;
  158.     for (int node : nodesToUse) {
  159.         auto it = nodeInfo.find(node);
  160.         if (it != nodeInfo.end())
  161.             nodes.push_back(it->second);
  162.     }
  163.     std::sort(nodes.begin(), nodes.end(), [](const NodeInfo& a, const NodeInfo& b) {
  164.         if (a.numCores != b.numCores)
  165.             return a.numCores > b.numCores;
  166.         return a.numThreads > b.numThreads;
  167.     });
  168.  
  169.     for (const NodeInfo& ni : nodes)
  170.         for (int i = 0; i < ni.numCores; i++)
  171.             threadToNode.push_back(ni.node);
  172.  
  173.     bool done = false;
  174.     while (!done) {
  175.         done = true;
  176.         for (NodeInfo& ni : nodes) {
  177.             if (ni.numThreads > ni.numCores) {
  178.                 threadToNode.push_back(ni.node);
  179.                 ni.numThreads--;
  180.                 done = false;
  181.             }
  182.         }
  183.     }
  184. #endif
  185. #endif
  186. }
  187.  
  188. void
  189. Numa::disable() {
  190.     threadToNode.clear();
  191. }
  192.  
  193. int
  194. Numa::nodeForThread(int threadNo) const {
  195. #ifdef NUMA
  196.     if (threadNo < (int)threadToNode.size())
  197.         return threadToNode[threadNo];
  198. #endif
  199.     return -1;
  200. }
  201.  
  202. void
  203. Numa::bindThread(int threadNo) const {
  204. #ifdef NUMA
  205.     int node = nodeForThread(threadNo);
  206.     if (node < 0)
  207.         return;
  208. //    Logger::log([&](std::ostream& os){os << "threadNo:" << threadNo << " node:" << node;});
  209. #ifdef _WIN32
  210.     ULONGLONG mask;
  211.     if (GetNumaNodeProcessorMask(node, &mask))
  212.         SetThreadAffinityMask(GetCurrentThread(), mask);
  213. #else
  214.     numa_run_on_node(node);
  215.     numa_set_preferred(node);
  216. #endif
  217. #endif
  218. }
  219.  
  220. bool
  221. Numa::isMainNode(int threadNo) const {
  222.     if (threadToNode.empty())
  223.         return true; // Not NUMA hardware
  224.     return nodeForThread(threadNo) == threadToNode[0];
  225. }
  226.