[Writing Code] Dummy Task 만들기

리눅쏘·2024년 4월 28일
post-thumbnail

1. 목적

  • 임의의 실행시간을 갖는 task를 만들고자 한다.
  • 일반적으로 for 문을 사용하여 임의의 실행시간을 갖는 task를 만든다.
  • 그러나, for 문을 사용하게 되면 컴파일러가 최적화하여 개발자의 의도와는 다르게 동작할 수 있게 된다. 따라서 __asm__ volatile를 사용하여 컴파일러가 최적화하지 못하도록 한다.

2. 예시 코드

  • load cycle 만큼 시간을 지연시키는 task를 의미한다.
void dummy_task(long load) {
	long i;
	for (i = 0 ; i < load; i++) 
		__asm__ volatile ("nop");
}
  1. __asm__ volatile ("nop");
    • volatile은 메모리 직접 접근을 의미한다.
    • nop은 no operation으로 아무것도 하지 않는 것을 의미한다.
    • asm volatile ("nop") 는 1 cycle을 지연한다는 의미한다.
  2. CPU frequency
    • 1 cycle 지연시간을 알기 위해서는 현재 cpu의 frequency를 알아야 한다.
    • lscpu 명령어를 사용하여 현재 cpu의 frequcy를 확인한다.
    • 아래의 결과를 예로 들어서 설명한다. 현재 cpu가 max frequency (performance mode) 를 사용한다면, 1 cycle 당 약 0.7ns가 걸린다.

  1. Dummy task
    • 따라서 max frequency를 사용하고 있을 때, 0.3s를 실행하는 dummy task를 만들고 싶다면 load는 4*(10^8) (=0.3s/0.7ns)가 되어야 한다.
    • 이때, loop overhead가 존재하기 때문에 asm volatile ("nop"); 를 10번 연속해서 사용하고, load를 4*(10^7)로 사용하면 훨씬 더 정확한 결과가 나온다.

3. 활용방법

  1. 먼저 benchmark.cpp 파일을 만들고, 아래 코드를 붙여넣는다.
    • input으로 원하는 시간을 넣으면, output으로 load를 반환하는 코드이다.
    • 이 코드를 이용하면, 미리 프로파일링한 값으로 dummy task를 만들 수 있다.
#include <chrono>
#include <iostream>
#include <string>

void dummy_task(long load) {
    for (long i = 0; i < load; i++) {
        __asm__ volatile ("nop");
        __asm__ volatile ("nop");
        __asm__ volatile ("nop");
        __asm__ volatile ("nop");
        __asm__ volatile ("nop");
        __asm__ volatile ("nop");
        __asm__ volatile ("nop");
        __asm__ volatile ("nop");
        __asm__ volatile ("nop");
        __asm__ volatile ("nop");
    }
}

double measure_execution_time(long load) {
    auto start = std::chrono::high_resolution_clock::now();
    dummy_task(load);
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double, std::milli> execution_time = end - start;
    return execution_time.count();
}

double measure_average_execution_time(long load, int num_measurements = 10) {
    double total_time = 0.0;
    for (int i = 0; i < num_measurements; i++) {
        total_time += measure_execution_time(load);
    }
    return total_time / num_measurements;
}

long calculate_load_for_target_time(double target_time, double initial_time, long initial_load) {
    double ratio = target_time / initial_time;
    return static_cast<long>(initial_load * ratio);
}

int main(int argc, char* argv[]) {
    if (argc < 2) {
        std::cerr << "Usage: " << argv[0] << " <target_time_ms>" << std::endl;
        return 1;
    }

    double target_time = std::stod(argv[1]);

    long initial_load = 100;

    double average_initial_time = measure_average_execution_time(initial_load);

    long estimated_load = calculate_load_for_target_time(target_time, average_initial_time, initial_load);

    std::cout << "Measured execution time for initial load " << initial_load << ": " << average_initial_time << " ms" << std::endl;
    std::cout << "For a target time of " << target_time << " ms, the estimated load is: " << estimated_load << std::endl;

    return 0;
}
  1. 아래 command를 이용하여 컴파일한다.
g++ -o benchmark_test benchmark_test.cpp
  1. 아래 command를 이용하면, 원하는 시간에 따른 load가 반환된다.
./benchmark_test <원하는 시간>

profile
리눅스 마스터 쏘

0개의 댓글