인프런 커뮤니티 질문&답변

김인형님의 프로필 이미지
김인형

작성한 질문수

CS 지식의 정석 | 디자인패턴 네트워크 운영체제 데이터베이스 자료구조

페이지히트와 페이지미스 ★★★

[페이지미스 부분] 프리페칭 관련 질문

작성

·

127

1

  1. 미리 많이 사용될 것 같은 데이터를 미리 깔아 놓는 것을 프리페칭이라고 하셨는데, 가상이 아닌 '실제' 메모리에 깔아두는 건가요?

  2. 내 요청이 프리페칭에 없는 경우 페이지미스가 뜬다고 했는데, 그럼 오류를 주고 여기서 끝인가요? 아니면 해당 요청에 맞게 메모리로 불러와서 처리를 하나요? 페이지미스 이후 무슨 일이 일어나는지 궁금합니다.

  3. 프리페칭에서 페이지미스가 일어나면 데이터를 덮어쓴다고 되어 있는데, 결국 데이터를 불러들이는 과정은 있는건가요? 그런데 이거를 페이지폴트라고 하지는 않는 건가요?

  4. 작업세트와 프리페칭 어떤 차이가 있는지 궁금합니다.

감사합니다!

답변 1

2

큰돌님의 프로필 이미지
큰돌
지식공유자

안녕하세요 인형님 ㅎㅎ

 

미리 많이 사용될 것 같은 데이터를 미리 깔아 놓는 것을 프리페칭이라고 하셨는데, 가상이 아닌 '실제' 메모리에 깔아두는 건가요?

>> 네 맞습니다.

프리페칭은 앞으로 사용할 가능성이 높은 데이터를 미리 메모리에 로드해두는 기술입니다. 프리페칭된 데이터는 실제 메모리(RAM)에 로드됩니다. 이를 통해 페이지미스(page miss) 발생 가능성을 줄이고 성능을 향상시킬 수 있습니다.

 

내 요청이 프리페칭에 없는 경우 페이지미스가 뜬다고 했는데, 그럼 오류를 주고 여기서 끝인가요? 아니면 해당 요청에 맞게 메모리로 불러와서 처리를 하나요? 페이지미스 이후 무슨 일이 일어나는지 궁금합니다.

프리페칭에서 페이지미스가 일어나면 데이터를 덮어쓴다고 되어 있는데, 결국 데이터를 불러들이는 과정은 있는건가요? 그런데 이거를 페이지폴트라고 하지는 않는 건가요?

>>

프리페치 -> 페이지미스 이후의 과정은 다음과 같습니다.

  • 프리페칭 시도: 시스템이 앞으로 필요할 가능성이 높은 페이지를 미리 로드하려고 시도합니다.

  • 페이지미스 발생: 예상한 페이지가 현재 메모리에 없는 경우 페이지미스가 발생합니다.

  • 필요성 검토: 운영체제가 해당 페이지를 실제로 로드할 필요가 있는지 검토합니다.

    • 필요 없음: 만약 해당 페이지가 실제로 필요하지 않게 되거나 다른 작업으로 인해 덮어써질 경우, 해당 페이지를 로드하지 않고 종료합니다.

    • 실제 필요: 만약 해당 페이지가 실제로 필요하게 되면 그때 페이지폴트가 발생하여 디스크에서 메모리로 페이지를 로드합니다.

작업세트와 프리페칭 어떤 차이가 있는지 궁금합니다.

>>

작업세트(working set)는 프로세스의 과거 사용이력을 기반으로 많이 사용하는 페이지집합을 만들어 한꺼번에 미리 메모리에 로드하는 것을 말합니다. 

프리페칭은 컴파일러, 운영체제 또는 하드웨어가 다양한 예측 알고리즘을 사용하여 미리 로드할 페이지를 결정해서 앞으로 필요할 데이터를 미리 메모리에 로드하는 것입니다. 이는 과거의 이력을 기반으로 한게 아닙니다.

 

예를 들어 루프 예측(Loop Prediction)이라는 알고리즘이 있습니다. 이는 컴파일러는 루프 내에서 연속적인 메모리 접근 패턴을 감지합니다.

배열을 순회하는 루프에서 다음 루프 반복(iteration)에서 사용할 데이터를 미리 로드하도록 프리페칭 지시어를 추가해서 구현할 수 있습니다.

#include <iostream>
#include <vector>
#include <chrono>

// 함수: 프리페칭을 통해 배열 요소 접근 속도 향상
void processWithPrefetch(const std::vector<int>& data, size_t prefetchDistance) {
    size_t dataSize = data.size();
    int sum = 0;

    for (size_t i = 0; i < dataSize; ++i) {
        if (i + prefetchDistance < dataSize) {
            __builtin_prefetch(&data[i + prefetchDistance], 0, 1); // 다음에 사용할 데이터를 미리 로드
        }
        sum += data[i]; // 실제 데이터 처리
    }

    std::cout << "Sum with prefetching: " << sum << std::endl;
}

// 함수: 프리페칭 없이 배열 요소 접근
void processWithoutPrefetch(const std::vector<int>& data) {
    int sum = 0;

    for (int val : data) {
        sum += val;
    }

    std::cout << "Sum without prefetching: " << sum << std::endl;
}

int main() {
    const size_t dataSize = 10000000; // 예제 데이터 크기
    const size_t prefetchDistance = 64; // 프리페칭 거리 (캐시 라인 크기에 따라 조정 가능)
    std::vector<int> data(dataSize, 1); // 예제 데이터 초기화

    auto start = std::chrono::high_resolution_clock::now();
    processWithoutPrefetch(data);
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> duration = end - start;
    std::cout << "Time without prefetching: " << duration.count() << " seconds" << std::endl;

    start = std::chrono::high_resolution_clock::now();
    processWithPrefetch(data, prefetchDistance);
    end = std::chrono::high_resolution_clock::now();
    duration = end - start;
    std::cout << "Time with prefetching: " << duration.count() << " seconds" << std::endl;

    return 0;
}
 

이 코드를.

https://www.programiz.com/cpp-programming/online-compiler/

다음의 사이트에서 실행을 시키면

Sum without prefetching: 10000000
Time without prefetching: 0.0719415 seconds
Sum with prefetching: 10000000
Time with prefetching: 0.0439269 seconds

이렇게 뜨는 것을 볼 수 있습니다.

 

핵심코드는 바로 이부분인데요.

for (int i = 0; i < n; i++) {
    __builtin_prefetch(&array[i + 1], 0, 1); // 다음 반복에서 사용할 데이터 프리페칭
    sum += array[i];
}

다음 순회에서 사용할 데이터를 -> 미리 프리페칭한다고 보시면 됩니다.

 

프리페칭은 다양한 레벨에서 구현될 수 있지만, 소프트웨어 레벨에서 구현할 수 있는 간단한 프리페칭 예제입니다.



또 질문 있으시면 언제든지 질문 부탁드립니다.

좋은 수강평과 별점 5점은 제게 큰 힘이 됩니다. :)

감사합니다.

강사 큰돌 올림.

 

김인형님의 프로필 이미지
김인형
질문자

상세하게 답변해주셔서 정말 감사합니다. 더 잘 이해할 수 있게 되었습니다. 감사합니다!

김인형님의 프로필 이미지
김인형

작성한 질문수

질문하기