3
\$\begingroup\$

I want to just write a simple function to check if my linux box has hyperthreading enabled. I want to know if there's a simpler way to produce the cpuinfo dict (would be great if I could use a dict comprehension somehow) or generally a better way to check this information:

def is_hyperthreading_enabled():
    cpuinfo = dict(map(str.strip, line.split(':'))
                   for line in open('/proc/cpuinfo')
                   if ':' in line)

    return cpuinfo['siblings'] != cpuinfo['cpu cores']
\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

First off, you should always close a file in Python.
There are two ways to do that.

  1. The old style way:

    file_handler = open('/proc/cpuinfo')
    // Your code here
    file_handler.close()
  2. The 'new' way with with.

    with open('/proc/cpuinfo') as file_handler:
        // Your code here

The second is more recommended as then it's both clear when the file is open, and you don't forget to close it.


While /proc/cpuinfo is unlikely to have more than one : per line, it could happen. As it seems model name can be whatever the model is called. If a vendor were to put a : in their name, your code won't work. with the exception:

ValueError: dictionary update sequence element #1 has length 3; 2 is required

To avoid this as an issue, you can use the optional second argument to str.split(':', 1).


If you want to use a dictionary comprehension at all cost. Then it's the same as your above code, but it will have a dictionary comprehension surrounding it.

with open('/proc/cpuinfo') as file_handler:
    cpuinfo = {
        key: value
        for key, value in (
            map(str.strip, line.split(':', 1))
            for line in file_handler
            if ':' in line
        )
    }

This is in my opinion worse than your above code, as it's common knowledge that dict([['key', 'value']]) can be used to make a dict, which was added in 2.2. Where dictionary comprehensions were added in 2.7.
And your original code is a lot less verbose.


You could however use a functional approach to this. Which has the drawback of needing to define line twice, and is more verbose than the original code.

with open('/proc/cpuinfo') as file_handler:
    cpuinfo = dict(map(
        lambda line: map(str.strip, line.split(':', 1)),
        filter(lambda line: ':' in line, file_handler)
    ))

Overall I would recommend using your original code, but with with and line.split(':', 1)) added.

Just to note, ':' in line returns false every time the CPU core changes. For example from my /proc/info. (note I removed the flags)

processor   : 0
vendor_id   : GenuineIntel
cpu family  : 6
model       : 42
model name  : Intel(R) Core(TM) i5-2500 CPU @ 3.30GHz
stepping    : 7
microcode   : 0x28
cpu MHz     : 1728.761
cache size  : 6144 KB
physical id : 0
siblings    : 4
core id     : 0
cpu cores   : 4
apicid      : 0
initial apicid  : 0
fpu     : yes
fpu_exception   : yes
cpuid level : 13
wp      : yes
bugs        :
bogomips    : 6800.31
clflush size    : 64
cache_alignment : 64
address sizes   : 36 bits physical, 48 bits virtual
power management:

processor   : 1
vendor_id   : GenuineIntel
cpu family  : 6
model       : 42
model name  : Intel(R) Core(TM) i5-2500 CPU @ 3.30GHz
stepping    : 7
microcode   : 0x28
cpu MHz     : 1686.609
cache size  : 6144 KB
physical id : 0
siblings    : 4
core id     : 1
cpu cores   : 4
apicid      : 2
initial apicid  : 2
fpu     : yes
fpu_exception   : yes
cpuid level : 13
wp      : yes
bugs        :
bogomips    : 6800.31
clflush size    : 64
cache_alignment : 64
address sizes   : 36 bits physical, 48 bits virtual
power management:

This also shows that the information can change. E.g. cpu MHz:1728.761 and cpu MHz:1686.609. So you may not want to overwrite some of this information. Also if you have more than one CPU (say you are using a sever) then you could return false on one CPU and true on another, but as the last core overwrites all the previous data it's somewhat up to chance to know.

Due to this I would recommend that you change your function to make cpuinfo a list of dicts.

Finally as far as I know unless you read the entire file and split on \n\n then there is no way to make it a comprehension. And so I would recommend:

def is_hyperthreading_enabled():
    with open('/proc/cpuinfo') as file_handler:
        cpuinfo = []
        temp_dict = {}
        for line in file_handler:
            try:
                key, value = map(str.strip, line.split(':', 1))
            except ValueError:
                cpuinfo.append(temp_dict)
                temp_dict = {}
            else:
                temp_dict[key] = value

    return any(map(lambda core: core['siblings'] != core['cpu cores'], cpuinfo))

Note, the above works as I make the assumption that the file always ends with an empty line. Otherwise you would be missing one of the cores. This assumption seems to be true, as I tested if /proc/cpuinfo ends with an empty line on all a single; quad-core, dual-core and single core, CPU.
If you think this is too small a test case, you can add the following after the for loop.

if temp_dict:
    cpuinfo.append(temp_dict)

So if you only care about one core, your way is best. If you want to test if any CPU can, then you would want to use the above. And if you want to know if all of them can then you should change any to all in the above.

\$\endgroup\$
7
  • \$\begingroup\$ Is there really such thing as one CPU hyperthreaded and not the others? \$\endgroup\$ Commented Oct 31, 2015 at 21:36
  • \$\begingroup\$ @MathiasEttinger I think you got mixed up with cores and CPU's. (I could be wrong). One CPU can have multiple cores, e.g. I showed 2 of my 4 cores. But I have 1 CPU. For a quick example say we have a 'Intel Xeon E7-8891 v3'. It has 10 cores, and so there would be 10 entry's in cpuinfo. However it can run with 7 other CPU's. For example you could have two 'E7-8891 v3's on a 'ASUS Z9PE-D8 WS' (if the have the same sockets) which supports 2 CPU's. So I would assume if you are minted, you could have 2 CPU's where one has hyperthreading where the other one does not. \$\endgroup\$
    – Peilonrayz
    Commented Oct 31, 2015 at 22:20
  • \$\begingroup\$ Oh, right! You are correct I messed up cores and CPUs... \$\endgroup\$ Commented Oct 31, 2015 at 22:23
  • \$\begingroup\$ @MathiasEttinger No problem, we all make mistakes, I'm sure half of what I said is wrong to some extent. \$\endgroup\$
    – Peilonrayz
    Commented Oct 31, 2015 at 22:36
  • \$\begingroup\$ In your last code extract, how does the final dictionary get added to the cpuinfo list? It looks as if you are relying on a blank line appearing at the end of the file. \$\endgroup\$ Commented Nov 1, 2015 at 13:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.