CPU の定格クロックを取得する(IA-32,Intel 64 限定)

Intel CPU の場合 cpuid 命令で取得できる諸情報のうち processor brand string という CPU のブランド名を取得する命令を使うことによって,このブランド名文字列の末尾にある文字列を見れば良い。

実のところ /proc/cpuinfo の model name の行を見るのと同じだが,Linux 以外でも使えるなど色々あって /proc を舐めたくないときがあります(本当?)
#include <cstdint>
#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>

// CPU 周波数を processor brand string に記述されている周波数から取得する
int main(int argc, char* argv[])
{
    struct reg{
        uint32_t eax,ebx,ecx,edx;
    } r;

    std::string processor_brand_string = {};
    // brand string を取得して processor_brand_string へ格納
    __asm__ volatile("cpuid" : "=a"(r.eax), "=b"(r.ebx), "=c"(r.ecx), "=d"(r.edx)
                             : "a"(0x80000002), "c"(0x0));
    processor_brand_string.append(reinterpret_cast(&r), 16);
    __asm__ volatile("cpuid" : "=a"(r.eax), "=b"(r.ebx), "=c"(r.ecx), "=d"(r.edx)
                             : "a"(0x80000003), "c"(0x0));
    processor_brand_string.append(reinterpret_cast(&r), 16);
    __asm__ volatile("cpuid" : "=a"(r.eax), "=b"(r.ebx), "=c"(r.ecx), "=d"(r.edx)
                             : "a"(0x80000004), "c"(0x0));
    processor_brand_string.append(reinterpret_cast(&r), 16);

    std::printf("%s\n", processor_brand_string.c_str());

    // 周波数の単位を取得し,ついでに文字列の周波数の部分の終わりの位置を取得
    std::string freq_unit_strings[3] = {"KHz", "GHz", "THz"};
    int freq_unit = 0;
    std::size_t epos = 0;
    for ( int i=0; i<3; ++i ) {
        auto r = std::search(processor_brand_string.begin(), processor_brand_string.end(), freq_unit_strings[i].begin(), freq_unit_strings[i].end());
        if ( r != processor_brand_string.end() ) {
            freq_unit = i;
            epos = std::distance(processor_brand_string.begin(), r);
        }
    }

    // 文字列の周波数が書かれている始まりの位置を取得
    auto spos = processor_brand_string.rfind(" ");

    try {
        float freq = std::stof(processor_brand_string.substr(spos,epos));
        std::printf("%.2f%s\n", freq, freq_unit_strings[freq_unit].c_str());
    } catch (const std::invalid_argument& e) {
        std::puts("invalid argument");
    } catch (const std::out_of_range& e) {
        std::puts("out of range");
    }

    return EXIT_SUCCESS;
}
これを実行すると次のように出力される
$ ./a.out
Intel(R) Core(TM) i7-6567U CPU @ 3.30GHz
3.30GHz
ちなみに,processor brand string から周波数の数値を走査するところはわりとズルしており,最後のスペースの後ろに周波数の文字列があることを決め打ちしているのでよくないですが,そうじゃない processor brand string を知らないので許してください。

とはいえいまの Intel CPU で GHz 以外を考慮する必要はあまりない気もします。

このブログの人気の投稿

GRUBとEFIの組み合わせで使うときのメモ またはEFI全般のtips

prnというフォルダが作れない理由がビル・ゲイツのあだ名というのはガセである件

BIOS+MBRなWindows7/8を、データコピーやデータ移動まったくなしで UEFI+GPTな環境に移行する