Subversion Repositories Games.Chess Giants

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
99 pmbaty 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
}