Подтвердить что ты не робот

Есть ли инструкция `x86`, чтобы указать, в каком ядре выполняется инструкция?

Когда я cat /proc/cpuinfo, я вижу 8 ядер, с идентификатором от 0 до 7.

Есть ли инструкция x86, которая сообщит о ядре ядра ядра, в котором работает сама инструкция?

Я посмотрел на cpuid, но, похоже, не возвращает coreid при любых параметрах.

4b9b3361

Ответ 1

Руководство разработчика программного обеспечения для систем Intel® 64 и IA-32 Volume 3A: Руководство по системному программированию, часть 1, раздел 8.4.5. Идентификация логических процессоров в системе MP перечисляет, среди прочего:

Этот идентификатор APIC указан CPUID.0BH: EDX [31: 0]

Обратите внимание, что это напрямую не соответствует нумерации ядра Linux. В ядре есть таблица x86_cpu_to_apicid, которую вы можете прочитать. Конечно, ядро ​​также знает, на каком процессоре работает код, без необходимости обращаться к APIC:

 * smp_processor_id(): get the current CPU ID.
 *
 * if DEBUG_PREEMPT is enabled then we check whether it is
 * used in a preemption-safe way. (smp_processor_id() is safe
 * if it used in a preemption-off critical section, or in
 * a thread that is bound to the current CPU.)

Ответ 2

Некоторые новые процессоры x86/x86_64 имеют вариант "RDTSCP" команды RDTSC:

http://ref.x86asm.net/coder32-abc.html#R

RDTSC   EAX EDX IA32_TIM…               0F  31
        P1+         f2              Read Time-Stamp Counter
RDTSCP  EAX EDX ECX ...         0F  01  F9  7   
        C7+         f2              Read Time-Stamp Counter and Processor ID

C7 + означает, что инструкция "0x0F01F9" была введена в некоторых "Core i7"...

Опкоды

Шестнадцатеричное кодирование с длинным режимом Режим устаревшего режима

0F 01 F9 RDTSCP A Действительный Действительный

Прочитайте 64-битный счетчик времени и 32-разрядное значение IA32_TSC_AUX в EDX: EAX и ECX.

ОС должна написать идентификатор ядра в IA32_TSC_AUX (Linux делает), и это значение доступно с помощью RDTSCP.

Linux кодирует numa id (< 12) и идентификатор ядра (8 бит) в TSC_AUX:

341         if (cpu_has(&cpu_data(cpu), X86_FEATURE_RDTSCP))
342                 write_rdtscp_aux((node << 12) | cpu);
343 
344         /*
345          * Store cpu number in limit so that it can be loaded quickly
346          * in user space in vgetcpu. (12 bits for the CPU and 8 bits for the node)
347          */

В Linux существует также vsyscall getcpu ( "__vdso_getcpu" ) для доступа к идентификатору процессора через RDTSCP (если cpu имеет инструкция) или через GDT - GDT_ENTRY_PER_CPU: __ getcpu в include/asm/vsyscall.h из 3.13. На странице man:

getcpu() добавлен в ядро ​​2.6.19 для x86_64 и i386.

Linux прилагает все усилия, чтобы сделать этот вызов максимально быстрым.        Цель getcpu() - позволить программам делать оптимизации с помощью        для каждого процессора или для оптимизации NUMA.

Из некоторых руководств по интеллекту: http://www.intel.com/content/dam/www/public/us/en/documents/white-papers/ia-32-ia-64-benchmark-code-execution-paper.pdf#page=15

3.2 Усовершенствования с использованием инструкции RDTSCP

Инструкция RDTSCP описана в архитектурах Intel® 64 и IA-32 Руководство по разработке программного обеспечения Volume 2B ([3]) в качестве инструкции по сборке, которая при в то же время считывает регистр временной метки и идентификатор ЦП. Значение регистр временной метки сохраняется в регистре EDX и EAX; значение Идентификатор CPU хранится в регистре ECX ( "На процессорах, поддерживающих Intel 64 архитектура, 32 бита высокого порядка каждого из RAX, RDX и RCX очищаются" ). Интересным в этом случае является "псевдо" сериализационное свойство RDTSCP. ручные состояния:

"Команда RDTSCP ожидает выполнения всех предыдущих инструкций перед чтением счетчика. Однако последующие инструкции могут начать выполнение перед выполнением операции чтения.

Это означает, что эта инструкция гарантирует, что все, что выше своего вызова в исходный код выполняется до вызова самой инструкции. Это не может, однако, гарантируйте, что - для целей оптимизации - CPU не будет выполняться, перед вызовом RDTSCP, инструкции, которые в исходном коде помещаются после Функция вызова RDTSCP. Если это произойдет, загрязнение, вызванное инструкциями в исходном коде, который появляется после того, как RDTSCP появится в коде под измерение.,

Кроме того, описание доступно здесь http://www.felixcloutier.com/x86/RDTSCP.html, который является клоном https://github.com/zneak/x86doc

UPDATE: отдельная инструкция RDPID просто для чтения регистра IA32_TSC_AUX без счетчика времени (как RDTSCP делает

https://hjlebbink.github.io/x86doc/html/RDPID.html

Считывает значение IA32_TSC_AUX MSR (адрес C0000103H) в регистр назначения. Значение CS.D и префиксов размера операнда (66H и REX.W) не влияет на поведение команды RDPID.

F3 0F C7 /7 RDPID r32 M   N.E./V  RDPID   Read IA32_TSC_AUX into r32.
F3 0F C7 /7 RDPID r64 M   V/N.E.  RDPID   Read IA32_TSC_AUX into r64.

Он будет включен, так как "Микроархитектура Ice Lake" (2018), как заявлено в https://software.intel.com/sites/default/files/managed/c5/15/architecture-instruction-set-extensions-programming-reference.pdf 319433-030 ОКТЯБРЬ 2017

Ответ 3

За исключением уже описанных инструкций CPUID и RDTSCP, также есть новая команда RDPID (страница загрузки Intel SDM) именно для этой цели.

Описание

Считывает значение IA32_TSC_AUX MSR (адрес C0000103H) в регистр назначения. Значение CS.D и префиксы размера операнда (66H и REX.W) не влияют на поведение команды RDPID.

Примечания:

RDPID считывает идентификатор ядра процессора как uint32_r или uint64_r, поэтому значение чтения не будет в последовательном диапазоне [0, MAX_CPU_COUNT]

RDPID - это новая инструкция, поэтому она не поддерживается аппаратно

Ответ 4

Вы видите 8 "виртуальных процессоров", а не ядра, поэтому, если у вас есть, скажем, 4-ядерный процессор Ivy Bridge с 2 аппаратными потоками на ядро, вы можете увидеть, какие пары vCPU разделяют ядро ​​через записи в /sys/devices/system/cpu/cpu[0-7]/topology/thread_siblings_list.

Еще один ответ дает прекрасное предложение использовать cpuid, но я не думаю, что вы можете быть уверены, что команда cpuid выполняется на том же самом vCPU, что и на интересующую инструкцию, если вы не привяжете поток к vCPU (в этом случае это довольно избыточно), так как вы не можете знать, что ядро ​​не перенесло ваш поток из одного vCPU в другой между тем временем, когда была выполнена инструкция-интерес, и время, когда команда cpuid была казнены.

Короче говоря, подавляющее большинство времени, две "близкие" инструкции будут выполняться на одном и том же VCPU, но он не гарантируется без привязки, и если вы прикрепили поток, вы уже знаете, что такое vCPU, на котором он работает, поэтому это несколько бессмысленно.

Ответ 5

taskset + __rdtscp работающий пример

И, наконец, для тех, кто хочет повеселиться с x86 intrinsics + taskset:

rdtscp.c

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include <x86intrin.h>

int main(void) {
    uint32_t pid;
    printf("0x%016" PRIX64 "\n", (uint64_t)__rdtscp(&pid));
    printf("0x%08" PRIX32 "\n", pid);
    return EXIT_SUCCESS;
}

GitHub вверх по течению.

затем скомпилируйте и запустите, контролируя, на каком ядре оно работает, с помощью taskset:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o rdtscp.out rdtscp.c
./taskset -c 0 ./rdtscp.out
./taskset -c 1 ./rdtscp.out

затем для каждого запуска вторая строка, которая показывает идентификатор процессора, соответствует значению, установленному набором задач.

Протестировано в Ubuntu 19.04 amd64 с процессором Intel Core i7-7820HQ.