it-swarm-eu.dev

Zjištění frekvence hodin CPU (na jádro, na procesor)

Programy jako CPUz jsou velmi dobré při poskytování podrobných informací o systému (rychlost sběrnice, časování paměti atd.)

Existuje však programový způsob výpočtu na jádro (a na procesor, ve víceprocesorových systémech s více jádry na CPU) frekvenci, aniž by bylo nutné řešit informace specifické pro CPU.

Snažím se vyvinout nástroj proti podvádění (pro použití s ​​časově omezenými soutěžemi s benchmarkem), který bude schopen zaznamenávat hodiny CPU během běhu benchmarku pro všechna aktivní jádra v systému (napříč všemi procesory).

34
kidoman

Budu rozšiřovat své komentáře zde. To je pro mě příliš velké a hluboké, abych se vešly do komentářů.

To, co se snažíte udělat, je velmi obtížné - do té míry, že je nepraktické z následujících důvodů:

  • Neexistuje žádný přenosný způsob, jak získat frekvenci procesoru. rdtsc dělá NE vždy dávají správnou frekvenci kvůli efektům jako SpeedStep a Turbo Boost.
  • Všechny známé metody měření frekvence vyžadují přesné měření času. Určený podvodník však může manipulovat se všemi hodinami a časovači v systému.
  • Přesné čtení buď frekvence procesoru, stejně jako času neoprávněným způsobem, bude vyžadovat přístup na úrovni jádra. To znamená podepisování ovladačů pro systém Windows.

Není k dispozici přenosný způsob, jak získat frekvenci procesoru:

"Snadný" způsob, jak získat frekvenci CPU, je volání rdtsc dvakrát s pevnou dobou trvání mezi nimi. Rozdělením rozdílu získáte frekvenci.

Problém je v tom, že rdtsc nedává skutečnou frekvenci procesoru. Protože aplikace v reálném čase, jako jsou hry, se na něj spoléhají, musí být rdtsc konzistentní díky omezování CPU a funkci Turbo Boost. Jakmile se systém spustí, bude rdtsc vždy fungovat stejnou rychlostí (pokud nezačnete s rychlostí sběrnice začínat s SetFSB nebo něco).

Například na mém Core i7 2600K bude rdtsc vždy zobrazovat frekvenci v 3.4 GHz. Ale ve skutečnosti, to nečiní u 1.6 GHz a hodiny nahoru k 4.6 GHz pod zátěží přes přetaktovaný Turbo Boost násobitel u 46x.

Ale jakmile najdete způsob, jak měřit skutečnou frekvenci (nebo jste spokojeni s rdtsc), můžete snadno získat frekvenci každého jádra pomocí vláknitých afinit .

Získání skutečné frekvence:

Chcete-li získat skutečnou frekvenci procesoru, musíte mít přístup buď k MSR (model-specifické registry) nebo k čítačům výkonu hardwaru.

Jedná se o instrukce na úrovni jádra, a proto vyžadují použití ovladače. Pokud se o to pokoušíte ve Windows pro účely distribuce, budete muset projít správným protokolem o podepisování ovladačů. Kromě toho se kód bude lišit podle výrobce a modelu procesoru, takže budete potřebovat jiný kód detekce pro každou generaci procesoru.

Jakmile se dostanete do této fáze, existuje celá řada způsobů, jak číst frekvenci.

Na procesorech Intel umožňují čítače hardwaru počítat cykly surového procesoru. V kombinaci s metodou přesného měření reálného času (další sekce) můžete vypočítat skutečnou frekvenci. MSR vám umožní přístup k dalším informacím, jako je násobitel frekvence CPU.


Všechny známé metody měření frekvence vyžadují přesné měření času:

To je snad větší problém. K měření frekvence potřebujete časovač. Schopný hacker bude schopen manipulovat se všemi hodinami, které můžete použít v C/C++. Toto zahrnuje všechny následující položky:

  • clock()
  • gettimeofday()
  • QueryPerformanceCounter()
  • atd...

Seznam pokračuje dál a dál. Jinými slovy, nemůžete věřit žádnému z časovačů, protože schopný hacker bude schopen spoofovat všechny z nich. Například clock() a gettimeofday() lze zmást změnou systémových hodin přímo v operačním systému. Fooling QueryPerformanceCounter() je těžší.

Získání skutečného měření času:

Všechny výše uvedené hodiny jsou zranitelné, protože jsou často odvozeny od stejných systémových základních hodin nějakým způsobem. A že základní systémové hodiny jsou často vázány na systémové základní hodiny - které mohou být změněny poté, co systém již byl spuštěn pomocí utilit pro přetaktování.

Jediný způsob, jak získat spolehlivé měření času, je přečtení externích hodin, například HPET nebo ACPI . Zdá se, že tyto požadavky také vyžadují přístup na úrovni jádra.


Shrnout:

Budování jakéhokoli typu benchmarků odolných proti manipulaci téměř jistě vyžaduje napsání ovladače režimu jádra, který vyžaduje podepisování certifikátů pro systém Windows. To je často příliš zátěž pro příležitostné benchmark spisovatelů.

To má za následek nedostatek srovnávacích testů, které pravděpodobně v posledních letech přispěly k celkovému poklesu konkurenceschopné komunity přetaktování.

30
Mysticial

Uvědomuji si, že to už bylo zodpovězeno. Uvědomuji si také, že je to v podstatě černé umění, tak ho prosím vezměte nebo ho nechte - nebo nabídněte zpětnou vazbu.

Ve snaze najít hodiny rychlost na škrcený (díky microft, hp, a Dell) HyperV hostitelé (nespolehlivé počítadlo výkonu), a HyperV hosty (lze získat pouze rychlost CPU CPU, ne aktuální), jsem se podařilo, přes zkušební chyby a vytvořit smyčku, která smyčky přesně jednou za hodinu. 

Kód následovně - C # 5.0, SharpDev, 32bit, Cíl 3.5, Optimalizovat na (klíčové), žádný debuger aktivní (klíčový)

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

Poznámky

  • 25 instrukcí na smyčku, pokud je aktivní ladicí program
  • Zvažte spuštění smyčky 2 nebo 3 sekundy před spřádáním procesoru (nebo alespoň pokusu o rotaci nahoru, s vědomím, jak jsou tyto servery v dnešní době značně omezeny)
  • Testováno na 64bitovém Core2 a Haswell Pentium a porovnáno s CPU-Z
3
Patrick

Dříve jsem na toto téma napsal (spolu se základním algoritmem): zde . Podle mého vědomí je algoritmus (viz diskuse) velmi přesný. Například Windows 7 hlásí mé CPU hodiny jako 2.00 GHz, CPU-Z jako 1994-1996 MHz a můj algoritmus jako 1995025-1995075 kHz.

Algoritmus provádí spoustu smyček, aby tak učinil, což způsobí, že frekvence procesoru se zvýší na maximum (stejně jako v průběhu benchmarků), takže do hry nepřijde software, který omezuje rychlost.

Další informace zde a zde .

Co se týče rychlosti-škrcení opravdu nevidím to jako problém, pokud aplikace používá hodnoty rychlosti k určení uplynulých časů a že časy jsou velmi důležité. Například, pokud divize vyžaduje x hodiny cyklů k dokončení, nezáleží na tom, jestli CPU běží na 3 GHz nebo 300 MHz: bude to stále potřebovat x hodinové cykly a jediný rozdíl je v tom, že dokončí divizi v desáté. času při @ 3 GHz.

2
Olof Forshell

Jeden by měl odkazovat na tuto bílou knihu: Intel® Turbo Boost technologie v procesorech Intel® Core ™ Microarchitecture (Nehalem) . V podstatě vytvořte několik přečtů počítadla UCC s pevným výkonem za vzorkovací periodu T.

Relative.Freq = Delta(UCC)  / T

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

Počínaje architekturou Nehalem, UCC zvýší a sníží počet kliknutí klíšťat relativně k Unhalted stavu jádra.

Pokud jsou aktivovány funkce SpeedStep nebo Turbo Boost, bude podle toho změřena odhadovaná frekvence pomocí UCC; zatímco TSC zůstává konstantní. Například Turbo Boost v akci odhaluje, že Delta (UCC) je větší nebo rovno Delta (TSC)

Příklad funkce Core_Cycle na Cyring | CoreFreq GitHub.

1
CyrIng

Jedním z nejjednodušších způsobů, jak to udělat, je použití RDTSC, ale když vidím, že se jedná o mechanismy proti podvádění, uvedl bych to jako ovladač jádra nebo rezidentní kód hyper-visor.

Pravděpodobně budete také potřebovat zavést svůj vlastní kód časování **, který lze opět provést pomocí RDTSC (QPC, jak je použito v příkladu níže, RDTSC, a ve skutečnosti je velmi jednoduchý na zpětné inženýrství a použití místní kopie, která znamená manipulovat s ním, budete muset manipulovat s řidičem).

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

** Já bych to byl pro bezpečnost, jak @ Mystický zmínil, ale jak jsem nikdy necítil nutkání podvracet mechanismy načasování systému na nízké úrovni, mohlo by to být více zapojeno, bylo by pěkné, kdyby Mystical něco přidal na to :)

1
Necrolis

Musíte použít CallNtPowerInformation . Zde je ukázka kódu z putil project. S tímto můžete získat aktuální a maximální frekvenci CPU. Pokud vím, není možné získat frekvenci na CPU.

0