Эффективно перенести процесс Linux с C - PullRequest
0 голосов
/ 29 октября 2019

Мне нужно оценить, сколько стоит перенести процесс linux на другое ядро ​​того же компьютера. Для миграции процесса я использую системный вызов sched_setaffinity, но я заметил, что миграция не всегда происходит мгновенно, что является моим требованием.

Более подробно, я создаю программу на C, которая выполняет множество простых вычислений по два раза каждый, первый без миграции, а второй с миграцией. Вычисление разницы между двумя временными метками должно дать мне приблизительную оценку затрат на миграцию. Однако мне нужно выяснить, как я могу перенести текущий процесс и подождать, пока произойдет миграция

#define _GNU_SOURCE
#define _POSIX_C_SOURCE 199309L

#include <assert.h>
#include <sched.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <stdint.h>

//Migrates the process
int migrate(pid_t pid) {
    const int totCPU = 8;
    const int nextCPU = (sched_getcpu() +1) % totCPU;

    cpu_set_t target;
    CPU_SET(nextCPU, &target);
    if(sched_setaffinity(pid, sizeof(target), &target) < 0)
        perror("Setaffinity");

    return nextCPU;
}

int main(void) {
    long i =0;
    const long iterations = 4;
    uint64_t total_sequential_delays = 0;
    uint64_t total_migration_delays = 0;
    uint64_t delta_us;

    for(int i=0; i < iterations; i++) {
        struct timespec start, end;

        //Migration benchmark only happens in odd iterations
        bool do_migration = i % 2 == 1;
        //Start timestamp
        clock_gettime(CLOCK_MONOTONIC_RAW, &start);
        //Target CPU to migrate
        int target;
        if(do_migration) {
            target = migrate(0);
            //if current CPU is not the target CPU
            if(target != sched_getcpu()) {
                do {
                    clock_gettime(CLOCK_MONOTONIC_RAW, &end);
                }
                while(target != sched_getcpu());
            }
        }

        //Simple computation 
        double k = 5;
        for(int j = 1; j <= 9999; j++) {
            k *= j / (k-3);
        }

        //End timestamp
        clock_gettime(CLOCK_MONOTONIC_RAW, &end);

        //Elapsed time
        delta_us = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000;
        if(do_migration) total_migration_delays += delta_us;
        else total_sequential_delays += delta_us;
    }

    //Compute the averages
    double avg_migration = total_migration_delays / iterations;
    double avg_sequential = total_sequential_delays / iterations;

    //Print them
    printf("\navg_migration=%f, avg_sequential=%f",avg_migration,avg_sequential);

    return EXIT_SUCCESS;
}

Проблема здесь в том, что цикл do-while (строки 46-49) иногда выполняется вечно.

Ответы [ 2 ]

0 голосов
/ 29 октября 2019

Мне нужно оценить, сколько стоит перенести процесс linux на другое ядро ​​того же компьютера.

ОК, стоимость можно оценить как:

  • время, необходимое для установки привязки нового ЦП и выполнения «yield» или «sleep(0)» для принудительного переключения / перепланирования задачи (включая накладные расходы на переключение задач и т. Д.).

  • стоимость пропуска кэша для каждого будущего »была кэширована на старом ЦП, но не кэширована на новом ЦП, но все же« доступ к памяти

  • стоимость TLBпропустить каждое будущее "виртуальный к физическому переводу был кеширован на старом ЦП, но еще не кеширован на новом ЦП" доступ к памяти

  • штрафы NUMA

  • проблемы с балансировкой нагрузки (например, миграция с «слегка загруженного» ЦП или ядра на «сильно загруженный другими процессами» ЦП или ядро ​​может привести к серьезным проблемам с производительностью, включая стоимость решения ядра перенести другие процессы на другие ЦП для исправлениябалансировка нагрузки, гдезатраты / накладные расходы, оплачиваемые другими процессами, вероятно, должны быть включены в общую стоимость, вызванную переносом вашего процесса.)

Обратите внимание, что:

a) существует несколько уровнейкеши (кэш трассы, кеш инструкций, кеш данных L1, кеш данных L2 и т. д.) и некоторые кеши совместно используются несколькими ЦП (например, L1 может быть разделен между логическими ЦП в одном и том же ядре, L2 может использоваться совместно двумя ядрами, L3может совместно использоваться 8 ядрами).

b) Затраты на пропуск TLB зависят от многих факторов (например, если ядро ​​использует средства защиты от Meltdown без функции PCID и в любом случае удаляет информацию TLB при каждом системном вызове).

c) Штрафы NUMA - это затраты на задержку - каждый доступ к ОЗУ (например, потеря кеша), который был выделен на предыдущем ЦП (для предыдущего узла NUMA), будет иметь большую задержку, чем доступ к ОЗУ, выделенной на новом/ текущий процессор (правильный узел NUMA).

d) Все затраты на кэш, расходы на TLB и штрафы NUMA зависят от доступа к памятисс узоры. Тест, который не имеет доступа к памяти, будет вводить в заблуждение.

e) Затраты на кэш, затраты на TLB и штрафы NUMA сильно зависят от используемого оборудования - например, тест на один "медленный процессор с быстрой оперативной памятью икомпьютер без NUMA не будет иметь никакого отношения к другому компьютеру с «быстрыми процессорами с медленной оперативной памятью и многими доменами NUMA». Точно так же это сильно зависит от того, какие процессоры (например, миграция с CPU № 0 на CPU № 1 может стоить очень мало, а миграция с CPU № 0 на CPU № 15 может быть очень дорогой).

Для миграции процесса я использую системный вызов sched_setaffinity, но я заметил, что миграция не всегда происходит мгновенно, что является моим требованием.

Поставьте "sleep(0);" после"sched_setaffinity();".

0 голосов
/ 29 октября 2019

Вы не очищаете целевой набор:

cpu_set_t target;
CPU_ZERO(&target);  /* you need to add this line */
CPU_SET(nextCPU, &target);

, поэтому вы не знаете, для чего вы устанавливаете свою близость. Другая проблема заключается в том, что стоимость миграции не оплачивается заранее, поэтому простое измерение времени от set_affinity () до тех пор, пока вы не определитесь, на самом деле не является правильным измерением. Возможно, вам лучше запустить рабочую нагрузку, содержащуюся в одном процессоре, а затем в двух, трех. Кроме того, подумайте, должна ли ваша рабочая нагрузка включать большие структуры данных, возможно, с обновлениями, поскольку потеря ваших вложений в кэш также является затратой на миграцию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...