piranha  0.10
runtime_info.hpp
1 /* Copyright 2009-2017 Francesco Biscani (bluescarni@gmail.com)
2 
3 This file is part of the Piranha library.
4 
5 The Piranha library is free software; you can redistribute it and/or modify
6 it under the terms of either:
7 
8  * the GNU Lesser General Public License as published by the Free
9  Software Foundation; either version 3 of the License, or (at your
10  option) any later version.
11 
12 or
13 
14  * the GNU General Public License as published by the Free Software
15  Foundation; either version 3 of the License, or (at your option) any
16  later version.
17 
18 or both in parallel, as here.
19 
20 The Piranha library is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 for more details.
24 
25 You should have received copies of the GNU General Public License and the
26 GNU Lesser General Public License along with the Piranha library. If not,
27 see https://www.gnu.org/licenses/. */
28 
29 #ifndef PIRANHA_RUNTIME_INFO_HPP
30 #define PIRANHA_RUNTIME_INFO_HPP
31 
32 #if defined(__linux__)
33 
34 #include <boost/lexical_cast.hpp>
35 #include <fstream>
36 #include <iostream>
37 #include <string>
38 
39 extern "C" {
40 #include <sys/sysinfo.h>
41 #include <unistd.h>
42 }
43 
44 #elif defined(__FreeBSD__)
45 
46 extern "C" {
47 #include <sys/sysctl.h>
48 #include <sys/types.h>
49 }
50 #include <cstddef>
51 
52 #elif defined(_WIN32)
53 
54 extern "C" {
55 #include <Windows.h>
56 }
57 #include <cstddef>
58 #include <cstdlib>
59 #include <new>
60 
61 #elif defined(__APPLE_CC__)
62 
63 #include <cstddef>
64 
65 extern "C" {
66 #include <sys/sysctl.h>
67 #include <unistd.h>
68 }
69 
70 #endif
71 
72 #include <memory>
73 #include <thread>
74 
75 #include <piranha/config.hpp>
76 #include <piranha/exceptions.hpp>
77 #include <piranha/runtime_info.hpp>
78 #include <piranha/safe_cast.hpp>
79 
80 namespace piranha
81 {
82 
84 
88 {
89 
90 public:
92 
96  static unsigned get_hardware_concurrency()
97  {
98 #if defined(__linux__)
99  int candidate = ::get_nprocs();
100  return (candidate <= 0) ? 0u : static_cast<unsigned>(candidate);
101 #elif defined(__FreeBSD__)
102  int count;
103  std::size_t size = sizeof(count);
104  return ::sysctlbyname("hw.ncpu", &count, &size, NULL, 0) ? 0u : static_cast<unsigned>(count);
105 #elif defined(_WIN32)
106  ::SYSTEM_INFO info = ::SYSTEM_INFO();
107  ::GetSystemInfo(&info);
108  // info.dwNumberOfProcessors is a dword, i.e., a long unsigned integer.
109  // http://msdn.microsoft.com/en-us/library/cc230318(v=prot.10).aspx
110  unsigned retval = 0u;
111  try {
112  retval = safe_cast<unsigned>(info.dwNumberOfProcessors);
113  } catch (...) {
114  // Do not do anything, just keep zero.
115  }
116  return retval;
117 #elif defined(__APPLE_CC__)
118  try {
119  return safe_cast<unsigned>(::sysconf(_SC_NPROCESSORS_ONLN));
120  } catch (...) {
121  return 0u;
122  }
123 #else
124  // Try the standard C++ version. Will return 0 in case the value cannot be
125  // determined.
126  return std::thread::hardware_concurrency();
127 #endif
128  }
130 
133  static unsigned get_cache_line_size()
134  {
135 #if defined(__linux__)
136  auto ls = ::sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
137  // This can fail on some systems, resort to try reading the /sys entry.
138  // NOTE: here we could iterate over all cpus and take the maximum cache line size.
139  if (!ls) {
140  std::ifstream sys_file("/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size");
141  if (sys_file.is_open() && sys_file.good()) {
142  try {
143  std::string line;
144  std::getline(sys_file, line);
145  ls = boost::lexical_cast<decltype(ls)>(line);
146  } catch (...) {
147  }
148  }
149  }
150  if (ls > 0) {
151  unsigned retval = 0u;
152  try {
153  retval = safe_cast<unsigned>(ls);
154  } catch (...) {
155  }
156  return retval;
157  } else {
158  return 0u;
159  }
160 #elif defined(_WIN32) && defined(PIRANHA_HAVE_SYSTEM_LOGICAL_PROCESSOR_INFORMATION)
161  // Adapted from:
162  // http://strupat.ca/2010/10/cross-platform-function-to-get-the-line-size-of-your-cache
163  std::size_t line_size = 0u;
164  ::DWORD buffer_size = 0u;
165  ::SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buffer = 0;
166  // This is expected to fail, with a specific error code.
167  const auto retval = ::GetLogicalProcessorInformation(0, &buffer_size);
168  if (retval || ::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
169  return 0u;
170  }
171  try {
172  // NOTE: here probably it is the only place were we avoid calling a positional new on a memory
173  // buffer. The reason is that the documentation states that GetLogicalProcessorInformation needs a
174  // "buffer" as first argument (here interpreted as "raw storage"), and also the example uses this idiom:
175  // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683194(v=vs.85).aspx
176  // In general, C++ prohibits to write directly to the output of malloc(), as no object has been created
177  // by the malloc call:
178  // http://en.cppreference.com/w/cpp/language/default_constructor#Trivial_default_constructor
179  // http://stackoverflow.com/questions/40873520/reinterpret-cast-creating-a-trivially-default-constructible-object
180  // http://wg21.link/p0137
181  buffer = (::SYSTEM_LOGICAL_PROCESSOR_INFORMATION *)std::malloc(safe_cast<std::size_t>(buffer_size));
182  if (unlikely(!buffer)) {
183  piranha_throw(std::bad_alloc, );
184  }
185  if (!::GetLogicalProcessorInformation(&buffer[0u], &buffer_size)) {
186  std::free(buffer);
187  return 0u;
188  }
189  for (::DWORD i = 0u; i != buffer_size / sizeof(::SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ++i) {
190  if (buffer[i].Relationship == ::_LOGICAL_PROCESSOR_RELATIONSHIP::RelationCache
191  && buffer[i].Cache.Level == 1) {
192  line_size = buffer[i].Cache.LineSize;
193  break;
194  }
195  }
196  std::free(buffer);
197  return safe_cast<unsigned>(line_size);
198  } catch (...) {
199  }
200  return 0u;
201 #elif defined(__APPLE_CC__)
202  // NOTE: apparently here we are expecting a std::size_t because the docs say
203  // that size_t is used to represent sizes, and we are getting the size of the cache line.
204  // Compare to the usage above for the FreeBSD cpu count, were we expect ints.
205  std::size_t ls, size = sizeof(ls);
206  try {
207  return ::sysctlbyname("hw.cachelinesize", &ls, &size, NULL, 0) ? 0u : safe_cast<unsigned>(ls);
208  } catch (...) {
209  return 0u;
210  }
211 #else
212  // TODO: FreeBSD, etc.?
213  return 0u;
214 #endif
215  }
216 };
217 }
218 
219 #endif
static unsigned get_cache_line_size()
Size of the data cache line.
Exceptions.
#define piranha_throw(exception_type,...)
Exception-throwing macro.
Definition: exceptions.hpp:118
Root piranha namespace.
Definition: array_key.hpp:52
static unsigned get_hardware_concurrency()
Hardware concurrency.
Runtime information.
safe_cast_type< To, From > safe_cast(const From &x)
Safe cast.
Definition: safe_cast.hpp:219