C++23에서는 다차원 배열을 효율적이고 유연하게 다룰 수 있는 새로운 도구인 std::mdspan이 도입되었습니다. 이번 글에서는 std::mdspan의 개념과 사용법, 그리고 이전 버전과 비교하여 어떻게 개선되었는지 알아보겠습니다.
std::mdspan이란 무엇인가요?
std::mdspan은 멀티디멘션 배열 스팬(Multidimensional Array Span)으로, 다차원 배열의 데이터를 복사 없이 효율적으로 접근할 수 있는 뷰(view)를 제공합니다. 이는 C++20에서 도입된 std::span을 다차원으로 확장한 개념으로, 고성능 계산과 과학 컴퓨팅에서 중요한 역할을 합니다.
이전 버전에서는 어떻게 했나요?
C++23 이전에는 다차원 배열을 다루기 위해 다음과 같은 방법을 사용했습니다.
1. 중첩 배열(Nested Arrays)
int arr[3][4]; // 2차원 배열
arr[1][2] = 5; // 요소 접근
- 문제점:
- 배열의 크기가 컴파일 타임에 고정되어야 합니다.
- 동적 할당이 필요한 경우 포인터의 포인터를 사용해야 하며, 이는 복잡성과 오버헤드를 증가시킵니다.
- 다차원 배열의 메모리 레이아웃을 직접 관리하기 어렵습니다.
2. 일차원 배열로 다차원 배열 시뮬레이션
int* arr = new int[rows * cols];
int get(int i, int j) {
return arr[i * cols + j];
}
void set(int i, int j, int value) {
arr[i * cols + j] = value;
}
- 문제점:
- 인덱싱을 수동으로 계산해야 하므로 오류 가능성이 높습니다.
- 코드의 가독성이 떨어집니다.
- 메모리 레이아웃을 변경하거나 다른 순서를 사용하려면 큰 수정이 필요합니다.
3. 벡터를 사용한 다차원 배열
#include <vector>
std::vector<std::vector<int>> arr(rows, std::vector<int>(cols));
arr[1][2] = 5;
- 문제점:
- 벡터의 벡터를 사용하면 연속적인 메모리 레이아웃이 보장되지 않습니다.
- 메모리 할당이 중첩되어 오버헤드가 증가합니다.
- 캐시 효율성이 떨어져 성능 저하가 발생할 수 있습니다.
C++23의 std::mdspan을 사용한 개선
std::mdspan을 사용하면 다차원 배열의 데이터를 효율적으로 참조하고, 메모리 레이아웃을 유연하게 관리할 수 있습니다.
예제: std::mdspan 사용
#include <mdspan>
#include <iostream>
int main() {
constexpr int rows = 3;
constexpr int cols = 4;
int data[rows * cols] = {0}; // 일차원 배열로 메모리 할당
std::mdspan<int, std::extents<rows, cols>> matrix(data);
matrix(1, 2) = 5;
std::cout << "matrix(1, 2) = " << matrix(1, 2) << '\n';
return 0;
}
- std::mdspan은 데이터를 소유하지 않고, 기존의 메모리를 참조합니다.
- matrix(1, 2)와 같이 직관적인 인덱싱을 제공합니다.
- 메모리 레이아웃과 접근 방식을 커스터마이즈할 수 있습니다.
어떻게 좋아졌나요?
- 성능 향상: 연속적인 메모리 레이아웃을 유지하면서도 캐시 효율성을 극대화할 수 있습니다.
- 유연성 증가: 메모리 레이아웃(행 우선, 열 우선 등)을 자유롭게 정의할 수 있습니다.
- 가독성 개선: 다차원 인덱싱을 지원하여 코드가 직관적이고 이해하기 쉬워집니다.
- 메모리 오버헤드 감소: 추가적인 메모리 할당 없이 데이터를 직접 참조하므로 오버헤드가 줄어듭니다.
상세한 예제와 비교
1. 메모리 레이아웃 변경
기존 방식
- 메모리 레이아웃을 변경하려면 코드를 크게 수정해야 합니다.
C++23 방식
#include <mdspan>
#include <iostream>
int main() {
constexpr int rows = 3;
constexpr int cols = 4;
int data[rows * cols] = {0};
// 행 우선(Row-major) 메모리 레이아웃
std::mdspan<int, std::extents<rows, cols>, std::layout_right> matrix_row(data);
// 열 우선(Column-major) 메모리 레이아웃
std::mdspan<int, std::extents<rows, cols>, std::layout_left> matrix_col(data);
matrix_row(1, 2) = 5;
std::cout << "matrix_row(1, 2) = " << matrix_row(1, 2) << '\n';
std::cout << "matrix_col(1, 2) = " << matrix_col(1, 2) << '\n';
return 0;
}
- std::layout_right와 std::layout_left를 사용하여 메모리 레이아웃을 선택할 수 있습니다.
- 동일한 데이터에 대해 다른 레이아웃의 뷰를 생성할 수 있습니다.
2. 동적 크기의 다차원 배열
#include <mdspan>
#include <vector>
int main() {
int rows = 3;
int cols = 4;
std::vector<int> data(rows * cols);
std::mdspan<int, std::dextents<size_t, 2>> matrix(data.data(), rows, cols);
matrix(1, 2) = 5;
std::cout << "matrix(1, 2) = " << matrix(1, 2) << '\n';
return 0;
}
- std::dextents를 사용하여 동적 크기의 다차원 배열을 지원합니다.
- 런타임에 크기를 결정할 수 있습니다.
3. 사용자 정의 매핑 정책
#include <mdspan>
#include <iostream>
struct custom_mapping {
size_t operator()(size_t i, size_t j) const {
// 사용자 정의 인덱스 매핑
return i * cols + j;
}
size_t rows;
size_t cols;
};
int main() {
size_t rows = 3;
size_t cols = 4;
int data[rows * cols] = {0};
std::mdspan<int, std::dextents<size_t, 2>, custom_mapping> matrix(data, custom_mapping{rows, cols});
matrix(1, 2) = 5;
std::cout << "matrix(1, 2) = " << matrix(1, 2) << '\n';
return 0;
}
- 사용자 정의 매핑 정책을 통해 복잡한 인덱스 매핑을 구현할 수 있습니다.
- 메모리 레이아웃을 완전히 커스터마이즈할 수 있습니다.
주의 사항
- 데이터 소유권: std::mdspan은 데이터를 소유하지 않으므로, 데이터의 수명이 mdspan보다 길어야 합니다.
- 헤더 파일 포함: std::mdspan을 사용하려면 <mdspan> 헤더를 포함해야 합니다.
- 컴파일러 지원: C++23 기능이므로, 이를 지원하는 컴파일러와 표준 라이브러리가 필요합니다.
std::mdspan과 std::span의 차이점
- std::span은 일차원 배열을 다루는 뷰이며, std::mdspan은 다차원 배열을 다룹니다.
- std::mdspan은 메모리 레이아웃과 매핑 정책을 커스터마이즈할 수 있습니다.
- 둘 다 데이터를 복사하지 않고 참조하므로 메모리 오버헤드가 없습니다.
요약
C++23의 std::mdspan은 다차원 배열을 효율적으로 다룰 수 있는 강력한 도구입니다. 이전에는 다차원 배열을 다루기 위해 복잡한 포인터 연산이나 비효율적인 자료 구조를 사용해야 했지만, 이제는 std::mdspan을 통해 직관적이고 유연한 인터페이스로 다차원 데이터를 처리할 수 있습니다. 이를 통해 코드의 가독성과 성능이 향상되며, 과학 계산, 머신 러닝, 그래픽스 등 고성능 컴퓨팅 분야에서 큰 이점을 제공합니다.
참고 자료:
반응형
'개발 이야기 > C++' 카테고리의 다른 글
[C++23 새기능 소개] std::move_only_function (0) | 2024.12.08 |
---|---|
[C++23 새기능 소개] deducing this (0) | 2024.12.08 |
[C++23 새기능 소개] std::expected (0) | 2024.12.08 |
[C++23 새기능 소개] std::stacktrace 라이브러리 (0) | 2024.12.08 |
[C++23 새기능 소개] std::print와 std::println 함수 (0) | 2024.12.08 |