web-dev-qa-db-ja.com

マルチコアアセンブリ言語はどのように見えますか?

昔々、たとえばx86アセンブラーを作成するには、「値5でEDXレジスタをロードする」、「EDXレジスタをインクリメントする」などの指示があります。

4コア(またはそれ以上)の最新のCPUでは、マシンコードレベルでは、4つの別個のCPUがあるように見えます(つまり、4つの異なる「EDX」レジスタがあるだけですか)。もしそうなら、「EDXレジスタをインクリメントする」と言うとき、どのCPUのEDXレジスタがインクリメントされるかを決定するものは何ですか? x86アセンブラーに「CPUコンテキスト」または「スレッド」の概念はありますか?

コア間の通信/同期はどのように機能しますか?

オペレーティングシステムを記述している場合、異なるコアで実行をスケジュールできるようにするために、ハードウェアを介してどのようなメカニズムが公開されていますか?特別な権限のある命令ですか?

マルチコアCPU用に最適化コンパイラ/バイトコードVMを記述している場合、すべてのコアで効率的に実行されるコードを生成するためにx86などについて具体的に何を知る必要がありますか?

マルチコア機能をサポートするためにx86マシンコードにどのような変更が加えられましたか?

220

これは質問に対する直接的な答えではありませんが、コメントに表示される質問に対する答えです。基本的に、問題は、ハードウェアがマルチスレッド操作にどのようなサポートを提供するかです。

Nicholas Flyntが正しかった 、少なくともx86に関しては。マルチスレッド環境(ハイパースレッディング、マルチコア、またはマルチプロセッサ)では、Bootstrap thread(通常、プロセッサ0のコア0のスレッド0 )アドレス0xfffffff0からコードのフェッチを開始します。他のすべてのスレッドは、Wait-for-SIPIと呼ばれる特別なスリープ状態で起動します。初期化の一部として、プライマリスレッドは、特別なプロセッサ間割り込み(IPI)をAPIC経由でSIPI(スタートアップIPI)と呼ばれるWFSにある各スレッドに送信します。 SIPIには、そのスレッドがコードのフェッチを開始するアドレスが含まれています。

このメカニズムにより、各スレッドは異なるアドレスからコードを実行できます。必要なのは、独自のテーブルとメッセージングキューを設定するための各スレッドのソフトウェアサポートです。 OSはthoseを使用して、実際のマルチスレッドスケジューリングを行います。

Nicholasが書いたように、実際のアセンブリに関する限り、シングルスレッドアプリケーションとマルチスレッドアプリケーションのアセンブリに違いはありません。各論理スレッドには独自のレジスタセットがあるため、次のように記述します。

mov edx, 0

現在実行中のスレッドEDXのみを更新します。単一のAssembly命令を使用して別のプロセッサでEDXを変更する方法はありません。独自のEDXを更新するコードを実行するように別のスレッドに指示するようOSに依頼するには、何らかのシステムコールが必要です。

136
Nathan Fellman

Intel x86最小実行可能ベアメタルの例

すべての必須のボイラープレートを備えた実行可能なベアメタルの例 。すべての主要部分については以下で説明します。

Ubuntu 15.10 QEMU 2.3.0およびLenovo ThinkPad T400 実際のハードウェアゲスト でテスト済み。

Intel Manual Volume 3 System Programming Guide-325384-056US September 2015 は、8章、9章、および10章のSMPをカバーしています。

表8-1。 「ブロードキャストINIT-SIPI-SIPIシーケンスとタイムアウトの選択」には、基本的に機能する例が含まれています。

MOV ESI, ICR_LOW    ; Load address of ICR low dword into ESI.
MOV EAX, 000C4500H  ; Load ICR encoding for broadcast INIT IPI
                    ; to all APs into EAX.
MOV [ESI], EAX      ; Broadcast INIT IPI to all APs
; 10-millisecond delay loop.
MOV EAX, 000C46XXH  ; Load ICR encoding for broadcast SIPI IP
                    ; to all APs into EAX, where xx is the vector computed in step 10.
MOV [ESI], EAX      ; Broadcast SIPI IPI to all APs
; 200-microsecond delay loop
MOV [ESI], EAX      ; Broadcast second SIPI IPI to all APs
                    ; Waits for the timer interrupt until the timer expires

そのコードについて:

  1. ほとんどのオペレーティングシステムは、これらの操作のほとんどをリング3(ユーザープログラム)から不可能にします。

    そのため、自由にプレイするには独自のカーネルを作成する必要があります。ユーザーランドのLinuxプログラムは機能しません。

  2. 最初に、bootstrapプロセッサー(BSP)と呼ばれる単一のプロセッサーが実行されます。

    Inter Processor Interrupts(IPI) と呼ばれる特別な割り込みを介して、他のもの(アプリケーションプロセッサ(AP)と呼ばれる)を起動する必要があります。

    これらの割り込みは、割り込みコマンドレジスタ(ICR)を介してAdvanced Programmable Interrupt Controller(APIC)をプログラミングすることで実行できます。

    ICRの形式は、10.6「ISSUING INTERPROCESSOR INTERRUPTS」に記載されています。

    IPIは、ICRに書き込むとすぐに発生します。

  3. ICR_LOWは、8.4.4「MP初期化の例」で次のように定義されています。

    ICR_LOW EQU 0FEE00300H
    

    マジック値0FEE00300は、表10-1「ローカルAPICレジスタアドレスマップ」に記載されているICRのメモリアドレスです。

  4. この例では、最も単純な方法が使用されています。現在のプロセッサを除く他のすべてのプロセッサに配信されるブロードキャストIPIを送信するようにICRを設定します。

    しかし、 で推奨されており、いくつかの によって推奨されており、BIOSによって ACPIテーブルまたはIntelのMP構成テーブル )、必要なもののみを1つずつ起動します。

  5. 000C46XXHXXは、プロセッサが次のように実行する最初の命令のアドレスをエンコードします。

    CS = XX * 0x100
    IP = 0
    

    CSはアドレスを0x10 で乗算するため、最初の命令の実際のメモリアドレスは次のとおりです。

    XX * 0x1000
    

    たとえば、XX == 1の場合、プロセッサは0x1000から開始します。

    次に、そのメモリの場所で実行する16ビットのリアルモードコードがあることを確認する必要があります。で:

    cld
    mov $init_len, %ecx
    mov $init, %esi
    mov 0x1000, %edi
    rep movsb
    
    .code16
    init:
        xor %ax, %ax
        mov %ax, %ds
        /* Do stuff. */
        hlt
    .equ init_len, . - init
    

    別の可能性として、リンカスクリプトの使用があります。

  6. 遅延ループは機能するための迷惑な部分です。そのようなスリープを正確に行うための超簡単な方法はありません。

    可能な方法は次のとおりです。

    • PIT(私の例で使用)
    • HPET
    • 上記でビジーループの時間を調整し、代わりに使用します

    関連: DOS x86アセンブリで画面に数字を表示して1秒間スリープさせる方法は?

  7. 16ビットには高すぎるアドレス0FEE00300Hに書き込むときに、これが機能するには、初期プロセッサが保護モードである必要があると思います

  8. プロセッサ間で通信するために、メインプロセスでスピンロックを使用し、2番目のコアからロックを変更できます。

    メモリの書き戻しが行われていることを確認する必要があります。 wbinvdを介して。

プロセッサ間の共有状態

8.7.1「論理プロセッサの状態」には次のように記載されています。

次の機能は、Intel Hyper-Threading TechnologyをサポートするIntel 64またはIA-32プロセッサ内の論理プロセッサのアーキテクチャ状態の一部です。機能は、次の3つのグループに分類できます。

  • 論理プロセッサーごとに複製
  • 物理プロセッサ内の論理プロセッサで共有
  • 実装に応じて、共有または複製

次の機能は、論理プロセッサごとに複製されます。

  • 汎用レジスター(EAX、EBX、ECX、EDX、ESI、EDI、ESP、およびEBP)
  • セグメントレジスタ(CS、DS、SS、ES、FS、およびGS)
  • EFLAGSおよびEIPレジスタ。各論理プロセッサのCSおよびEIP/RIPレジスタは、論理プロセッサによって実行されているスレッドの命令ストリームを指していることに注意してください。
  • x87 FPUレジスタ(ST0〜ST7、ステータスワード、コントロールワード、タグワード、データオペランドポインター、および命令ポインター)
  • MMXレジスタ(MM0〜MM7)
  • XMMレジスタ(XMM0〜XMM7)およびMXCSRレジスタ
  • 制御レジスタおよびシステムテーブルポインターレジスタ(GDTR、LDTR、IDTR、タスクレジスタ)
  • デバッグレジスタ(DR0、DR1、DR2、DR3、DR6、DR7)およびデバッグ制御MSR
  • マシンチェックグローバルステータス(IA32_MCG_STATUS)およびマシンチェック機能(IA32_MCG_CAP)MSR
  • サーマルクロック変調およびACPI電力管理制御MSR
  • タイムスタンプカウンターMSR
  • ページ属性テーブル(PAT)を含む、他のほとんどのMSRレジスタ。以下の例外を参照してください。
  • ローカルAPICレジスタ。
  • 追加の汎用レジスター(R8-R15)、XMMレジスター(XMM8-XMM15)、制御レジスター、Intel 64プロセッサー上のIA32_EFER。

次の機能は、論理プロセッサによって共有されます。

  • メモリタイプ範囲レジスタ(MTRR)

次の機能を共有するか複製するかは実装固有です。

  • IA32_MISC_ENABLE MSR(MSRアドレス1A0H)
  • Machine Check Architecture(MCA)MSR(IA32_MCG_STATUSおよびIA32_MCG_CAP MSRを除く)
  • パフォーマンス監視制御およびカウンターMSR

キャッシュ共有については、次で説明します。

Intelハイパースレッドは、個別のコアよりも優れたキャッシュとパイプライン共有を備えています: https://superuser.com/questions/133082/hyper-threading-and-dual-core-whats-the-difference/995858 #995858

Linuxカーネル4.2

主な初期化アクションはArch/x86/kernel/smpboot.cにあるようです。

ARM最小実行可能ベアメタルの例

ここでは、QEMUの最小限の実行可能なARMv8 aarch64の例を示します。

.global mystart
mystart:
    /* Reset spinlock. */
    mov x0, #0
    ldr x1, =spinlock
    str x0, [x1]

    /* Read cpu id into x1.
     * TODO: cores beyond 4th?
     * Mnemonic: Main Processor ID Register
     */
    mrs x1, mpidr_el1
    ands x1, x1, 3
    beq cpu0_only
cpu1_only:
    /* Only CPU 1 reaches this point and sets the spinlock. */
    mov x0, 1
    ldr x1, =spinlock
    str x0, [x1]
    /* Ensure that CPU 0 sees the write right now.
     * Optional, but could save some useless CPU 1 loops.
     */
    dmb sy
    /* Wake up CPU 0 if it is sleeping on wfe.
     * Optional, but could save power on a real system.
     */
    sev
cpu1_sleep_forever:
    /* Hint CPU 1 to enter low power mode.
     * Optional, but could save power on a real system.
     */
    wfe
    b cpu1_sleep_forever
cpu0_only:
    /* Only CPU 0 reaches this point. */

    /* Wake up CPU 1 from initial sleep!
     * See:https://github.com/cirosantilli/linux-kernel-module-cheat#psci
     */
    /* PCSI function identifier: CPU_ON. */
    ldr w0, =0xc4000003
    /* Argument 1: target_cpu */
    mov x1, 1
    /* Argument 2: entry_point_address */
    ldr x2, =cpu1_only
    /* Argument 3: context_id */
    mov x3, 0
    /* Unused hvc args: the Linux kernel zeroes them,
     * but I don't think it is required.
     */
    hvc 0

spinlock_start:
    ldr x0, spinlock
    /* Hint CPU 0 to enter low power mode. */
    wfe
    cbz x0, spinlock_start

    /* Semihost exit. */
    mov x1, 0x26
    movk x1, 2, lsl 16
    str x1, [sp, 0]
    mov x0, 0
    str x0, [sp, 8]
    mov x1, sp
    mov w0, 0x18
    hlt 0xf000

spinlock:
    .skip 8

GitHubアップストリーム

組み立てて実行する:

aarch64-linux-gnu-gcc \
  -mcpu=cortex-a57 \
  -nostdlib \
  -nostartfiles \
  -Wl,--section-start=.text=0x40000000 \
  -Wl,-N \
  -o aarch64.elf \
  -T link.ld \
  aarch64.S \
;
qemu-system-aarch64 \
  -machine virt \
  -cpu cortex-a57 \
  -d in_asm \
  -kernel aarch64.elf \
  -nographic \
  -semihosting \
  -smp 2 \
;

この例では、CPU 0をスピンロックループに入れ、CPU 1がスピンロックを解除したときにのみ終了します。

スピンロックの後、CPU 0は semihost exit call を実行し、QEMUを終了します。

-smp 1を使用して1つのCPUのみでQEMUを起動すると、スピンロックでシミュレーションが永久に停止します。

CPU 1はPSCIインターフェースで起動されます。詳細は次のとおりです: ARM:他のCPUコア/ APを起動/起動/起動し、実行開始アドレスを渡しますか?

上流バージョン にもgem5で動作するようにいくつかの微調整がありますので、パフォーマンス特性も実験できます。

私は実際のハードウェアでテストしていないので、これがどれほど移植性があるかはわかりません。次のRaspberry Piの参考文献が参考になります。

このドキュメントでは、ARM同期プリミティブを使用して、複数のコアで楽しいことを行うために使用できるガイダンスについて説明します。 http://infocenter.arm.com /help/topic/com.arm.doc.dht0008a/DHT0008A_arm_synchronization_primitives.pdf

Ubuntu 18.10、GCC 8.2.0、Binutils 2.31.1、QEMU 2.12.0でテスト済み。

より便利なプログラミングのための次のステップ

前の例では、セカンダリCPUを起動し、専用の命令を使用して基本的なメモリ同期を実行します。

しかし、マルチコアシステムをプログラムしやすくするため、たとえば [〜#〜] posix [〜#〜]pthreadsのように、次のより複雑なトピックに進む必要があります。

  • 割り込みを設定し、現在実行するスレッドを定期的に決定するタイマーを実行します。これは、 プリエンプティブマルチスレッド として知られています。

    このようなシステムでは、スレッドレジスタの開始および停止時にスレッドレジスタを保存および復元する必要もあります。

    非プリエンプティブマルチタスクシステムを使用することも可能ですが、すべてのスレッドが(たとえば、pthread_yield実装で)生成されるようにコードを変更する必要があり、ワークロードのバランスを取ることが難しくなります。

    単純なベアメタルタイマーの例を次に示します。

  • メモリの競合に対処します。特に、各スレッドには 一意のスタック が必要です。

    最大スタックサイズを固定するようにスレッドを制限することもできますが、これに対処するより良い方法は paging を使用することです。スタック。

これらは、Linuxカーネルまたはその他のオペレーティングシステムを使用する正当な理由です:-)

ユーザーランドのメモリ同期プリミティブ

スレッドの開始/停止/管理は一般にユーザーランドの範囲を超えていますが、ユーザーランドスレッドからのアセンブリ命令を使用して、より高価なシステムコールを発生させることなくメモリアクセスを同期できます。

もちろん、これらの低レベルのプリミティブを移植可能にラップするライブラリを使用することをお勧めします。 C++標準自体は、 <atomic> ヘッダー、特に std::memory_order 。実現可能なすべてのメモリセマンティクスを網羅しているかどうかはわかりませんが、そうかもしれません。

より微妙なセマンティクスは、 ロックフリーのデータ構造 のコンテキストに特に関連しており、特定の場合にパフォーマンスの利点を提供できます。これらを実装するには、さまざまなタイプのメモリバリアについて少し学ぶ必要があります。 https://preshing.com/20120710/memory-barriers-are-like-source-control -operations /

たとえば、Boostには https://www.boost.org/doc/libs/1_63_0/doc/html/lockfree.html

インラインアセンブリを使用した最小限の役に立たないC++ x86_64/aarch64の例は、このような命令の基本的な使用法を主に説明しています。

main.cpp

#include <atomic>
#include <cassert>
#include <iostream>
#include <thread>
#include <vector>

std::atomic_ulong my_atomic_ulong(0);
unsigned long my_non_atomic_ulong = 0;
#if defined(__x86_64__) || defined(__aarch64__)
unsigned long my_Arch_atomic_ulong = 0;
unsigned long my_Arch_non_atomic_ulong = 0;
#endif
size_t niters;

void threadMain() {
    for (size_t i = 0; i < niters; ++i) {
        my_atomic_ulong++;
        my_non_atomic_ulong++;
#if defined(__x86_64__)
        __asm__ __volatile__ (
            "incq %0;"
            : "+m" (my_Arch_non_atomic_ulong)
            :
            :
        );
        // https://github.com/cirosantilli/linux-kernel-module-cheat#x86-lock-prefix
        __asm__ __volatile__ (
            "lock;"
            "incq %0;"
            : "+m" (my_Arch_atomic_ulong)
            :
            :
        );
#Elif defined(__aarch64__)
        __asm__ __volatile__ (
            "add %0, %0, 1;"
            : "+r" (my_Arch_non_atomic_ulong)
            :
            :
        );
        // https://github.com/cirosantilli/linux-kernel-module-cheat#arm-lse
        __asm__ __volatile__ (
            "ldadd %[inc], xzr, [%[addr]];"
            : "=m" (my_Arch_atomic_ulong)
            : [inc] "r" (1),
              [addr] "r" (&my_Arch_atomic_ulong)
            :
        );
#endif
    }
}

int main(int argc, char **argv) {
    size_t nthreads;
    if (argc > 1) {
        nthreads = std::stoull(argv[1], NULL, 0);
    } else {
        nthreads = 2;
    }
    if (argc > 2) {
        niters = std::stoull(argv[2], NULL, 0);
    } else {
        niters = 10000;
    }
    std::vector<std::thread> threads(nthreads);
    for (size_t i = 0; i < nthreads; ++i)
        threads[i] = std::thread(threadMain);
    for (size_t i = 0; i < nthreads; ++i)
        threads[i].join();
    assert(my_atomic_ulong.load() == nthreads * niters);
    // We can also use the atomics direclty through `operator T` conversion.
    assert(my_atomic_ulong == my_atomic_ulong.load());
    std::cout << "my_non_atomic_ulong " << my_non_atomic_ulong << std::endl;
#if defined(__x86_64__) || defined(__aarch64__)
    assert(my_Arch_atomic_ulong == nthreads * niters);
    std::cout << "my_Arch_non_atomic_ulong " << my_Arch_non_atomic_ulong << std::endl;
#endif
}

GitHubアップストリーム

可能な出力:

my_non_atomic_ulong 15264
my_Arch_non_atomic_ulong 15267

このことから、x86 LOCK prefix/aarch64 LDADD命令が加算をアトミックにしたことがわかります。これがないと、多くの加算で競合状態が発生し、最後の合計カウントが同期20000未満になります。

参照: x86アセンブリでの「ロック」命令の意味は何ですか?

Ubuntu 19.04 AMD64およびQEMU aarch64ユーザーモードでテスト済み。

私の理解では、各「コア」は完全なプロセッサであり、独自のレジスタセットがあります。基本的に、BIOSは1つのコアを実行して起動し、オペレーティングシステムは他のコアを初期化し、実行するコードを指すなどして他のコアを「起動」できます。

同期はOSによって行われます。通常、各プロセッサはOSに対して異なるプロセスを実行しているため、オペレーティングシステムのマルチスレッド機能は、どのプロセスがどのメモリにアクセスするか、およびメモリ衝突の場合に何をするかを決定します。

42
Nicholas Flynt

各コアは、異なるメモリ領域から実行されます。オペレーティングシステムがプログラムにコアを向け、コアがプログラムを実行します。プログラムは、複数のコアがあること、または実行中のコアがあることを認識しません。

また、オペレーティングシステムのみが利用できる追加の指示はありません。これらのコアは、シングルコアチップと同じです。各コアは、実行する次のメモリ領域を見つけるための情報交換に使用される共通メモリ領域への通信を処理するオペレーティングシステムの一部を実行します。

これは単純化ですが、それがどのように行われるかの基本的なアイデアを提供します。 マルチコアとマルチプロセッサの詳細 Embedded.comには、このトピックに関する多くの情報があります...このトピックは非常に迅速に複雑になります!

9
Gerhard

マルチコアCPU向けに最適化コンパイラ/バイトコードVMを記述している場合、すべてのコアで効率的に実行されるコードを生成するためにx86などについて具体的に何を知る必要がありますか?

最適化コンパイラ/バイトコードVMを書く人として、私はここであなたを助けることができるかもしれません。

すべてのコアで効率的に実行されるコードを生成するために、x86について特に何も知る必要はありません。

ただし、すべてのコアで正しくを実行するコードを作成するには、cmpxchgとその友達について知る必要がある場合があります。マルチコアプログラミングでは、実行スレッド間の同期と通信を使用する必要があります。

一般的にx86で効率的に実行されるコードを生成するには、x86についての知識が必要な場合があります。

あなたが学ぶのに役立つ他のことがあります:

OS(Linux、Windows、またはOSX)が提供する機能について学習して、複数のスレッドを実行できるようにする必要があります。 OpenMPやスレッディングビルディングブロックなどの並列化API、またはOSX 10.6「Snow Leopard」の今後の「Grand Central」について学ぶ必要があります。

コンパイラを自動並列化する必要があるかどうか、またはコンパイラによってコンパイルされたアプリケーションの作成者が複数のコアを利用するために特別な構文またはAPI呼び出しをプログラムに追加する必要があるかどうかを検討する必要があります。

9
Alex Brown

アセンブリコードは、1つのコアで実行されるマシンコードに変換されます。マルチスレッド化する場合は、オペレーティングシステムプリミティブを使用して、このコードを異なるプロセッサで数回、または異なるコアの異なるコードで起動する必要があります。各コアは個別のスレッドを実行します。各スレッドは、現在実行中の1つのコアのみを認識します。

5
sharptooth

機械命令ではまったく行われていません。コアは個別のCPUのふりをしており、互いに通信するための特別な機能はありません。コミュニケーションには2つの方法があります。

  • 物理アドレス空間を共有します。ハードウェアはキャッシュの一貫性を処理するため、1つのCPUが別のCPUが読み取るメモリアドレスに書き込みます。

  • aPIC(プログラム可能な割り込みコントローラー)を共有します。これは、物理アドレス空間にマッピングされたメモリであり、1つのプロセッサが他のプロセッサを使用して、他のプロセッサを制御したり、オンまたはオフにしたり、割り込みを送信したりできます。

http://www.cheesecake.org/sac/smp.html は、愚かなURLを使用した適切なリファレンスです。

3
pjc50

シングルスレッドアプリケーションとマルチスレッドアプリケーションの主な違いは、前者には1つのスタックがあり、後者には各スレッドに1つのスタックがあることです。コンパイラはデータとスタックセグメントレジスタ(dsとss)が等しくないと想定するため、コードの生成方法は多少異なります。これは、デフォルトでssレジスタに設定されているebpおよびespレジスタを介したインダイレクションも、dsにデフォルト設定されないことを意味します(ds!= ssのため)。逆に、デフォルトがdsである他のレジスタを介した間接指定は、デフォルトがssになりません。

スレッドは、データおよびコード領域を含む他のすべてを共有します。また、libルーチンも共有しているため、スレッドセーフであることを確認してください。 RAMで領域をソートする手順は、物事を高速化するためにマルチスレッド化できます。その後、スレッドは同じ物理メモリ領域のデータにアクセス、比較、順序付けし、同じコードを実行しますが、ソートのそれぞれの部分を制御するために異なるローカル変数を使用します。これはもちろん、スレッドがローカル変数が含まれるスタックが異なるためです。このタイプのプログラミングは、コア間データ衝突(キャッシュ内)そして、RAM)が削減され、その結果、コードが1つだけの場合よりも2つ以上のスレッドの方が高速になります。もちろん、未調整のコードは、2つ以上のプロセッサーよりも1つのプロセッサーの方が高速です。特定のスレッドではなくすべてのスレッドに割り込みたいため、標準の「int 3」ブレークポイントは適用できないため、より困難です。デバッグレジスタブレークポイントは、spを実行する特定のプロセッサに設定できない限り、この問題も解決しません。中断したいスレッド。

他のマルチスレッドコードには、プログラムのさまざまな部分で実行されるさまざまなスレッドが関係する場合があります。このタイプのプログラミングは、同じ種類のチューニングを必要としないため、学習がはるかに簡単です。

1
Olof Forshell

以前に登場したシングルプロセッサのバリアントと比較して、すべてのマルチプロセッシング対応アーキテクチャに追加されたのは、コア間で同期するための命令です。また、キャッシュの一貫性、フラッシュバッファ、およびOSが処理しなければならない同様の低レベル操作を処理するための指示があります。 IBM POWER6、IBM Cell、Sun Niagara、Intelの「ハイパースレッディング」などの同時マルチスレッドアーキテクチャの場合、スレッド間で優先順位を付けるための新しい命令が表示される傾向があります(優先順位を設定し、何もしないときに明示的にプロセッサを明け渡すなど) 。

しかし、基本的なシングルスレッドのセマンティクスは同じです。他のコアとの同期と通信を処理するための追加機能を追加するだけです。

0
jakobengblom2