Skrivet av GarfieldPower:
Speciellt när man skriver en massa paragrafer som inte är relevant på det man svarar i syfte att vilseleda.
Jag har en agenda, men det är min och endast min. Enda anledningen för mig att spendera tid i forum är för att det ibland ger ny information som kan vara svårt att komma över på annat sätt.
Är också långt mer intresserad kring att få beteenden förklarade på ett trovärdigt sätt, själva resultatet i sig är inte jättespännande att få veta. Då det finns en talesätt "bemöt andra som du själv vill bli bemött" så hoppas jag på fler inlägg där folk med djupare kunskap i ämnet faktiskt broderar ut sina inlägg med VARFÖR man ser ett visst resultat, inte bara smäller upp resultatet och säger: det är så för X sa så...
Finns liksom ingen anledningen överhuvudtaget varför jag skulle vilja vilseleda någon, det vore enbart att slösa med MIN tid (vilket är den enda tid jag bryr mig om i denna kontext). Syftet är i stället visa varför jag dragit den slutsats jag gjort, genom att sätta saker i kontext blir det
intressantare att läsa för de som faktiskt är ute efter ett svar, mitt personliga mål är enbart att öka min kunskap men genom att försöka bjuda på det jag vet kanske andra känner att de vill bjuda på sin kunskap (och det sista gynnar min högst personliga agenda)
genom att blotta de antagande och idéer man baserar sin slutsats på ger man andra med god områdeskunskap möjlighet att hitta tankevurpor, om jag gjort sådana som någon hittar är det åter igen vinst för mig då det gynnar med högst personliga agenda
Mer komplicerad än så är inte min agenda här. Att vissa här irriterar sig måste ju långt mer bero på att det jag skriver råkar på något sätt strida mot vad det TYCKER ska vara sanningen, för så här långt har jag haft väsentligt mycket mer rätt än fel.
Om det är någon tröst i folks AMD vs Intel korståg är min övertygelse att svärmen med företag som använder ARM (eller möjligen RISC-V) kommer i slutändan "vinna" över båda dessa företag. x86 designer är allt för komplicerade att validera så AMD/Intel kommer få en näst intill omöjligt uppgift att hänga med allt eftersom ARM-designerna blir kraftigare. D.v.s. vi kommer få en repris på hur x86 slog ut de mer avancerade (och dyrare) RISC CPUer under 90-talet, om det tar 10 eller 30 år vet jag inte men det kommer hända.
Skrivet av liket:
Visst att Windows Scheduler fungerar som den ska men borde det inte gå att optimera den att hålla kommunikationen mellan CCX-modulerna till ett minimum? Kanske även att spelutvecklare optimerar den för det?
Det kan öppna för mer spelutveckling med NUMA-stöd också om man ska vara extremt optimistisk.
Spelutvecklare kan absolut optimera för någon som CCX designen, det görs ju redan idag på konsolerna som har en väldigt snarlik design.
Däremot har de som hävdar att en generell OS-scheduler ska kunna lösa ett sådant problem uppenbarligen noll koll på OS-utveckling. Det är helt beroende på vad trådarna gör som bestämmer om det optimala är att sprida ut lasten över CCX (detta är optimalt för sådant som är "embarrassingly parallel") eller om man ska hålla saker relativt hårt inom en CCX, kanske till och med föredra SMT över andra CCX, detta blir det optimala valet när "tillräckligt mycket" kommunikation sker mellan CPU-trådar.
Ett OS kan omöjligen förstå hur logiken i olika programtrådar är beskaffade, därför ska inte OSet ens försöka göra vissa optimeringar då det blir femti/femti huruvida ett val är positivt eller negativt. Sådana val ska överlåtas till applikationen, både Windows och Linux har de funktion som krävs för att applikationer ska kunna göra dessa optimeringar vid behov.
Vill man dela upp sin Ryzen CPU i två separata schemaläggningsgrupper, där en varje enskild applikation hårt hålls inom en enda grupp om applikationen inte specifikt är skriven så att den förstår gruppbegreppet, kan detta göra redan idag. Här, här och här beskrivs finessen.
För att dela upp 8-kärnors Ryzen i två grupper där varje CCX utgör en grupp, kör detta som administrator
bcdedit.exe /set groupsize 8
och boota om.
Återställ så här
bcdedit.exe /deletevalue groupsize
Skrivet av wowsers:
200 sidor!
Ok så tillbaka till spekulationer som jag har (som det finns chans att jag har pratat om tidigare).
CCX i Zen går genom infinity fabric för att kommunicera med varandra, latency mellan CCX borde då inte vara allt för mycket högre än vad det redan är i Ryzen. Jag har ingen aning om hur infinity fabric fungerar för ett CCX dock, om det fungerar mer som en större ring bus eller mer likt hur CCX pratar mot RAM minne. Det sistnämda låter som det mer troliga efter att se hur mycket prestanda ökar med högre RAM hastigheter och hur ihopkopplat RAM hastigheterna är med infinity fabric
PC Perspective har gjort ett riktigt bra jobb, de är uppenbarligen hyfsat tekniskt insatta i Windows scheduler och vad i en CPU som bör påverka den. De är lite ute sladdar lite när de diskuterar eventuella NUMA-separation för Ryzen, är ju självklart att Windows inte på något sätt kan styra hur minneskontroller är fördelen mellan CCX. Sådant är helt dikterat av kretsdesignen, OS kan bara konstatera hur det ligger till men inte förändra det på något sätt.
De borde ha postat källkoden för sitt program, tyckte det var ett kul test så svängde ihop motsvarande på lunchen. Fungerar både på Windows (testat i Visual Studio 2015) och Linux (testat på Ubuntu server 16.04). Får på nanosekunden samma HT-till-HT latens på Haswell som de fick i sitt test med i7-5960X (som är Haswell), så gissar att de körde exakt samma metod.
Här är programmet för den som vill testa
// -*- compile-command: "make CXXFLAGS=\"-std=c++11 -O2 -pthread\" th_ping" -*-
// Either remove "stdafx.h" on non-Windows platforms, or create an empty file
// with that name.
#include "stdafx.h"
#ifdef WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <iomanip>
#include <iostream>
#include <set>
#include <string>
#include <thread>
#include <vector>
using namespace std;
// SMT on x86, tell the CPU-thread to relax a bit while busy waiting
#ifdef WIN32
#define cpu_relax() __asm { pause }
#else
#if defined(__i386__) || defined(__x86_64)
#define cpu_relax() asm volatile("pause" ::: "memory")
#else
#define cpu_relax() asm volatile ("" ::: "memory")
#endif
#endif
typedef uint32_t num_t;
typedef uint32_t cpu_id;
typedef enum tse {
THR_READY,
THR_STEADY,
THR_GO
} thr_state_e;
// Global state to implement a rendevouz for the two communicating threads
mutex mtx;
condition_variable cv;
thr_state_e thr_state;
static void rendevouz(void)
{
unique_lock<mutex> lock(mtx);
if (thr_state == THR_READY) {
thr_state = THR_STEADY;
cv.wait(lock, [] { return thr_state == THR_GO; });
} else {
thr_state = THR_GO;
cv.notify_all();
}
}
[[noreturn]] static void panic(const char * desc)
{
perror(desc);
exit(EXIT_FAILURE);
}
// Binds a software thread to a specific CPU-thread
static void cpu_bind(cpu_id c)
{
#ifdef WIN32
if (SetThreadAffinityMask(GetCurrentThread(), 1UL << c) == 0) {
panic("SetThreadAffinityMask() failed");
}
#else
cpu_set_t cpu;
CPU_ZERO(&cpu);
CPU_SET(c, &cpu);
if (pthread_setaffinity_np(pthread_self(), sizeof cpu, &cpu) != 0) {
panic("pthread_setaffinity_np() failed");
}
#endif
}
void worker(atomic<num_t> &ch,
atomic<bool> &is_done,
cpu_id cpu_thread,
uint32_t *latency)
{
num_t cnt = (latency == nullptr ? 1 : 0);
cpu_bind(cpu_thread);
rendevouz();
auto startClk = chrono::steady_clock::now();
while (!is_done.load(memory_order_relaxed)) {
if (ch.load(memory_order_relaxed) == cnt) {
cnt = ch.fetch_add(1, memory_order_relaxed) + 2;
} else {
cpu_relax();
}
}
if (latency != nullptr) {
auto endClk = chrono::steady_clock::now();
auto duration = chrono::duration_cast<chrono::nanoseconds>(endClk - startClk);
*latency = duration.count() / cnt;
}
};
static void measure(cpu_id cpu_threads, const set<cpu_id> &cpu_filter)
{
for (cpu_id ping_id = 0; ping_id < cpu_threads - 1; ping_id++) {
for (cpu_id pong_id = ping_id + 1; pong_id < cpu_threads; pong_id++) {
if (cpu_filter.count(ping_id) + cpu_filter.count(pong_id) != 2) {
continue;
}
cout << setw(2) << ping_id << " <-> "
<< setw(2) << left << pong_id << right << flush;
atomic<num_t> channel{ 0 };
atomic<bool> is_done{ false };
uint32_t latency;
thread ping(worker, ref(channel), ref(is_done), ping_id, &latency);
thread pong(worker, ref(channel), ref(is_done), pong_id, nullptr);
this_thread::sleep_for(chrono::seconds(3));
is_done.store(true);
ping.join();
pong.join();
cout << " : " << setw(3) << latency << " ns" << endl;
}
}
}
static set<cpu_id> parse_args(const vector<string> &args, cpu_id cpu_cnt)
{
set<cpu_id> filter;
if (args.size() == 0) {
for (int c = 0; c < cpu_cnt; c++) {
filter.insert(c);
}
} else {
for (auto cpu_thr: args) {
filter.insert(stoi(cpu_thr));
}
}
return filter;
}
int main(int argc, char *argv[])
{
vector<string> args(argv + 1, argv + argc);
cpu_id cpu_threads = thread::hardware_concurrency();
auto cpu_filter = parse_args(args, cpu_threads);
cout << "This system got " << cpu_threads << " CPU-threads" << endl;
measure(cpu_threads, cpu_filter);
}
Dold text
Här är mätningar på några CPUer
i7-4702HQ, är Windows så 0/1, 2/3, 4/5 och 6/7 delar fysisk kärna
This system got 8 CPU-threads
0 <-> 1 : 14 ns
0 <-> 2 : 62 ns
0 <-> 3 : 61 ns
0 <-> 4 : 63 ns
0 <-> 5 : 63 ns
0 <-> 6 : 66 ns
0 <-> 7 : 65 ns
1 <-> 2 : 59 ns
1 <-> 3 : 59 ns
1 <-> 4 : 61 ns
1 <-> 5 : 62 ns
1 <-> 6 : 65 ns
1 <-> 7 : 65 ns
2 <-> 3 : 14 ns
2 <-> 4 : 59 ns
2 <-> 5 : 59 ns
2 <-> 6 : 55 ns
2 <-> 7 : 57 ns
3 <-> 4 : 61 ns
3 <-> 5 : 57 ns
3 <-> 6 : 56 ns
3 <-> 7 : 56 ns
4 <-> 5 : 14 ns
4 <-> 6 : 55 ns
4 <-> 7 : 57 ns
5 <-> 6 : 57 ns
5 <-> 7 : 57 ns
6 <-> 7 : 14 ns
i7-6770HQ, är Linux så 0/4, 1/5, 2/6 och 3/7 delar fysisk kärna
This system got 8 CPU-threads
0 <-> 1 : 56 ns
0 <-> 2 : 58 ns
0 <-> 3 : 58 ns
0 <-> 4 : 19 ns
0 <-> 5 : 57 ns
0 <-> 6 : 58 ns
0 <-> 7 : 60 ns
1 <-> 2 : 54 ns
1 <-> 3 : 55 ns
1 <-> 4 : 57 ns
1 <-> 5 : 19 ns
1 <-> 6 : 54 ns
1 <-> 7 : 55 ns
2 <-> 3 : 57 ns
2 <-> 4 : 56 ns
2 <-> 5 : 55 ns
2 <-> 6 : 19 ns
2 <-> 7 : 57 ns
3 <-> 4 : 59 ns
3 <-> 5 : 56 ns
3 <-> 6 : 57 ns
3 <-> 7 : 19 ns
4 <-> 5 : 56 ns
4 <-> 6 : 58 ns
4 <-> 7 : 58 ns
5 <-> 6 : 55 ns
5 <-> 7 : 55 ns
6 <-> 7 : 57 ns
e5 2699v4, 0-21 är CPU#0, 22-43 är CPU#1, 44-65 är CPU#0 andra tråden, 66-87 är CPU#1 andra tråden
This system got 88 CPU-threads
0 <-> 11 : 87 ns
0 <-> 22 : 179 ns
0 <-> 33 : 202 ns
0 <-> 44 : 17 ns
0 <-> 55 : 86 ns
0 <-> 66 : 182 ns
0 <-> 77 : 188 ns
11 <-> 22 : 176 ns
11 <-> 33 : 175 ns
11 <-> 44 : 85 ns
11 <-> 55 : 17 ns
11 <-> 66 : 186 ns
11 <-> 77 : 183 ns
22 <-> 33 : 75 ns
22 <-> 44 : 182 ns
22 <-> 55 : 183 ns
22 <-> 66 : 16 ns
22 <-> 77 : 75 ns
33 <-> 44 : 170 ns
33 <-> 55 : 157 ns
33 <-> 66 : 73 ns
33 <-> 77 : 16 ns
44 <-> 55 : 80 ns
44 <-> 66 : 186 ns
44 <-> 77 : 158 ns
55 <-> 66 : 183 ns
55 <-> 77 : 147 ns
66 <-> 77 : 74 ns
Dold text
Notera att Skylake har högre HT-to-HT latens jämfört med Haswell. Värdet i sig är inte jätteviktigt, det viktiga är att latensen är tillräckligt liten för att inte orsaka pipeline-stalls. Typiskt får man balansera bandbredd mot latens, högre bandbredd betydligt ofta tyvärr också högre latens.
En sak som varje ny x86 design från både AMD och Intel ökar är hur många instruktioner som kan vara "in-flight", så länge som latensen ligger på en nivå så man med råge inte slår i "in-flight" gränsen så är latensen i sig inte jätteviktig. Dagens high-end x86or kan hålla runt 200 instruktioner i luften samtidigt, med ett genomsnittlig IPC på mellan 1,5-2,5 och runt 4 GHz slår man i taken vid ~100 ns och man vill nog ha en hyfsad marginal.
Man ser också att latensen mellan CCX är rätt snarlik latensen mellan två CPU-socklar i ett dual-socket E5 system. Vilket är helt väntat då Infinity fabric är precis som vilken annan interconnect som helst, då med snarlik latens (går att få lite bättre latens i Ryzen då allt är på samma krets så betydligt kortare avstånd).
Men undrar fortfarande varför ingen ser elefanten i rummet. Latensen förklarar bara varför Ryzen skalar lite sämre i spel, framförallt DX12 spel, som är riktigt ivriga på att använda många CPU-kärnor.
Förklaringen till varför det finns en skillnad pekade jag redan när Zens mikroarkitektur lanserades, Zen har en distribuerad scheduler för heltal och Core har en central scheduler, Zen har ungefär samma "bredd" som Haswell/Broadwell -> heltals IPC kommer vara lägre för Zen än för Haswell/Broadwell.
Nu efter lanseringen har vi siffror som kvantifierar skillnad i heltals IPC. SPECInt_2006, Java Script benchmarks, kompilator benchmarks och spel visar exakt samma sak. Är det någon i detta läge som tror det är en slump?
För skalära flyttal presterar Zen lysande, där är det samma IPC i Zen som Broadwell i de flesta fall (och Broadwell har några procent högre IPC här än Haswell).
För skalära heltal ligger Zen på Sandy Bridge nivå vid enkeltrådade fallet och vid Ivy Bridge när SMT används.
Tänk själv: R7 1700 och i7-7700K är helt jämna när det kommer till kompilering. Kompilering skalar i det närmaste perfekt med kärnor (ett p-värde i Amdahls på 96-98 %), kompenserat för antal kärnor och frekvens (antar 3,15 GHz för R7 1700 och 4,4 GHz för i7 7700K) så har Kaby Lake ~40 % högre IPC vid kompilering.
För mig blir därför i7-7770K då det klart bästa valet på skrivbordet, bättre än både Ryzen och E-serien (även om man ignorerar priset) då programvarutveckling 2017 betyder en massa skriv-inkrementell_kompilering-testa cykler. Så går >10 inkrementella kompileringar (som använder en eller någon enstaka CPU-kärna) på varje "rebuild-all" (som skalar perfekt med kärnor).
Så går inte att att generellt säga vilken CPU som är bäst, man måste förstå sitt eget användarmönster för att veta detta. För spel är det absolut minimal sannolikhet att dagens Ryzen någosin kommer passera i7-7700K.
Vad man kan hoppas på är att Intel känner trycket från AMD, lanserar man en 6-kärnig Coffee-Lake på >=4,2 GHz när alla kärnor jobbar skulle jag säga att har den bästa desktop CPUn för allt utom extrema nischer. Men en sådan modell skulle i praktiken göra hela E-serien meninslös, så blir väldigt spännade i Q4!