Skip to content

Commit

Permalink
Linux cpu info support (using lspci)
Browse files Browse the repository at this point in the history
  • Loading branch information
grantila committed Feb 28, 2016
1 parent 0d1a2b1 commit 85757a4
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 7 deletions.
102 changes: 102 additions & 0 deletions libs/q/src/detail/cpu_linux.cpp
Original file line number Diff line number Diff line change
@@ -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 <stdlib.h>
#include <algorithm>
#include <iostream>
#include <regex>
#include <thread>

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
86 changes: 86 additions & 0 deletions libs/q/src/detail/exec.hpp
Original file line number Diff line number Diff line change
@@ -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 <q/pp.hpp>

#ifdef LIBQ_ON_LINUX

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <string>

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
12 changes: 6 additions & 6 deletions libs/q/src/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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( );
Expand All @@ -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
Expand All @@ -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
Expand Down
5 changes: 4 additions & 1 deletion progs/playground/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down

0 comments on commit 85757a4

Please sign in to comment.