Skip to content

Commit 7943fec

Browse files
committedOct 13, 2016
Add interface to compute number of physical cores on host system
Summary: For now I have only added support for x86_64 Linux, but other systems can be added incrementally. This is to be used for setting the default parallelism for ThinLTO backends (instead of thread::hardware_concurrency which includes hyperthreading and is too aggressive). I'll send this as a follow-on patch, and it will fall back to hardware_concurrency when the new getHostNumPhysicalCores returns -1 (when not supported for a given host system). I also added an interface to MemoryBuffer to force reading a file as a stream - this is required for /proc/cpuinfo which is a special file that looks like a normal file but appears to have 0 size. The existing readers of this file in Host.cpp are reading the first 1024 or so bytes from it, because the necessary info is near the top. But for the new functionality we need to be able to read the entire file. I can go back and change the other readers to use the new getFileAsStream as a follow-on patch since it seems much more robust. Added a unittest. Reviewers: mehdi_amini Subscribers: beanz, mgorny, llvm-commits, modocache Differential Revision: https://reviews.llvm.org/D25564 llvm-svn: 284138
1 parent 02c3e8c commit 7943fec

File tree

6 files changed

+128
-1
lines changed

6 files changed

+128
-1
lines changed
 

‎llvm/include/llvm/Support/Host.h

+5
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ namespace sys {
7070
///
7171
/// \return - True on success.
7272
bool getHostCPUFeatures(StringMap<bool> &Features);
73+
74+
/// Get the number of physical cores (as opposed to logical cores returned
75+
/// from thread::hardware_concurrency(), which includes hyperthreads).
76+
/// Returns -1 if unknown for the current host system.
77+
int getHostNumPhysicalCores();
7378
}
7479
}
7580

‎llvm/include/llvm/Support/MemoryBuffer.h

+6
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ class MemoryBuffer {
7070
getFile(const Twine &Filename, int64_t FileSize = -1,
7171
bool RequiresNullTerminator = true, bool IsVolatileSize = false);
7272

73+
/// Read all of the specified file into a MemoryBuffer as a stream
74+
/// (i.e. until EOF reached). This is useful for special files that
75+
/// look like a regular file but have 0 size (e.g. /proc/cpuinfo on Linux).
76+
static ErrorOr<std::unique_ptr<MemoryBuffer>>
77+
getFileAsStream(const Twine &Filename);
78+
7379
/// Given an already-open file descriptor, map some slice of it into a
7480
/// MemoryBuffer. The slice is specified by an \p Offset and \p MapSize.
7581
/// Since this is in the middle of a file, the buffer is not null terminated.

‎llvm/lib/Support/Host.cpp

+57-1
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,18 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
#include "llvm/Support/Host.h"
15+
#include "llvm/ADT/SmallSet.h"
1516
#include "llvm/ADT/SmallVector.h"
1617
#include "llvm/ADT/StringRef.h"
1718
#include "llvm/ADT/StringSwitch.h"
1819
#include "llvm/ADT/Triple.h"
1920
#include "llvm/Config/config.h"
2021
#include "llvm/Support/Debug.h"
2122
#include "llvm/Support/FileSystem.h"
23+
#include "llvm/Support/MemoryBuffer.h"
2224
#include "llvm/Support/raw_ostream.h"
23-
#include <string.h>
2425
#include <assert.h>
26+
#include <string.h>
2527

2628
// Include the platform-specific parts of this class.
2729
#ifdef LLVM_ON_UNIX
@@ -1188,6 +1190,60 @@ StringRef sys::getHostCPUName() {
11881190
StringRef sys::getHostCPUName() { return "generic"; }
11891191
#endif
11901192

1193+
#if defined(__linux__) && defined(__x86_64__)
1194+
// On Linux, the number of physical cores can be computed from /proc/cpuinfo,
1195+
// using the number of unique physical/core id pairs. The following
1196+
// implementation reads the /proc/cpuinfo format on an x86_64 system.
1197+
int computeHostNumPhysicalCores() {
1198+
// Read /proc/cpuinfo as a stream (until EOF reached). It cannot be
1199+
// mmapped because it appears to have 0 size.
1200+
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
1201+
llvm::MemoryBuffer::getFileAsStream("/proc/cpuinfo");
1202+
if (std::error_code EC = Text.getError()) {
1203+
llvm::errs() << "Can't read "
1204+
<< "/proc/cpuinfo: " << EC.message() << "\n";
1205+
}
1206+
SmallVector<StringRef, 8> strs;
1207+
(*Text)->getBuffer().split(strs, "\n", /*MaxSplit=*/-1,
1208+
/*KeepEmpty=*/false);
1209+
int CurPhysicalId = -1;
1210+
int CurCoreId = -1;
1211+
SmallSet<std::pair<int, int>, 32> UniqueItems;
1212+
for (auto &Line : strs) {
1213+
Line = Line.trim();
1214+
if (!Line.startswith("physical id") && !Line.startswith("core id"))
1215+
continue;
1216+
std::pair<StringRef, StringRef> Data = Line.split(':');
1217+
auto Name = Data.first.trim();
1218+
auto Val = Data.second.trim();
1219+
if (Name == "physical id") {
1220+
assert(CurPhysicalId == -1 &&
1221+
"Expected a core id before seeing another physical id");
1222+
Val.getAsInteger(10, CurPhysicalId);
1223+
}
1224+
if (Name == "core id") {
1225+
assert(CurCoreId == -1 &&
1226+
"Expected a physical id before seeing another core id");
1227+
Val.getAsInteger(10, CurCoreId);
1228+
}
1229+
if (CurPhysicalId != -1 && CurCoreId != -1) {
1230+
UniqueItems.insert(std::make_pair(CurPhysicalId, CurCoreId));
1231+
CurPhysicalId = -1;
1232+
CurCoreId = -1;
1233+
}
1234+
}
1235+
return UniqueItems.size();
1236+
}
1237+
#else
1238+
// On other systems, return -1 to indicate unknown.
1239+
int computeHostNumPhysicalCores() { return -1; }
1240+
#endif
1241+
1242+
int sys::getHostNumPhysicalCores() {
1243+
static int NumCores = computeHostNumPhysicalCores();
1244+
return NumCores;
1245+
}
1246+
11911247
#if defined(__i386__) || defined(_M_IX86) || \
11921248
defined(__x86_64__) || defined(_M_X64)
11931249
bool sys::getHostCPUFeatures(StringMap<bool> &Features) {

‎llvm/lib/Support/MemoryBuffer.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,18 @@ ErrorOr<std::unique_ptr<MemoryBuffer>> MemoryBuffer::getSTDIN() {
438438
return getMemoryBufferForStream(0, "<stdin>");
439439
}
440440

441+
ErrorOr<std::unique_ptr<MemoryBuffer>>
442+
MemoryBuffer::getFileAsStream(const Twine &Filename) {
443+
int FD;
444+
std::error_code EC = sys::fs::openFileForRead(Filename, FD);
445+
if (EC)
446+
return EC;
447+
ErrorOr<std::unique_ptr<MemoryBuffer>> Ret =
448+
getMemoryBufferForStream(FD, Filename);
449+
close(FD);
450+
return Ret;
451+
}
452+
441453
MemoryBufferRef MemoryBuffer::getMemBufferRef() const {
442454
StringRef Data = getBuffer();
443455
StringRef Identifier = getBufferIdentifier();

‎llvm/unittests/Support/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ add_llvm_unittest(SupportTests
1919
ErrorTest.cpp
2020
ErrorOrTest.cpp
2121
FileOutputBufferTest.cpp
22+
Host.cpp
2223
LEB128Test.cpp
2324
LineIteratorTest.cpp
2425
LockFileManagerTest.cpp

‎llvm/unittests/Support/Host.cpp

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//========- unittests/Support/Host.cpp - Host.cpp tests --------------========//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#include "llvm/Support/Host.h"
11+
#include "llvm/ADT/SmallVector.h"
12+
#include "llvm/ADT/Triple.h"
13+
14+
#include "gtest/gtest.h"
15+
16+
using namespace llvm;
17+
18+
class HostTest : public testing::Test {
19+
Triple Host;
20+
SmallVector<std::pair<Triple::ArchType, Triple::OSType>, 4> SupportedArchAndOSs;
21+
22+
protected:
23+
bool isSupportedArchAndOS() {
24+
if (is_contained(SupportedArchAndOSs, std::make_pair(Host.getArch(), Host.getOS())))
25+
return true;
26+
27+
return false;
28+
}
29+
30+
HostTest() {
31+
Host.setTriple(Triple::normalize(sys::getProcessTriple()));
32+
33+
// Initially this is only testing detection of the number of
34+
// physical cores, which is currently only supported for
35+
// x86_64 Linux.
36+
SupportedArchAndOSs.push_back(std::make_pair(Triple::x86_64, Triple::Linux));
37+
}
38+
};
39+
40+
TEST_F(HostTest, NumPhysicalCores) {
41+
int Num = sys::getHostNumPhysicalCores();
42+
43+
if (isSupportedArchAndOS())
44+
ASSERT_GT(Num, 0);
45+
else
46+
ASSERT_EQ(Num, -1);
47+
}

0 commit comments

Comments
 (0)
Please sign in to comment.