Megfigyeljük az A11 – Heterogén Mag

Eredeti Mike Ash, kifüggesztett- https://www.mikeash.com/pyblog/friday-qa-2017-11-10-observing-the-a11s-heterogenous-cores.html

Az Apple legújabb mobil CPU, az A11, hoz egy új szintre a heterogén informatikai iOS, mind a magas, illetve alacsony teljesítmény magok, hogy mindig be vannak kapcsolva. A kiadás az iPhone X, elhatároztam, hogy ha lehet megfigyelni ezeket a heterogén mag akcióban.

(Igen, tisztában vagyok vele, hogy A11 eszközöket lehet beszerezni héttel ezelőtt, amikor az iPhone 8 jött ki, de én nem ismerek senkit, akinek ilyen van, de nehéz volt a munka, sok izgalom érte az iPhone X jönnek után nem sokkal.)

Rövid Áttekintés
Többmagos CPU már az Apple világon, mivel legalább a Power Mac G5, amely elérhető volt a két mag CPU, akár két CPU egy gép.

Lett a norma számos részén a számítástechnika világában. Egy természetes reakció, hogy egyre tranzisztor számít, mint a szilícium chip gyártás technológia továbbra is aszimptotikus március felé végtelen. CPU tervezők mindig több tranzisztort, hogy a hardver gyorsabban, de a csökkenő hozadék. Ahelyett, hogy több tranzisztort a gyorshajtás fel egy-menetes teljesítmény, azok a tranzisztorok lehet használni, hogy hatékonyan fel több CPU egyetlen chip. Azok ismertté vált, mint a CPU magok.

Manapság már lehet kapni CPU több tucat vagy akár több száz mag. Ez gyakran nem a legjobb kompromisszum, mivel sok szoftver nem kihasználni, hogy sok. Még jobb is lehet, hogy kevesebb, gyorsabb, magok helyett. Manapság tipikus felhasználó néző számítógépek van valahol a környéken, a két 16 mag.

Általában az összes mag rendszerben azonosak. Szoftver futtatható bármely vagy az összes őket, de ez nem változtat semmin. Ha ásunk elég mélyre, néhány CPU készletek mag, amely adatátviteli a csoporton belül, sokkal gyorsabban, mint a csoporton kívül. Ez így van értelme, hogy tegye több szálon dolgozik ugyanazon adatokkal együtt belül egy ilyen csoport. Ez az egyik oka a szál affinitás API. Még így is, az egyes magok még mindig ugyanaz, csak nem kapcsolódnak 100% szimmetrikusan.

Tavaly az Apple bemutatta az A10 CPU heterogén mag. Ez egy négy-core CPU, de azok a magok nem azonos. Ehelyett két nagy teljesítményű mag, két nagy hatékonyságú mag. A magas hatásfokú magok lassabb, de sokat fogyasztanak kevesebb energiát. A feladatok nem kell kitölteni, amilyen gyorsan csak lehetséges, fut a magas hatásfokú mag teszi őket használni, sokkal kevesebb energiát fogyaszt. A rendszer váltás a futó szoftver a magas teljesítményű, magok, vagy a magas hatásfokú magok attól függően, hogy a munkaterhelés bármely adott időpontban.

Ez egy jó ötlet, mivel az iPhone elemes pedig nagyon akarom használni, mint kis teljesítmény a lehető leghamarabb, de sok a munka, hogy iphone tenned, hogy viszonylag hétköznapi feladatokat, hogy nem kell szuper gyors, mint letölteni a legfrissebb tweet a patak, vagy betöltése a következő darab audio adatokat a flash tároló. Ez egy kicsit pazarló, bár, mivel a két magot, csak ül, nem csinálsz semmit, bármely adott időpontban. Ez az, amit akarsz, a nagy hatásfokú módban, mivel az egész ötlet az, hogy kevesebb hardver annak érdekében, hogy kevesebb energiát fogyaszt, de a high-performance mód sajnálatos, hogy nem tudja kihasználni a két tétlen magas hatásfokú mag.

Ebben az évben, az Apple bemutatta az A11, amely veszi a koncepció egy lépéssel tovább. Hat mag: két nagy teljesítményt négy, nagy hatékonyság. Ellentétben az A10, A11 tudja használni mind a hat mag egyszerre. Ha a munkát igényel, az A11 képes futtatni a két szál, a magas teljesítményű, magok, ugyanakkor fut még négy szálak a magas hatásfokú mag.

Tervezés
Elkezdtem gondolkodni, hogy hogyan lehetne elkapni a törvény. A rendszer valószínűleg mozog szálak körül rendszeresen, így az időzítés egy régóta futó számítás valószínűleg nem árultak el sokat. Rövid futó számítások lenne nehéz kezelni, mivel szeretnénk meggyőződni arról, hogy pontosan hat megy egyszerre.

Úgy döntöttem, írok egy tesztet, ami sok kis számítások minden szál. Ez lenne majd a mintát az időzítés egy pár ilyen kis számítások során a folyamat. Remélhetőleg lesz elég gyorsan, hogy nem valószínű, hogy vándorolnak között magok során a minta.

A következő kérdés az volt, milyen számítás elvégzésére. Elkezdtem csinálni egy SHA-256 a jelenlegi iteráció szám, de attól féltem, hogy különleges kriptográfiai utasításokat zavarhatja az eredményeket. Aztán megpróbáltam egy egyszerű négyzetgyök algoritmus a jelenlegi iteráció szám. Azt hittem, hogy ez talán forgalomba is nagy hangsúlyt lebegőpontos teljesítményt, így végre fel, hogy egy egész négyzetgyök helyett. Végső soron mind a három adta ugyanazt az eredményt. Folytattam az egész négyzetgyök, mivel egész számítások úgy tűnik, mintha az uralkodó munkaterhelés a legtöbb szoftver.

Az elmélet az volt, hogy ez kell mutatni egy erősen bimodal megoszlása futó alkalommal az A11. Működött? Olvass alább, hogy megtudja!

Kód
Minden szál fut egy funkció, ami száma ismétlések elvégezni, valamint a mintavételi intervallum. Vissza a futásidejével kapcsolatosan minden minta egy tömb, kifejezve az egység, amelyet a mach_absolute_time hívjon:

    func ThreadFunc(iterations: UInt64, interval: UInt64) -> [UInt64] {

Ez létrehoz egy tömböt, ami végül tartani a mintában szereplő versenyidő:

       var times: [UInt64] = []

Aztán belép egy hurkot a megadott számú ismétlések:

        for i in 1 ... iterations {

Mielőtt bármilyen munka, megragadja az aktuális időt. Ez történik, függetlenül attól, hogy ez egy futni minta, egy kísérlet arra, hogy a mintába fel nem vett ismétléseket, mint hasonló, mint lehetséges, hogy a mintában szereplő ismétlések:

            let start = mach_absolute_time()

ismétlések egy UInt64 de azt akarjuk, hogy a munka Int64 számok, így átalakítani, valamint a rejtekhely egy változó:

            let x = Int64(i)

Én hajtotta végre a Babiloni módszer számítási egy négyzetgyök. Ez abból áll, hogy egy hiszem, a négyzetgyök, akkor iteratively finomítás azt guess, számítástechnikai az átlagos hiszem, x / guess. Halad, amíg a kívánt pontosság eléréséig. Ez nem egy nagyon gyors módszer, de nem érdekel a sebesség, más, mint a következetesség. Én hajtották végre ezt az algoritmust, hogy fut 1024 ismétlések, ami túl sok a semmilyen elfogadható eredmény, de ez biztosítja a jó munka mennyiségét a benchmarking célokra:

            var guess = x
            for _ in 0 ... 1024 {
                guess = (guess + x / guess) / 2
            }

Úgy volt, hogy győződjön meg arról, hogy a fordító valóban végre ez a számítás nem dobja ki az egész, mint szükségtelen. Ez azt jelentette, használnom kellett az eredmény valahogy. Hozzáadtam egy bábu ellenőrizze, hogy a számított négyzetgyök volt, ahogy az általában egy, a print (amely nem lehet optimalizált el) ebben az esetben:

            if abs(guess * guess - x) > 1000000000 {
                print("Found a really inexact square root! \(guess * guess) \(x)")
            }

Egyik sem az igazi fut valaha megütni a nyomtatás, tehát nem volt IO ferdíteni az időzítés.

A munka elkészül, ez lesz az aktuális időt újra:

            let end = mach_absolute_time()

Ha ez egy mintavételi pontra, add hozzá a teljes futási erre a pontra, hogy az times tömb:

            if i % interval == 0 {
                times.append(end - start)
            }
        }

Egyszer minden iteráció után, vissza a times:

        return times
    }

Ez a kód az egyetlen szál. Mi is kell a kódot, hogy spawn ezek a szálak, majd elemezzük az eredményeket. Ezt a kódot azzal kezdődik, hogy egy kijelentés, ami a szálak száma nem kapsz száma ismétlések, hogy fut, pedig a mintavételi intervallum:

    let threadCount = 6
    let iterations: UInt64 = 1000000
    let interval = iterations / 20

Ettől egy tömb, amely összegyűjti az összes a mintában szereplő idők:

    var times: [UInt64] = []

Használja az NSCondition tárgy szinkronizálni hozzáférést alkalommal, majd várni az eredményt, hogy:

    let cond = NSCondition()

Követni fogjuk az aktív szálak hogy tudjuk, mikor mindenki elkészült:

    var runningThreads = threadCount

A kezdeti beállítás kész, kezd ívás szálak:

    for _ in 0 ..< threadCount {
        Thread.detachNewThread({

Az első dolog, amit minden szál tesz hívás ThreadFunc, hogy ezt a munkát gyülekező eredmények:

            let oneTimes = ThreadFunc(iterations: iterations, interval: interval)

Ha az eredmények nem jönnek vissza, fűzi őket times, ami jelzi, hogy ez a szál befejeződött:

            cond.lock()
            times.append(contentsOf: oneTimes)
            runningThreads -= 1
            cond.signal()
            cond.unlock()
        })
    }

Újra az ellenőrző kódot, csak arra vár, hogy a futó szálak teljes:

    cond.lock()
    while runningThreads > 0 {
        cond.wait()
    }
    cond.unlock()

Ezen a ponton, minden minta az times tömb. Azok a minták tekintetében az egységek által visszaadott mach_absolute_time, ami nem hasznos, saját, bár a relatív értékek még mindig tanulságos. Fogjuk átalakítani, hogy ns:

    let nanoseconds = times.map({ machToNanoseconds($0) })

A következő, fut egy nagyon egyszerű fürtözés algoritmus, amely csak lépéseket át a mintát, úgy néz ki, a hiányosságok, ahol a relatív különbség a két minta nagyobb, mint egy küszöböt. Nem voltam biztos benne, hogy melyik küszöbérték megfelelő lenne, szóval volt itt egy csokor:

    for threshold in [0.01, 0.02, 0.03, 0.04, 0.05, 0.1] {
        print("Threshold: \(threshold)")
        let clusters = cluster(nanoseconds, threshold: threshold)

Ez a függvény minden egyes klaszter, mint egy tömb értékei belül a klaszter. A kódot ezután kiszámítja az átlag, medián, szórás az egyes klaszter, illetve nyomtatja ki őket:

        for cluster in clusters {
            let mean = cluster.reduce(0, +) / Double(cluster.count)
            let median = cluster[cluster.count / 2]
            let stddev = sqrt(cluster.map({ ($0 - mean) * ($0 - mean) }).reduce(0, +) / Double(cluster.count))

            print("count: \(cluster.count) - mean: \(mean) - median: \(median) - stddev: \(stddev)")
        }
        print("----------")
    }

Ez az! Készen állunk, hogy az eredményeket.

Eredmények
Először futott be az iPhone 6+ generálni valami viszonyítási alap. A küszöb 0.05 úgy tűnt, hogy a lehető legjobb halmozódást. Ott vannak azok, eredmények:

    Threshold: 0.05
    count: 120 - mean: 10993.4027777778 - median: 10958.3333333333 - stddev: 75.1148490502343

Minden mintát vesz, majdnem ugyanannyi időt. Ők 11 körül mikroszekundum, szórás mellett csak 75 ns.

Itt vannak az eredmények az iPhone X:

    Threshold: 0.05
    count: 54 - mean: 6969.90740740741 - median: 6958.33333333333 - stddev: 24.6068190109599
    count: 65 - mean: 9082.69230769231 - median: 9250.0 - stddev: 278.358695652034
    count: 1 - mean: 14125.0 - median: 14125.0 - stddev: 0.0

Van egy kiugró érték, ami azt mutatja, elég következetesen több fut. Nem vagyok teljesen biztos benne, miért lenne olyan következetes. Talán vesz egy pillanatra, hogy rámpa a CPU teljes sebességgel? Figyelmen kívül hagyva a kiugró, látjuk, hogy a heterogén mag egyértelműen. Van egy szűk halmaz köré ~7 mikroszekundum, egy keskeny klaszter köré ~9 mikroszekundum, a kettő között semmi.

A sebesség különbség kisebb, mint amire számítottam, de a kísérletek tartományban változott egy kicsit, attól függően, hogy milyen típusú munka. Ez különösen a microbenchmark valószínűleg bottlenecked az egész hadosztály, amely nem a képviselő feladata.

Függetlenül attól, hogy a jel egyértelműen egy darab mintát futó jelentősen gyorsabb, mint a másik darab, amely szemlélteti a nagy teljesítményű, magas hatásfokú mag dolgozik egyszerre.

Következtetés
Érdekes volt követni a fejlődését az Apple CPU, illetve a heterogén magok a legújabb iterációs nagyon klassz. Vártam, hogy valami munkát, hogy megfigyeljék őket, de a vége az lett, hogy egyszerű. A futás egy hosszú sorozat gyors számítások több szálak, valamint a mintavételi néhány közülük, a különböző magok nyilvánvalóvá válnak.

 

 

Vissza a főoldalra

 

Leave a Reply

Your email address will not be published. Required fields are marked *