diff --git a/doc/scoped_thread.qbk b/doc/scoped_thread.qbk index 938f6508..0a8fd964 100644 --- a/doc/scoped_thread.qbk +++ b/doc/scoped_thread.qbk @@ -216,6 +216,7 @@ This wrapper can be used to join the thread before destroying it seems a natural void detach(); static unsigned hardware_concurrency() noexcept; + static unsigned physical_concurrency() noexcept; typedef thread::native_handle_type native_handle_type; native_handle_type native_handle(); @@ -458,6 +459,20 @@ any) to `*this`. [endsect] + +[section:physical_concurrency Static member function `physical_concurrency()`] + + unsigned physical_concurrency() noexecpt; + +[variablelist + +[[Effects:] [Equivalent to return `thread::physical_concurrency()`.]] + +] + +[endsect] + + [section:nativehandle Member function `native_handle()`] typedef thread::native_handle_type native_handle_type; diff --git a/doc/thread_ref.qbk b/doc/thread_ref.qbk index e0060165..a950507e 100644 --- a/doc/thread_ref.qbk +++ b/doc/thread_ref.qbk @@ -470,6 +470,7 @@ This behavior is incompatible with the current Boost.Thread design, so the use o void detach(); static unsigned hardware_concurrency() noexcept; + static unsigned physical_concurrency() noexcept; typedef platform-specific-type native_handle_type; native_handle_type native_handle(); @@ -991,6 +992,21 @@ or 0 if this information is not available.]] [endsect] +[section:physical_concurrency Static member function `physical_concurrency()`] + + unsigned physical_concurrency() noexecpt; + +[variablelist + +[[Returns:] [The number of physical cores available on the current system. In contrast to `hardware_concurrency()` it does not return + the number of virtual cores, but it counts only physical cores.]] + +[[Throws:] [Nothing]] + +] + +[endsect] + [section:nativehandle Member function `native_handle()`] typedef platform-specific-type native_handle_type; diff --git a/include/boost/thread/detail/thread.hpp b/include/boost/thread/detail/thread.hpp index a6e2da30..019d0d73 100644 --- a/include/boost/thread/detail/thread.hpp +++ b/include/boost/thread/detail/thread.hpp @@ -546,6 +546,7 @@ namespace boost void detach(); static unsigned hardware_concurrency() BOOST_NOEXCEPT; + static unsigned physical_concurrency() BOOST_NOEXCEPT; #define BOOST_THREAD_DEFINES_THREAD_NATIVE_HANDLE typedef detail::thread_data_base::native_handle_type native_handle_type; diff --git a/include/boost/thread/scoped_thread.hpp b/include/boost/thread/scoped_thread.hpp index e7a00f69..d7fbabd8 100644 --- a/include/boost/thread/scoped_thread.hpp +++ b/include/boost/thread/scoped_thread.hpp @@ -261,11 +261,15 @@ namespace boost } #endif - static unsigned hardware_concurrency()BOOST_NOEXCEPT + static unsigned hardware_concurrency() BOOST_NOEXCEPT { return thread::hardware_concurrency(); } + static unsigned physical_concurrency() BOOST_NOEXCEPT + { + return thread::physical_concurrency(); + } }; /** diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index b7f830c1..39d476a4 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -27,6 +27,15 @@ #include #endif +#include +#include +#include + +#include +#include +#include +#include + #include "./timeconv.inl" namespace boost @@ -543,6 +552,56 @@ namespace boost #endif } + unsigned thread::physical_concurrency() BOOST_NOEXCEPT + { +#ifdef __linux__ + try { + using namespace std; + + ifstream proc_cpuinfo ("/proc/cpuinfo"); + + unsigned current_processor = 0; + const string physical_id("physical id"), core_id("core id"); + + typedef std::pair core_entry; // [physical ID, core id] + + std::set cores; + + core_entry current_core_entry; + + for (string line; getline(proc_cpuinfo, line); ) { + vector key_val(2); + boost::split(key_val, line, boost::is_any_of(":")); + + string key = key_val[0]; + string value = key_val[1]; + boost::trim(key); + boost::trim(value); + + if (key == physical_id) { + current_core_entry.first = boost::lexical_cast(value); + continue; + } + + if (key == core_id) { + current_core_entry.second = boost::lexical_cast(value); + cores.insert(current_core_entry); + continue; + } + } + return cores.size(); + } catch(...) { + return 0; + } +#elif defined(__APPLE__) + int count; + size_t size=sizeof(count); + return sysctlbyname("hw.physicalcpu",&count,&size,NULL,0)?0:count; +#else + return hardware_concurrency(); +#endif + } + #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS void thread::interrupt() { diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index bc4a45ed..640664ef 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -407,6 +407,8 @@ namespace boost return local_thread_info.get() && (detail::win32::WaitForSingleObject(local_thread_info->interruption_handle,0)==0); } +#endif + unsigned thread::hardware_concurrency() BOOST_NOEXCEPT { //SYSTEM_INFO info={{0}}; @@ -414,7 +416,28 @@ namespace boost GetSystemInfo(&info); return info.dwNumberOfProcessors; } -#endif + + unsigned thread::physical_concurrency() BOOST_NOEXCEPT + { + unsigned cores = 0; + DWORD size = 0; + + GetLogicalProcessorInformation(NULL, &size); + if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) + return 0; + + std::vector buffer(size); + if (GetLogicalProcessorInformation(buffer.data(), &size) == FALSE) + return 0; + + const size_t Elements = size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + + for (size_t i = 0; i < Elements; ++i) { + if (buffer[i].Relationship == RelationProcessorCore) + ++cores; + } + return cores; + } thread::native_handle_type thread::native_handle() { diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 2e69786e..9c0f93f7 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -209,6 +209,7 @@ rule thread-compile ( sources : reqs * : name ) [ thread-test test_thread.cpp ] [ thread-test test_thread_id.cpp ] [ thread-test test_hardware_concurrency.cpp ] + [ thread-test test_physical_concurrency.cpp ] [ thread-test test_thread_move.cpp ] [ thread-test test_thread_return_local.cpp ] [ thread-test test_thread_move_return.cpp ] diff --git a/test/test_physical_concurrency.cpp b/test/test_physical_concurrency.cpp new file mode 100644 index 00000000..20b5dc02 --- /dev/null +++ b/test/test_physical_concurrency.cpp @@ -0,0 +1,24 @@ +// Copyright (C) 2007 Anthony Williams +// Copyright (C) 2013 Tim Blechmann +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#include +#include +#include + +void test_physical_concurrency_is_non_zero() +{ + BOOST_CHECK(boost::thread::physical_concurrency()!=0); +} + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Threads: physical concurrency test suite"); + + test->add(BOOST_TEST_CASE(test_physical_concurrency_is_non_zero)); + return test; +} + +