반응형
프로젝트 목표
이 단계에서는 매트릭스 라이브러리의 성능을 더욱 향상시키기 위해 최적화 기법을 적용하고, 추가적인 기능을 구현합니다. 특히, 다음과 같은 부분을 다룹니다:
- 루프 언롤링 및 벡터화: 성능을 향상시키기 위한 루프 최적화.
- 캐시 친화적 접근: 메모리 접근 패턴을 최적화하여 캐시 효율성을 높입니다.
- 추가 기능 구현: 행렬의 역행렬 및 행렬식 계산 기능 추가.
Step 1: 루프 언롤링 및 벡터화
행렬 곱셈에서 루프 언롤링을 적용하여 성능을 향상시킵니다. 루프 언롤링은 반복문 내의 작업을 반복적으로 실행하지 않고, 여러 번의 작업을 한 번에 수행하도록 변경하는 기법입니다.
Matrix.cpp: 루프 언롤링 적용
#include "Matrix.h"
Matrix operator*(const Matrix& lhs, const Matrix& rhs) {
if (lhs.getCols() != rhs.getRows()) {
throw std::invalid_argument("Number of columns of the first matrix must be equal to the number of rows of the second matrix");
}
Matrix result(lhs.getRows(), rhs.getCols());
size_t lhsCols = lhs.getCols();
size_t rhsCols = rhs.getCols();
size_t rows = lhs.getRows();
for (size_t i = 0; i < rows; ++i) {
for (size_t j = 0; j < rhsCols; ++j) {
double sum = 0.0;
for (size_t k = 0; k < lhsCols; k += 4) {
sum += lhs(i, k) * rhs(k, j)
+ lhs(i, k + 1) * rhs(k + 1, j)
+ lhs(i, k + 2) * rhs(k + 2, j)
+ lhs(i, k + 3) * rhs(k + 3, j);
}
result(i, j) = sum;
}
}
return result;
}
Step 2: 캐시 친화적 접근
행렬의 전치 연산에서 캐시 친화적인 접근 방식을 적용하여 성능을 향상시킵니다. 데이터가 캐시 라인에 잘 맞도록 배치하는 것이 중요합니다.
Matrix.cpp: 캐시 친화적 전치 연산
#include "Matrix.h"
Matrix transpose(const Matrix& matrix) {
Matrix result(matrix.getCols(), matrix.getRows());
size_t blockSize = 16; // 캐시 친화적 접근을 위한 블록 크기 설정
for (size_t i = 0; i < matrix.getRows(); i += blockSize) {
for (size_t j = 0; j < matrix.getCols(); j += blockSize) {
for (size_t k = i; k < i + blockSize && k < matrix.getRows(); ++k) {
for (size_t l = j; l < j + blockSize && l < matrix.getCols(); ++l) {
result(l, k) = matrix(k, l);
}
}
}
}
return result;
}
Step 3: 행렬의 역행렬 및 행렬식 계산
행렬의 역행렬과 행렬식을 계산하는 기능을 추가합니다. 역행렬을 계산하기 위해 가우스-조던 소거법을 사용하고, 행렬식은 재귀적으로 계산합니다.
Matrix.h: 역행렬 및 행렬식 함수 선언
Matrix inverse(const Matrix& matrix);
double determinant(const Matrix& matrix);
Matrix.cpp: 역행렬 및 행렬식 함수 구현
#include "Matrix.h"
Matrix inverse(const Matrix& matrix) {
if (matrix.getRows() != matrix.getCols()) {
throw std::invalid_argument("Matrix must be square to find its inverse");
}
size_t n = matrix.getRows();
Matrix augmented(matrix.getRows(), matrix.getCols() * 2);
// Augment the matrix with the identity matrix
for (size_t i = 0; i < n; ++i) {
for (size_t j = 0; j < n; ++j) {
augmented(i, j) = matrix(i, j);
augmented(i, j + n) = (i == j) ? 1.0 : 0.0;
}
}
// Perform Gaussian elimination
for (size_t i = 0; i < n; ++i) {
if (augmented(i, i) == 0) {
throw std::runtime_error("Matrix is singular and cannot be inverted");
}
for (size_t j = 0; j < 2 * n; ++j) {
augmented(i, j) /= augmented(i, i);
}
for (size_t k = 0; k < n; ++k) {
if (k != i) {
double factor = augmented(k, i);
for (size_t j = 0; j < 2 * n; ++j) {
augmented(k, j) -= factor * augmented(i, j);
}
}
}
}
// Extract the inverse matrix
Matrix result(n, n);
for (size_t i = 0; i < n; ++i) {
for (size_t j = 0; j < n; ++j) {
result(i, j) = augmented(i, j + n);
}
}
return result;
}
double determinant(const Matrix& matrix) {
if (matrix.getRows() != matrix.getCols()) {
throw std::invalid_argument("Matrix must be square to calculate its determinant");
}
size_t n = matrix.getRows();
if (n == 1) {
return matrix(0, 0);
}
if (n == 2) {
return matrix(0, 0) * matrix(1, 1) - matrix(0, 1) * matrix(1, 0);
}
double det = 0.0;
for (size_t i = 0; i < n; ++i) {
Matrix subMatrix(n - 1, n - 1);
for (size_t j = 1; j < n; ++j) {
size_t colIndex = 0;
for (size_t k = 0; k < n; ++k) {
if (k == i) continue;
subMatrix(j - 1, colIndex++) = matrix(j, k);
}
}
det += ((i % 2 == 0) ? 1 : -1) * matrix(0, i) * determinant(subMatrix);
}
return det;
}
테스트 코드
main.cpp
#include <iostream>
#include "Matrix.h"
int main() {
Matrix mat1(3, 3);
mat1(0, 0) = 1; mat1(0, 1) = 2; mat1(0, 2) = 3;
mat1(1, 0) = 0; mat1(1, 1) = 1; mat1(1, 2) = 4;
mat1(2, 0) = 5; mat1(2, 1) = 6; mat1(2, 2) = 0;
std::cout << "Matrix 1:" << std::endl;
mat1.print();
Matrix inv = inverse(mat1);
std::cout << "Inverse of Matrix 1:" << std::endl;
inv.print();
double det = determinant(mat1);
std::cout << "Determinant of Matrix 1: " << det << std::endl;
Matrix transposed = transpose(mat1);
std::cout << "Transposed Matrix 1:" << std::endl;
transposed.print();
return 0;
}
이제 스물다섯 번째 날의 학습을 마쳤습니다. 고성능 매트릭스 라이브러리 개발 프로젝트의 두 번째 단계를 통해 루프 언롤링 및 벡터화, 캐시 친화적 접근, 행렬의 역행렬 및 행렬식 계산 기능을 추가했습니다.
질문이나 피드백이 있으면 언제든지 댓글로 남겨 주세요. 내일은 "프로젝트: 고성능 매트릭스 라이브러리 개발 (3)"에 대해 학습하겠습니다.
반응형
'-----ETC----- > C++ 성능 최적화 및 고급 테크닉 시리즈' 카테고리의 다른 글
[C++ 성능 최적화 및 고급 테크닉] Day 26: 프로젝트: 고성능 매트릭스 라이브러리 개발 (3) (0) | 2024.08.01 |
---|---|
[C++ 성능 최적화 및 고급 테크닉] Day 24: 프로젝트: 고성능 매트릭스 라이브러리 개발 (1) (0) | 2024.08.01 |
[C++ 성능 최적화 및 고급 테크닉] Day 22: 실전 최적화 사례 연구 (1) (0) | 2024.08.01 |
[C++ 성능 최적화 및 고급 테크닉] Day 23: 실전 최적화 사례 연구 (2) (0) | 2024.08.01 |
[C++ 성능 최적화 및 고급 테크닉] Day 20: OpenMP를 이용한 병렬 프로그래밍 (0) | 2024.08.01 |