web-dev-qa-db-ja.com

CPUクロック周波数を見つける(コアごと、プロセッサごと)

CPUzなどのプログラムは、システムに関する詳細な情報(バス速度、メモリタイミングなど)を提供するのに非常に優れています。

ただし、CPU固有の情報を処理することなく、コアごと(およびCPUごとに複数のコアを備えたマルチプロセッサシステムのプロセッサごと)の周波数をプログラムで計算する方法はありますか。

システム内のすべてのアクティブコア(すべてのプロセッサにわたって)のベンチマーク実行中にCPUクロックを記録できるアンチチートツール(クロック制限ベンチマークコンペティション用)を開発しようとしています。

36
kidoman

ここでコメントを拡大します。これは大きすぎて綿密であり、コメントに収まりきれません。

あなたがやろうとしていることは非常に困難です-以下の理由で非実用的であるという点まで:

  • プロセッサの周波数を取得するポータブルな方法はありません。 rdtsc[〜#〜] not [〜#〜]は常に与えるSpeedStepやTurbo Boostなどの効果による正しい周波数。
  • 周波数を測定するすべての既知の方法では、正確な時間の測定が必要です。ただし、決心した詐欺師は、システム内のすべてのクロックとタイマーを改ざんできます。
  • プロセッサ周波数と時間の両方を改ざん防止方法で正確に読み取るには、カーネルレベルのアクセスが必要です。これは、Windows用のドライバー署名を意味します。

プロセッサ周波数を取得するポータブルな方法はありません:

CPU周波数を取得する「簡単な」方法は、 rdtsc を一定の時間間隔で2回呼び出すことです。次に、差を分割すると、頻度がわかります。

問題は、rdtscがプロセッサの真の周波数を提供しないことです。ゲームなどのリアルタイムアプリケーションはこれに依存しているため、rdtscはCPUスロットリングとターボブーストを通じて一貫している必要があります。したがって、システムが起動すると、rdtscは常に同じ速度で実行されます( SetFSB などでバス速度をいじり始めない限り)。

たとえば、Core i7 2600Kでは、rdtscは常に_3.4 GHz_の周波数を表示します。しかし実際には、_1.6 GHz_でアイドル状態になり、_4.6 GHz_でオーバークロックされたTurbo Boost乗算器を介して負荷がかかったときに_46x_にクロックアップします。

しかし、真の周波数を測定する方法を見つけたら(またはrdtscに満足しているなら)、 thread-affinities を使用して各コアの周波数を簡単に取得できます。

真の周波数の取得:

プロセッサの実際の周波数を取得するには、MSR(モデル固有のレジスタ)またはハードウェアパフォーマンスカウンターのいずれかにアクセスする必要があります。

これらはカーネルレベルの命令であるため、ドライバーを使用する必要があります。したがって、配布を目的としてWindowsでこれを試みている場合は、適切なドライバー署名プロトコルを実行する必要があります。さらに、コードはプロセッサのメーカーとモデルによって異なるため、プロセッサの世代ごとに異なる検出コードが必要になります。

この段階に到達すると、周波数を読み取るためのさまざまな方法があります。

Intelプロセッサでは、ハードウェアカウンタを使用して、未加工のCPUサイクルをカウントできます。リアルタイムを正確に測定する方法と組み合わせて(次のセクション)、真の周波数を計算できます。 MSRを使用すると、CPU周波数乗数などの他の情報にアクセスできます。


周波数を測定するすべての既知の方法では、正確な時間の測定が必要です:

これはおそらく大きな問題です。周波数を測定するにはタイマーが必要です。有能なハッカーは、C/C++で使用できるすべてのクロックを改ざんできます。これには、次のすべてが含まれます。

  • clock()
  • gettimeofday()
  • QueryPerformanceCounter()
  • 等...

リストは延々と続く。つまり、有能なハッカーがすべてのタイマーを偽装できるため、タイマーを信頼することはできません。たとえば、clock()gettimeofday()は、OS内でシステムクロックを直接変更することでだまされる可能性があります。 QueryPerformanceCounter()をだますのは難しい。

時間の真の測定値を取得する:

上記のすべてのクロックは、何らかの方法で同じシステムベースクロックから派生することが多いため、脆弱です。そして、そのシステムベースクロックは、多くの場合、システムベースクロックに関連付けられています。これは、オーバークロックユーティリティを使用してシステムが既に起動した後に変更できます。

そのため、信頼性が高く不正操作されていない時間を測定する唯一の方法は、 [〜#〜] hpet [〜#〜] または [〜# 〜] acpi [〜#〜] 。残念ながら、これらにはカーネルレベルのアクセスが必要なようです。


要約するには:

あらゆる種類の改ざん防止ベンチマークを構築するには、ほぼ確実に、Windowsの証明書署名を必要とするカーネルモードドライバーを記述する必要があります。これは多くの場合、カジュアルなベンチマーク作成者にとっては負担が大きすぎます。

これにより、改ざん防止のベンチマークが不足し、近年の競争の激しいオーバークロックコミュニティの全体的な衰退の一因となっている可能性があります。

31
Mysticial

これはすでに答えられていることを理解しています。また、これは基本的に黒の芸術であることがわかっているので、それを受け取ったり、残したり、フィードバックを提供してください。

スロットル(microsft、hp、およびDellに感謝)HyperVホスト(信頼性の低いperfカウンター)、およびHyperVゲスト(現在のCPU速度のみを取得できます)のクロックレートを検索するために、試行錯誤を通じて管理しました。フルーク、クロックごとに1回だけループするループを作成します。

コードは次のとおりです-C#5.0、SharpDev、32ビット、ターゲット3.5、最適化オン(重要)、デバッガーがアクティブでない(重要)

        long frequency, start, stop;
        double multiplier = 1000 * 1000 * 1000;//nano
        if (Win32.QueryPerformanceFrequency(out frequency) == false)
            throw new Win32Exception();

        Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
        const int gigahertz= 1000*1000*1000;
        const int known_instructions_per_loop = 1; 

        int iterations = int.MaxValue;
        int g = 0;

        Win32.QueryPerformanceCounter(out start);
        for( i = 0; i < iterations; i++)
        {
            g++;
            g++;
            g++;
            g++;
        }
        Win32.QueryPerformanceCounter(out stop);

        //normal ticks differs from the WMI data, i.e 3125, when WMI 3201, and CPUZ 3199
        var normal_ticks_per_second = frequency * 1000;
        var ticks = (double)(stop - start);
        var time = (ticks * multiplier) /frequency;
        var loops_per_sec = iterations / (time/multiplier);
        var instructions_per_loop = normal_ticks_per_second  / loops_per_sec;

        var ratio = (instructions_per_loop / known_instructions_per_loop);
        var actual_freq = normal_ticks_per_second / ratio;

        Console.WriteLine( String.Format("Perf counhter freq: {0:n}", normal_ticks_per_second));
        Console.WriteLine( String.Format("Loops per sec:      {0:n}", loops_per_sec));
        Console.WriteLine( String.Format("Perf counter freq div loops per sec: {0:n}", instructions_per_loop));
        Console.WriteLine( String.Format("Presumed freq: {0:n}", actual_freq));
        Console.WriteLine( String.Format("ratio: {0:n}", ratio));

ノート

  • デバッガーがアクティブな場合、ループごとに25命令
  • 手前に2〜3秒のループを実行してプロセッサをスピンアップすることを検討してください(または、少なくともサーバーのスロットルがどれほど重くなっているかを把握して、スピンアップを試行してください)。
  • 64ビットCore2およびHaswell Pentiumでテストし、CPU-Zと比較
3
Patrick

私は以前にこのテーマについて(基本的なアルゴリズムとともに)投稿しました: here 。私の知る限り、アルゴリズム(説明を参照)は非常に正確です。たとえば、Windows 7は、CPUクロックを2.00 GHz、CPU-Zを1994-1996 MHz、アルゴリズムを1995025-1995075 kHzとして報告します。

アルゴリズムはこれを行うために多くのループを実行し、これによりCPU周波数が最大になり(ベンチマーク中も同様)、速度調整ソフトウェアが機能しなくなります。

追加情報 here および here

速度調整の問題については、アプリケーションが速度値を使用して経過時間を決定し、時間自体が非常に重要である場合を除き、実際に問題とは見なしません。たとえば、分割が完了するのにxクロックサイクルを必要とする場合、CPUが3 GHzまたは300 MHzで実行されているかどうかは関係ありません。 @ 3 GHzの時間。

2
Olof Forshell

このホワイトペーパーを参照してください: Intel®Core™Microarchitecture(Nehalem)ベースのプロセッサのIntel®Turbo Boost Technology 。基本的に、サンプル期間TにわたってUCC固定パフォーマンスカウンターの読み取りをいくつか生成します。

Relative.Freq = Delta(UCC)  / T

Where:
   Delta() = UCC @ period T
                 - UCC @ period T-1

Nehalemアーキテクチャから始めて、UCCはコアのUnhalted状態と比較してクリックティックの数を増減します。

SpeedStepまたはTurbo Boostがアクティブになると、それに応じてUCCを使用した推定周波数が測定されます。 TSCは一定のままです。たとえば、動作中のターボブーストは、Delta(UCC)がDelta(TSC)以上であることを明らかにします

Cyring | CoreFreq GitHubの関数Core_Cycle関数の例.

1
CyrIng

CallNtPowerInformation を使用する必要があります。 コードサンプル from putil プロジェクトです。これにより、現在および最大のCPU周波数を取得できます。私の知る限り、CPUごとの周波数を取得することはできません。

1

最も簡単な方法の1つはRDTSCを使用することですが、これは不正防止メカニズム用であるため、これをカーネルドライバーまたはハイパーバイザーに常駐するコードとして組み込みます。

また、おそらく独自のタイミングコードをロールする必要があります**。これもRDTSCで実行できます(以下の例で使用するQPCはRDTSCを使用します。リバースエンジニアリングし、ローカルコピーを使用します。つまり、それを改ざんするには、ドライバーを改ざんする必要があります)。

void GetProcessorSpeed()
{
    CPUInfo* pInfo = this;
    LARGE_INTEGER qwWait, qwStart, qwCurrent;
    QueryPerformanceCounter(&qwStart);
    QueryPerformanceFrequency(&qwWait);
    qwWait.QuadPart >>= 5;
    unsigned __int64 Start = __rdtsc();
    do
    {
        QueryPerformanceCounter(&qwCurrent);
    }while(qwCurrent.QuadPart - qwStart.QuadPart < qwWait.QuadPart);
    pInfo->dCPUSpeedMHz = ((__rdtsc() - Start) << 5) / 1000000.0;
}

**これは@Mysticalが述べたようにセキュリティのためですが、低レベルのシステムタイミングメカニズムを破壊する衝動を感じたことがないので、もっと複雑になる可能性があります。

1
Necrolis