Tato odpověď není odpovědí, ale spíše souborem poznámek.
Za prvé, CPU má tendenci pracovat na řádcích mezipaměti, nikoli na jednotlivých bytech/slovech/dwordech. To znamená, že pokud postupně čtete/zapisujete pole celých čísel, pak první přístup k řádku mezipaměti může způsobit vynechání mezipaměti, ale následné přístupy k různým celým číslům ve stejném řádku mezipaměti ne. Pro 64bajtové řádky mezipaměti a 4bajtová celá čísla to znamená, že vynechání mezipaměti zaznamenáte pouze jednou za každých 16 přístupů; což rozředí výsledky.
Za druhé, CPU má „hardwarový pre-fetcher“. Pokud zjistí, že jsou řádky mezipaměti čteny postupně, hardwarový pre-fetcher automaticky předběžně načte řádky mezipaměti, o kterých předpovídá, že budou potřeba jako další (ve snaze načíst je do mezipaměti dříve, než budou potřeba).
Zatřetí, CPU dělá další věci (jako „provádění mimo pořadí“), aby skryl náklady na načtení. Časový rozdíl (mezi přístupem do mezipaměti a vynecháním mezipaměti), který můžete měřit, je čas, který CPU nemohl skrýt, a nikoli celkové náklady na načtení.
Tyto 3 věci dohromady znamenají, že; pro sekvenční čtení pole celých čísel je pravděpodobné, že CPU předběžně načte další řádek mezipaměti, zatímco vy provádíte 16 čtení z předchozího řádku mezipaměti; a případné náklady na vynechání mezipaměti nebudou patrné a mohou být zcela skryté. Aby se tomu zabránilo; budete chtít „náhodně“ přistupovat ke každému řádku mezipaměti jednou, abyste maximalizovali výkonnostní rozdíl měřený mezi „pracovní sada se vejde do mezipaměti“ a „pracovní sada se nevejde do mezipaměti“.
Nakonec existují další faktory, které mohou měření ovlivnit. Například u operačního systému, který používá stránkování (např. Linux a téměř všechny ostatní moderní operační systémy), je nad tím vším celá vrstva mezipaměti (TLBs/Translation Look-aside Buffers) a TLB chybí, jakmile pracovní sada překročí určitou velikost.; který by měl být viditelný jako čtvrtý „krok“ v grafu. Existuje také interference z jádra (IRQ, chyby stránek, přepínače úloh, více CPU atd.); která může být v grafu viditelná jako náhodná statika/chyba (pokud se testy často neopakují a odlehlé hodnoty nejsou vyřazeny). Existují také artefakty návrhu mezipaměti (asociativita mezipaměti), které mohou snížit efektivitu vyrovnávací paměti způsoby, které závisí na fyzické adrese/adresách přidělených jádrem; což může být viděno jako "kroky" v grafu posouvající se na různá místa.
Je s mojí metodou něco špatně?
Možná, ale bez zobrazení vašeho skutečného kódu, na který nelze odpovědět.
-
Váš popis toho, co váš kód dělá, neříká, zda pole čtete jednou nebo mnohokrát.
-
Pole nemusí být dost velké ... v závislosti na vašem hardwaru. (Nemají některé moderní čipy mezipaměť 3. úrovně o několika megabajtech?)
-
Zejména v případě Javy musíte udělat spoustu věcí správným způsobem, abyste implementovali smysluplný mikro-benchmark.
V případě C:
-
Můžete zkusit upravit optimalizační přepínače kompilátoru C.
-
Protože váš kód přistupuje k poli sériově, kompilátor může být schopen seřadit instrukce tak, aby CPU drželo krok, nebo může CPU optimisticky přednačítat nebo provádět rozsáhlé načítání. Můžete zkusit číst prvky pole v méně předvídatelném pořadí.
-
Je dokonce možné, že překladač zcela optimalizoval smyčku, protože výsledek výpočtu smyčky se k ničemu nepoužívá.
(Podle těchto otázek a odpovědí – Jak dlouho trvá načtení jednoho slova z paměti?, načtení z mezipaměti L2 je ~7 nanosekund a načtení z hlavní paměti je ~100 nanosekund. Získáváte ale ~2 nanosekundy. Něco chytrého musí zde probíhat, aby to běželo tak rychle, jak to pozorujete.)