diff --git a/libs/q/src/detail/cpu_linux.cpp b/libs/q/src/detail/cpu_linux.cpp new file mode 100644 index 0000000..9e2ad65 --- /dev/null +++ b/libs/q/src/detail/cpu_linux.cpp @@ -0,0 +1,102 @@ +/* + * Copyright 2016 Gustaf Räntilä + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cpu.hpp" + +#ifdef LIBQ_ON_LINUX + +#include "exec.hpp" +#include +#include +#include +#include +#include + +namespace q { + +namespace detail { + +// C++ regex is pretty broken, since multiline support is implementation +// dependent, so we need to create our own '^' and '$' breaks... +#define RE_BOL "[\\n\\r^]" +#define RE_EOL "[\\n\\r$]" + +cpu_info get_cpu_info( ) +{ + cpu_info info{ 0, 0, 0, { 0, 0 }, { 0, 0 }, { 0, 0 } }; + + std::string data = exec_read_all( "lscpu" ); + + if ( data.empty( ) ) + return info; + + auto to_numeric = [ ]( const std::string& s ) -> std::size_t + { + std::size_t mul = 1; + + if ( s.empty( ) ) + return 0; + + if ( s[ s.size( ) - 1 ] == 'K' ) + mul = 1024; + else if ( s[ s.size( ) - 1 ] == 'M' ) + mul = 1024 * 1024; + + auto val = ::strtoull( s.c_str( ), nullptr, 10 ); + if ( val == ULLONG_MAX ) + return 0; + return static_cast< std::size_t >( val ) * mul; + }; + + auto get_value = [ & ]( const std::string& re ) -> std::size_t + { + auto re_cpu = std::regex( RE_BOL + re + RE_EOL ); + std::smatch match; + std::regex_search( data, match, re_cpu ); + if ( match.size( ) == 2 ) + return to_numeric( match[ 1 ] ); + return 0; + }; + + auto soft_cores = get_value( + "CPU\\(s\\):[[:space:]]*([[:digit:]]+)" ); + auto tpc = std::max( std::size_t( 1 ), get_value( + "Threads\\(s\\) per core:[[:space:]]*([[:digit:]]+)" ) ); + auto l1c = get_value( + "L1d cache:[[:space:]]*([[:digit:]]+)" ); + auto l2c = get_value( + "L2 cache:[[:space:]]*([[:digit:]]+)" ); + auto l3c = get_value( + "L3 cache:[[:space:]]*([[:digit:]]+)" ); + auto processors = get_value( + "Socket\\(s\\):[[:space:]]*([[:digit:]]+)" ); + + info.soft_cores = soft_cores; + info.hard_cores = soft_cores / tpc; + info.processors = processors; + + info.level_1_cache.size = l1c; + info.level_2_cache.size = l2c; + info.level_3_cache.size = l3c; + + return info; +} + +} // namespace detail + +} // namespace q + +#endif // LIBQ_ON_LINUX diff --git a/libs/q/src/detail/exec.hpp b/libs/q/src/detail/exec.hpp new file mode 100644 index 0000000..9898a8f --- /dev/null +++ b/libs/q/src/detail/exec.hpp @@ -0,0 +1,86 @@ +/* + * Copyright 2016 Gustaf Räntilä + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifdef LIBQ_ON_LINUX + +#include +#include +#include +#include + +namespace q { + +namespace detail { + +std::string exec_read_all( const std::string& prog, std::size_t bytes = 4096 ) +{ + std::string ret( bytes, '\0' ); + + int stdout_pipe[ 2 ]; + + if ( ::pipe( stdout_pipe ) < 0 ) + return ""; + + pid_t pid; + + switch( pid = ::fork( ) ) + { + case -1: + ::close( stdout_pipe[ 0 ] ); + ::close( stdout_pipe[ 1 ] ); + return ""; + case 0: + { + // Child + ::close( 0 ); // Close stdin + ::close( 1 ); // Close stdout + ::close( 2 ); // Close stderr + + ::close( stdout_pipe[ 0 ] ); + ::dup2( stdout_pipe[ 1 ], 1 ); + + char* const args[ 2 ] = { + ::strdup( prog.c_str( ) ), + nullptr + }; + ::execvp( args[ 0 ], args ); + // We shouldn't end up here + + ::close( stdout_pipe[ 1 ] ); + _exit( 1 ); + } + default: + // Parent + ::close( stdout_pipe[ 1 ] ); + auto count = ::read( + stdout_pipe[ 0 ], + const_cast< char* >( ret.data( ) ), + bytes - 1 ); + ret.erase( count ); + ::close( stdout_pipe[ 0 ] ); + return ret; + } + + return ""; +} + +} // namespace detail + +} // namespace q + +#endif // LIBQ_ON_LINUX diff --git a/libs/q/src/thread.cpp b/libs/q/src/thread.cpp index a3bfa61..267286b 100644 --- a/libs/q/src/thread.cpp +++ b/libs/q/src/thread.cpp @@ -47,8 +47,8 @@ std::size_t hard_cores( ) size_t count_len = sizeof( count ); ::sysctlbyname( "hw.physicalcpu_max", &count, &count_len, NULL, 0 ); return AT_LEAST_ONE( count ); -#elif defined( LIBQ_ON_WINDOWS ) - return detail::get_cpu_info( ).hard_cores; +#elif defined( LIBQ_ON_WINDOWS ) || defined( LIBQ_ON_POSIX ) + return AT_LEAST_ONE( detail::get_cpu_info( ).hard_cores ); #else // TODO: Move and change implementation. This is a decent default, nothing else return fallback_cores( ); @@ -62,8 +62,8 @@ std::size_t soft_cores( ) size_t count_len = sizeof( count ); ::sysctlbyname( "hw.logicalcpu_max", &count, &count_len, NULL, 0 ); return AT_LEAST_ONE( count ); -#elif defined( LIBQ_ON_WINDOWS ) - return detail::get_cpu_info( ).soft_cores; +#elif defined( LIBQ_ON_WINDOWS ) || defined( LIBQ_ON_POSIX ) + return AT_LEAST_ONE( detail::get_cpu_info( ).soft_cores ); #else return fallback_cores( ); #endif @@ -76,8 +76,8 @@ std::size_t processors( ) size_t count_len = sizeof( count ); ::sysctlbyname( "hw.packages", &count, &count_len, NULL, 0 ); return static_cast< std::size_t >( count ); -#elif defined( LIBQ_ON_WINDOWS ) - return detail::get_cpu_info( ).processors; +#elif defined( LIBQ_ON_WINDOWS ) || defined( LIBQ_ON_POSIX ) + return AT_LEAST_ONE( detail::get_cpu_info( ).processors ); #else return 0; #endif diff --git a/progs/playground/main.cpp b/progs/playground/main.cpp index 1ac81e0..9692e57 100644 --- a/progs/playground/main.cpp +++ b/progs/playground/main.cpp @@ -194,7 +194,10 @@ int main( int argc, char** argv ) auto q_scope = initialize( ); std::cout << "The computer has " - << q::processors( ) << " processors" << std::endl; + << q::processors( ) << " processors, " + << q::hard_cores() << " hard cores, " + << q::soft_cores() << " soft cores" + << std::endl; auto bd = q::make_shared< q::blocking_dispatcher >( "main" ); auto queue = q::make_shared< q::queue >( 0 );