web-dev-qa-db-ja.com

物理プロセッサ/コアの数をプログラムで検出するか、ハイパースレッディングがWindows、Mac、Linuxでアクティブかどうかを検出します

Windows、Mac、およびいくつかのLinuxフレーバーで実行されるマルチスレッドc ++アプリケーションがあります。

簡単に言うと、最大の効率で実行するには、物理​​プロセッサ/コアごとに単一のスレッドをインスタンス化できる必要があります。物理プロセッサ/コアよりも多くのスレッドを作成すると、プログラムのパフォーマンスが大幅に低下します。これら3つのプラットフォームすべてで、論理プロセッサ/コアの数を正しく正しく検出できます。物理プロセッサ/コアの数を正しく検出できるようにするには、ハイパートレッドがサポートされ、アクティブであるかどうかを検出する必要があります。

したがって、私の質問は、ハイパースレッディングがサポートされているかどうかを検出する方法があるかどうかですおよび有効?もしそうなら、どのように正確に。

42
HTASSCPP

編集:Intelの進行中の混乱により、これはもはや100%正確ではありません。

私が質問を理解する方法は、システム内の論理コアと物理コアの数を検出するのとは異なるCPUコア数とCPUスレッドの数を検出する方法を尋ねているということです。 CPUコアは、独自のパッケージまたはダイがない限り、OSによって物理コアと見なされないことがよくあります。そのため、OSは、たとえば、Core 2 Duoに1つの物理CPUと2つの論理CPUがあり、ハイパースレッドを備えたIntel P4は、2つのハイパースレッドと2つのCPUコアがまったく同じように報告されます賢明な別の事。

以下のソリューションをつなぎ合わせるまで、これに苦労しました。これは、AMDとIntelの両方のプロセッサで動作すると考えています。私が知る限り、そして私が間違っている可能性がある限り、AMDにはまだCPUスレッドがありませんが、CPUスレッドを持つ可能性のある将来のAMDプロセッサで動作すると思われるそれらを検出する方法を提供しました。

CPUID命令を使用した手順は次のとおりです。

  1. CPUID機能0を使用してCPUベンダーを検出
  2. CPUのHTTビット28をチェックするには、CPUID機能1からのEDXを使用します
  3. CPUID関数1からEBX [23:16]から論理コアカウントを取得します
  4. 実際の非スレッドCPUコア数を取得します
    1. ベンダー== 'GenuineIntel'の場合、これは1にCPUID機能4からのEAX [31:26]を加えたものです
    2. ベンダー== 'AuthenticAMD'の場合、これは1にCPUID機能0x80000008からのECX [7:0]を加えたものです

難しいように聞こえますが、ここでは、うまくいけば、プラットフォームに依存しない、トリックを行うC++プログラムです。

#include <iostream>
#include <string>

using namespace std;


void cpuID(unsigned i, unsigned regs[4]) {
#ifdef _WIN32
  __cpuid((int *)regs, (int)i);

#else
  asm volatile
    ("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
     : "a" (i), "c" (0));
  // ECX is set to zero for CPUID function 4
#endif
}


int main(int argc, char *argv[]) {
  unsigned regs[4];

  // Get vendor
  char vendor[12];
  cpuID(0, regs);
  ((unsigned *)vendor)[0] = regs[1]; // EBX
  ((unsigned *)vendor)[1] = regs[3]; // EDX
  ((unsigned *)vendor)[2] = regs[2]; // ECX
  string cpuVendor = string(vendor, 12);

  // Get CPU features
  cpuID(1, regs);
  unsigned cpuFeatures = regs[3]; // EDX

  // Logical core count per CPU
  cpuID(1, regs);
  unsigned logical = (regs[1] >> 16) & 0xff; // EBX[23:16]
  cout << " logical cpus: " << logical << endl;
  unsigned cores = logical;

  if (cpuVendor == "GenuineIntel") {
    // Get DCP cache info
    cpuID(4, regs);
    cores = ((regs[0] >> 26) & 0x3f) + 1; // EAX[31:26] + 1

  } else if (cpuVendor == "AuthenticAMD") {
    // Get NC: Number of CPU cores - 1
    cpuID(0x80000008, regs);
    cores = ((unsigned)(regs[2] & 0xff)) + 1; // ECX[7:0] + 1
  }

  cout << "    cpu cores: " << cores << endl;

  // Detect hyper-threads  
  bool hyperThreads = cpuFeatures & (1 << 28) && cores < logical;

  cout << "hyper-threads: " << (hyperThreads ? "true" : "false") << endl;

  return 0;
}

私は実際にこれをまだWindowsやOSXでテストしていませんが、CPUID命令はi686マシンで有効なので動作するはずです。明らかに、これはPowerPCでは機能しませんが、ハイパースレッドもありません。

次に、いくつかの異なるIntelマシンでの出力を示します。

Intel(R)Core(TM)2 Duo CPU T7500 @ 2.20GHz:

 logical cpus: 2
    cpu cores: 2
hyper-threads: false

Intel(R)Core(TM)2 Quad CPU Q8400 @ 2.66GHz:

 logical cpus: 4
    cpu cores: 4
hyper-threads: false

Intel(R)Xeon(R)CPU E5520 @ 2.27GHz(x2物理CPUパッケージ付き):

 logical cpus: 16
    cpu cores: 8
hyper-threads: true

Intel(R)Pentium(R)4 CPU 3.00GHz:

 logical cpus: 2
    cpu cores: 1
hyper-threads: true
25
jcoffland

これは、意図したとおりの物理コアの数ではなく、論理コアの数を示しています

C++ 11を使用できる場合(下のalfCのコメントのおかげ):

#include <iostream>
#include <thread>

int main() {
    std::cout << std::thread::hardware_concurrency() << std::endl;
    return 0;
}

それ以外の場合は、Boostライブラリがオプションになるかもしれません。上記と同じコードですが、インクルードが異なります。含む<boost/thread.hpp> の代わりに <thread>

17
math

ここに記載されているWindowsのみのソリューション:

GetLogicalProcessorInformation

linuxの場合、/ proc/cpuinfoファイル。今はLinuxを実行していないので、詳細を説明することはできません。物理/論理プロセッサインスタンスをカウントできます。論理カウントが物理の2倍の場合、HTが有効になっています(x86の場合のみtrue)。

14
rados

CPUIDを使用した現在の最高投票数の回答は廃止されたようです。間違った数の論理プロセッサと物理プロセッサの両方を報告します。これは、この答えから確認されたようです cpuid-on-intel-i7-processors

具体的には、CPUID.1.EBX [23:16]を使用して論理プロセッサを取得するか、CPUID.4.EAX [31:26] +1を使用してIntelプロセッサで物理プロセッサを取得すると、IntelプロセッサIで正しい結果が得られません。持ってる。

Intelの場合、CPUID.Bhを使用する必要があります Intel_thread/Fcoreおよびキャッシュトポロジ 。解決策は簡単ではないようです。 AMDの場合、別のソリューションが必要です。

物理コアおよび論理コアの正しい数とソケットの正しい数を報告するIntelのソースコードを次に示します https://software.intel.com/en-us/articles/intel-64-architecture -processor-topology-enumeration / 。 80の論理コア、40の物理コア、4ソケットのIntelシステムでこれをテストしました。

AMDのソースコードは次のとおりです http://developer.AMD.com/resources/documentation-articles/articles-whitepapers/processor-and-core-enumeration-using-cpuid/ 。シングルソケットIntelシステムでは正しい結果が得られましたが、4ソケットシステムでは正しくありませんでした。テストするAMDシステムがありません。

CPUIDを使用した簡単な答え(存在する場合)を見つけるために、ソースコードをまだ分析していません。解決策が変更される可能性がある場合(あると思われる)、ライブラリまたはOS呼び出しを使用することが最善の解決策であると思われます。

編集:

CPUIDリーフ11(Bh)のIntelプロセッサー向けのソリューションを次に示します。これを行う方法は、論理プロセッサをループし、CPUIDから各論理プロセッサのx2APIC IDを取得し、最下位ビットがゼロであるx2APIC IDの数をカウントすることです。ハイパースレッディングのないシステムでは、x2APIC IDは常に偶数になります。ハイパースレッディングを備えたシステムの場合、各x2APIC IDには偶数バージョンと奇数バージョンがあります。

// input:  eax = functionnumber, ecx = 0
// output: eax = output[0], ebx = output[1], ecx = output[2], edx = output[3]
//static inline void cpuid (int output[4], int functionnumber)  

int getNumCores(void) {
    //Assuming an Intel processor with CPUID leaf 11
    int cores = 0;
    #pragma omp parallel reduction(+:cores)
    {
        int regs[4];
        cpuid(regs,11);
        if(!(regs[3]&1)) cores++; 
    }
    return cores;
}

これが機能するには、スレッドをバインドする必要があります。デフォルトでは、OpenMPはスレッドをバインドしません。設定export OMP_PROC_BIND=trueはそれらをバインドするか、または thread-affinity-with-windows-msvc-and-openmp に示すようにコードでバインドできます。

これを4コア/ 8 HTシステムでテストし、BIOSでハイパースレッディングを無効にした場合と無効にした場合に4を返しました。また、各ソケットが10コア/ 20 HTの4ソケットシステムでテストし、40コアを返しました。

CPUIDリーフ11のないAMDプロセッサーまたは古いIntelプロセッサーは、別のことをする必要があります。

12
Z boson

Mathの答えを引き継ぐために、boost 1.56の時点で、まさにあなたが望むことをするphysical_concurrency属性が存在します。

ドキュメントから- http://www.boost.org/doc/libs/1_56_0/doc/html/thread/thread_management.html#thread.thread_management.thread.physical_concurrency

現在のシステムで使用可能な物理コアの数。 hardware_concurrency()とは対照的に、仮想コアの数は返しませんが、物理コアのみをカウントします。

したがって、例は

    #include <iostream>
    #include <boost/thread.hpp>

    int main()
    {
        std::cout << boost::thread::physical_concurrency();
        return 0;
    }
7
Harry

これは古いスレッドですが、誰も言及していません hwloc 。 hwlocライブラリは、ほとんどのLinuxディストリビューションで使用でき、Windowsでもコンパイルできます。次のコードは、物理プロセッサの数を返します。 i7 CPUの場合は4。

#include <hwloc.h>

int nPhysicalProcessorCount = 0;

hwloc_topology_t sTopology;

if (hwloc_topology_init(&sTopology) == 0 &&
    hwloc_topology_load(sTopology) == 0)
{
    nPhysicalProcessorCount =
        hwloc_get_nbobjs_by_type(sTopology, HWLOC_OBJ_CORE);

    hwloc_topology_destroy(sTopology);
}

if (nPhysicalProcessorCount < 1)
{
#ifdef _OPENMP
    nPhysicalProcessorCount = omp_get_num_procs();
#else
    nPhysicalProcessorCount = 1;
#endif
}
6
pneveu

上記のアイデアのいくつかからアイデアと概念を集めて、私はこの解決策を思いつきました。批判してください。

//EDIT INCLUDES

#ifdef _WIN32
    #include <windows.h>
#Elif MACOS
    #include <sys/param.h>
    #include <sys/sysctl.h>
#else
    #include <unistd.h>
#endif

ほとんどすべてのOSで、標準の「コアカウントの取得」機能は論理コアカウントを返します。ただし、物理コアカウントを取得するには、最初にCPUにハイパースレッドがあるかどうかを検出する必要があります。

uint32_t registers[4];
unsigned logicalcpucount;
unsigned physicalcpucount;
#ifdef _WIN32
SYSTEM_INFO systeminfo;
GetSystemInfo( &systeminfo );

logicalcpucount = systeminfo.dwNumberOfProcessors;

#else
logicalcpucount = sysconf( _SC_NPROCESSORS_ONLN );
#endif

これで論理コアカウントが得られました。目的の結果を得るには、まず、ハイパースレッドが使用されているかどうか、または使用可能かどうかを確認する必要があります。

__asm__ __volatile__ ("cpuid " :
                      "=a" (registers[0]),
                      "=b" (registers[1]),
                      "=c" (registers[2]),
                      "=d" (registers[3])
                      : "a" (1), "c" (0));

unsigned CPUFeatureSet = registers[3];
bool hyperthreading = CPUFeatureSet & (1 << 28);

(少なくとも私が読んだものからではなく)1つのコアのみをハイパースレッドするハイパースレッドを備えたIntel CPUがないためです。これにより、これが非常に簡単な方法であることがわかります。ハイパースレッディングが利用可能な場合、論理プロセッサは物理プロセッサのちょうど2倍になります。それ以外の場合、オペレーティングシステムはすべてのシングルコアの論理プロセッサを検出します。論理コア数と物理コア数の意味は同じです。

if (hyperthreading){
    physicalcpucount = logicalcpucount / 2;
} else {
    physicalcpucount = logicalcpucount;
}

fprintf (stdout, "LOGICAL: %i\n", logicalcpucount);
fprintf (stdout, "PHYSICAL: %i\n", physicalcpucount);
6
Pnelego

OS Xでは、これらの値をsysctl(3)(C API、または同じ名前のコマンドラインユーティリティ)から読み取ることができます。マニュアルページには、使用方法に関する情報が記載されています。次のキーが興味深い場合があります。

$ sysctl hw
hw.ncpu: 24
hw.activecpu: 24
hw.physicalcpu: 12  <-- number of cores
hw.physicalcpu_max: 12
hw.logicalcpu: 24   <-- number of cores including hyper-threaded cores
hw.logicalcpu_max: 24
hw.packages: 2      <-- number of CPU packages
hw.ncpu = 24
hw.availcpu = 24

Windowsでは、Windowsで利用可能なGetLogicalProcessorInformationGetLogicalProcessorInformationExがありますXP SP3以前とWindows 7以降。違いは、GetLogicalProcessorInformationはセットアップをサポートしないことです。サーバーのセットアップには重要かもしれませんが、64を超える論理コアがありますが、XPを使用している場合はいつでもGetLogicalProcessorInformationにフォールバックできます。GetLogicalProcessorInformationExの使用例( source ):

PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX buffer = NULL;
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX ptr = NULL;
BOOL rc;
DWORD length = 0;
DWORD offset = 0;
DWORD ncpus = 0;
DWORD prev_processor_info_size = 0;
for (;;) {
    rc = psutil_GetLogicalProcessorInformationEx(
            RelationAll, buffer, &length);
    if (rc == FALSE) {
        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
            if (buffer) {
                free(buffer);
            }
            buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)malloc(length);
            if (NULL == buffer) {
                return NULL;
            }
        }
        else {
            goto return_none;
        }
    }
    else {
        break;
    }
}
ptr = buffer;
while (offset < length) {
    // Advance ptr by the size of the previous
    // SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct.
    ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)\
        (((char*)ptr) + prev_processor_info_size);

    if (ptr->Relationship == RelationProcessorCore) {
        ncpus += 1;
    }

    // When offset == length, we've reached the last processor
    // info struct in the buffer.
    offset += ptr->Size;
    prev_processor_info_size = ptr->Size;
}

free(buffer);
if (ncpus != 0) {
    return ncpus;
}
else {
    return NULL;
}

return_none:
if (buffer != NULL)
    free(buffer);
return NULL;

Linuxでは、/proc/cpuinfo 役立つかもしれません。

0

Pythonでこれを行うのは非常に簡単です。

$ python -c "import psutil; psutil.cpu_count(logical=False)"
4

たぶん、あなたはpsutilソースコードを見て何が起こっているのかを見ることができますか?

0
ostrokach

3つすべてが同じ方法で情報を公開することはわかりませんが、NTカーネルがPOSIX標準(NTがサポートしていると思われる)に従ってデバイス情報をレポートすると安全に想定できる場合は、それを解決できます標準。

ただし、デバイス管理の違いは、クロスプラットフォーム開発の障害の1つとしてしばしば引用されます。せいぜい、これを3つのロジックストランドとして実装します。すべてのプラットフォームを均等に処理するために、1つのコードを記述しようとはしません。

OK、それはすべてC++を前提としています。 ASMの場合、x86またはAMD64 CPUでのみ実行されると思いますか?各アーキテクチャに1つずつ、合計2つのブランチパスが必要です。AMD(IIRC)とは別にIntelをテストする必要がありますが、概してCPUIDを確認するだけです。それはあなたが見つけようとしているものですか? Intel/AMDファミリCPU上のASMからのCPUID?

0
jcolebrand