it-swarm.com.de

Ermittlung der CPU-Taktfrequenz (pro Kern, pro Prozessor)

Programme wie CPUz sind sehr gut darin, detaillierte Informationen über das System (Busgeschwindigkeit, Speicherzeiten usw.) zu geben.

Gibt es eine programmatische Methode zur Berechnung der Frequenz pro Kern (und pro Prozessor in Mehrprozessorsystemen mit mehreren Kernen pro CPU), ohne sich mit CPU-spezifischen Informationen befassen zu müssen.

Ich versuche ein Anti-Cheating-Tool zu entwickeln (für den Einsatz bei Zeitlimit-Benchmark-Wettbewerben), mit dem der CPU-Takt während des Benchmark-Laufs für alle aktiven Kerne im System (über alle Prozessoren) aufgezeichnet werden kann.

34
kidoman

Ich werde hier auf meine Kommentare eingehen. Das ist zu groß und zu ausführlich, als dass ich es in die Kommentare einfügen könnte.

Was Sie versuchen zu tun, ist sehr schwierig - bis zu dem Punkt, aus den folgenden Gründen unpraktisch zu sein:

  • Es gibt keine tragbare Möglichkeit, die Prozessorfrequenz zu ermitteln. rdtsc gibt NOT aufgrund von Effekten wie SpeedStep und Turbo Boost immer die richtige Frequenz an.
  • Alle bekannten Methoden zur Frequenzmessung erfordern eine genaue Zeitmessung. Ein entschlossener Betrüger kann jedoch alle Uhren und Timer im System manipulieren.
  • Um sowohl die Prozessorfrequenz als auch die Uhrzeit manipulationssicher ablesen zu können, ist ein Zugriff auf Kernel-Ebene erforderlich. Dies setzt die Treibersignatur für Windows voraus.

Es gibt keine tragbare Möglichkeit, die Prozessorfrequenz zu ermitteln:

Die "einfache" Möglichkeit, die CPU-Frequenz zu ermitteln, besteht darin, rdtsc zweimal mit einer festgelegten Zeitdauer dazwischen aufzurufen. Wenn Sie dann die Differenz aufteilen, erhalten Sie die Frequenz.

Das Problem ist, dass rdtsc nicht die wahre Frequenz des Prozessors angibt. Da Echtzeitanwendungen wie Spiele darauf angewiesen sind, muss rdtsc durch CPU-Drosselung und Turbo-Boost konsistent sein. Sobald Ihr System hochfährt, wird rdtsc immer mit der gleichen Rate ausgeführt (es sei denn, Sie beginnen, mit SetFSB oder so etwas mit den Busgeschwindigkeiten herumzuspielen).

Beispielsweise zeigt rdtsc auf meinem Core i7 2600K immer die Frequenz bei 3.4 GHz an. In Wirklichkeit läuft es jedoch bei 1.6 GHz im Leerlauf und taktet über den übertakteten Turbo-Boost-Multiplikator bei 4.6 GHz bis zu 46x.

Sobald Sie jedoch einen Weg gefunden haben, die wahre Frequenz zu messen (oder mit rdtsc zufrieden sind), können Sie die Frequenz jedes Kerns leicht mit Thread-Affinitäten ermitteln.

Ermitteln der wahren Frequenz:

Um die tatsächliche Frequenz des Prozessors zu ermitteln, müssen Sie entweder auf die MSRs (modellspezifischen Register) oder auf die Hardwareleistungsindikatoren zugreifen.

Dies sind Anweisungen auf Kernel-Ebene und erfordern daher die Verwendung eines Treibers. Wenn Sie dies in Windows zum Zweck der Verteilung versuchen, müssen Sie daher das richtige Treibersignaturprotokoll durchgehen. Darüber hinaus unterscheidet sich der Code nach Prozessorhersteller und -modell, sodass Sie für jede Prozessorgeneration einen anderen Erkennungscode benötigen.

Wenn Sie diese Phase erreicht haben, gibt es verschiedene Möglichkeiten, die Frequenz abzulesen.

Auf Intel-Prozessoren können Sie mit den Hardware-Zählern unformatierte CPU-Zyklen zählen. In Kombination mit einer Methode zur präzisen Echtzeitmessung (nächster Abschnitt) können Sie die wahre Frequenz berechnen. Über die MSRs haben Sie Zugriff auf andere Informationen, z. B. den CPU-Frequenzvervielfacher.


Alle bekannten Methoden zur Frequenzmessung erfordern eine genaue Zeitmessung:

Dies ist vielleicht das größere Problem. Sie benötigen einen Timer, um die Frequenz messen zu können. Ein fähiger Hacker kann alle Uhren manipulieren, die Sie in C/C++ verwenden können. Dies umfasst alle folgenden Punkte:

  • clock()
  • gettimeofday()
  • QueryPerformanceCounter()
  • usw...

Die Liste geht weiter und weiter. Mit anderen Worten, Sie können keinem der Timer vertrauen, da ein fähiger Hacker sie alle fälschen kann. Zum Beispiel können clock() und gettimeofday() getäuscht werden, indem die Systemuhr direkt im Betriebssystem geändert wird. Es ist schwerer, QueryPerformanceCounter() zu täuschen.

Eine echte Zeitmessung erhalten:

Alle oben aufgeführten Uhren sind anfällig, da sie häufig auf die eine oder andere Weise von derselben Systembasisuhr abgeleitet werden. Und diese Systembasisuhr ist häufig an die Systembasisuhr gebunden - diese kann geändert werden, nachdem das System bereits mithilfe von Übertaktungsdienstprogrammen hochgefahren wurde.

Die einzige Möglichkeit, eine zuverlässige und manipulationssichere Zeitmessung zu erhalten, besteht darin, externe Uhren wie die HPET oder die ACPI auszulesen. Leider scheinen diese auch einen Zugriff auf Kernel-Ebene zu erfordern.


Zusammenfassen:

Um einen manipulationssicheren Benchmark zu erstellen, muss mit ziemlicher Sicherheit ein Kernelmodustreiber geschrieben werden, für den eine Zertifikatsignatur für Windows erforderlich ist. Dies ist für gelegentliche Benchmark-Autoren oft eine zu große Belastung.

Dies hat zu einem Mangel an manipulationssicheren Benchmarks geführt, was wahrscheinlich zum allgemeinen Niedergang der wettbewerbsorientierten Overclocking-Community in den letzten Jahren beigetragen hat.

31
Mysticial

Mir ist klar, dass dies bereits beantwortet wurde. Mir ist auch klar, dass dies im Grunde eine schwarze Kunst ist, also nimm es bitte oder lass es - oder gib Feedback.

Bei der Suche nach der Taktrate auf gedrosselten (dank microsft, hp und Dell) HyperV-Hosts (unzuverlässiger Leistungsindikator) und HyperV-Gästen (die nur CPU-Geschwindigkeit erhalten können, nicht aktuell) habe ich es geschafft, durch Testfehler und Fluke, um eine Schleife zu erstellen, die genau einmal pro Uhr wiederholt wird. 

Code wie folgt - C # 5.0, SharpDev, 32bit, Target 3.5, Optimieren auf (entscheidend), kein Debuger aktiv (entscheidend)

        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));

Anmerkungen

  • 25 Anweisungen pro Schleife, wenn der Debugger aktiv ist
  • Erwägen Sie vor dem Start eine 2 oder 3 Sekunden lange Schleife, um den Prozessor hochzufahren (oder versuchen Sie mindestens, hochzufahren, da Sie wissen, wie stark Server heutzutage gedrosselt werden).
  • Getestet auf einem 64-Bit-Core2- und Haswell-Pentium und im Vergleich mit CPU-Z
3
Patrick

Ich habe bereits zu diesem Thema (zusammen mit einem grundlegenden Algorithmus) gepostet: hier . Meines Wissens ist der Algorithmus (siehe die Diskussion) sehr genau. Zum Beispiel meldet Windows 7 meinen CPU-Takt als 2.00 GHz, CPU-Z als 1994-1996 MHz und meinen Algorithmus als 1995025-1995075 kHz.

Der Algorithmus führt dazu viele Schleifen aus, wodurch die CPU-Frequenz (wie auch bei Benchmarks) auf das Maximum ansteigt, so dass Software zur Geschwindigkeitsbeschränkung nicht zum Tragen kommt.

Zusatzinfo hier und hier .

Bei der Frage der Geschwindigkeitsbeschränkung sehe ich es wirklich nicht als Problem an, es sei denn, eine Anwendung verwendet die Geschwindigkeitswerte, um abgelaufene Zeiten zu bestimmen, und dass die Zeiten selbst extrem wichtig sind. Wenn beispielsweise für eine Division x Taktzyklen erforderlich sind, spielt es keine Rolle, ob die CPU mit 3 GHz oder 300 MHz läuft: Sie benötigt immer noch x Taktzyklen. Der einzige Unterschied besteht darin, dass die Division in einem Zehntel abgeschlossen wird der Zeit bei @ 3 GHz.

2
Olof Forshell

Man sollte sich auf dieses Whitepaper beziehen: Intel® Turbo-Boost-Technologie in Prozessoren mit Intel® Core ™ Mikroarchitektur (Nehalem) . Generell mehrere Lesevorgänge des festen UCC-Leistungsindikators über einen Abtastzeitraum T erzeugen.

Relative.Freq = Delta(UCC)  / T

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

Beginnend mit der Nehalem-Architektur erhöht und verringert UCC die Anzahl der Klick-Ticks im Verhältnis zum Status "Nicht aufrechterhalten" des Kerns.

Wenn SpeedStep oder Turbo Boost aktiviert ist, wird die geschätzte Frequenz mit UCC entsprechend gemessen. während TSC konstant bleibt. Zum Beispiel zeigt Turbo Boost in Action, dass Delta (UCC) größer oder gleich Delta (TSC) ist.

Beispiel in der Funktion Core_Cycle Funktion bei Cyring | CoreFreq GitHub.

1
CyrIng

Eine der einfachsten Möglichkeiten ist die Verwendung von RDTSC. Da dies jedoch für Anti-Cheating-Mechanismen ist, würde ich dies als Kernel-Treiber oder als residenten Code-Code einsetzen.

Sie müssen wahrscheinlich auch Ihren eigenen Timing-Code ** rollen, der wiederum mit RDTSC ausgeführt werden kann (wie in dem folgenden Beispiel verwendet QPC RDTSC). In der Tat ist es sehr einfach, eine lokale Kopie von was zurückzuentwickeln und zu verwenden bedeutet, damit zu manipulieren, müssen Sie Ihren Fahrer manipulieren).

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;
}

** Ich würde dies aus Sicherheitsgründen als @Mystical erwähnen, aber da ich noch nie den Drang verspürt hatte, Low-Level-System-Timing-Mechanismen zu unterlaufen, wäre es vielleicht umständlicher, es wäre schön, wenn Mystical etwas hinzufügen könnte :)

1
Necrolis

Sie müssen CallNtPowerInformation verwenden. Hier ist ein Code-Beispiel from putil project ..__ Hiermit können Sie die aktuelle und maximale CPU - Frequenz abrufen. Soweit ich weiß, ist es nicht möglich, die Frequenz pro CPU zu erhalten.

0