'캐시'에 해당되는 글 1건

  1. 2021.07.23 C# - 함수형 프로그래밍, 지연 - 캐시 기법
728x90

프로그래밍에서 지연이란 특정 코드의 실행을 연기하는 것을 뜻한다.

 

-지연 열거

C#에서 컬렉션 데이터를 열거하는 IEnumerable<T>는 지연 평가를 이용한다.

배열과 List<T>도 IEnumerable<T> 인터페이스를 구현하지만 데이터로 채워져야 하기 때문에 사전 평가가 이뤄져야 한다.

Enumerable.Skip(n) : 지정 된 개수의 시퀀스의 요소를 무시 하고 나머지 요소를 반환

Enumerable.Take(n) : 시퀀스의 시작 부분부터 지정 된 수의 연속 요소를 반환

IEnumerable이 지연 평가로 실행이 되기 때문에 foreach()문에서 컬렉션을 순회하면서 GetEnumerator()를 이용한 IEnumerator<T>형식에 존재하는 MoveNext() 메서드 같은 경우엔 결과 계산이 필요한 경우에만 호출이 된다. 

 

-지연 평가

if (data != null || data.count > 1) 이라는 조건 구문이 있을때

OR 연산자는 

data != null 이 false인 것을 확인하면 data.count > 1을 평가한다 하지만 data가 null이므로 count 속성에 접근이 불가하여 예외가 발생한다.

 

f (data != null && data.count > 1) 이라는 조건 구문이 있을때

AND 연산자

data != null이 false인 것을 확인하면 나머지 식을 판단하지 않고 지연시켜서 && 논리 연산의 결과를 false로 결론 내어 예외가 발생하지 않는다. 이런 지연 평가를 이용하면 하나의 식만으로 결론을 판단하고 나머지 식을 무시할 수 있다.

 

-엄격하지 않은 평가

4 + ( 3 * 2 ) 계산식을 작성할때 엄격한 평가는 (3 * 2)를 먼저 계산한 결과를 가지고 후에 + 4를 해주는 과정을 거치며

엄격하지 않은 평가는 + 연산자가 먼저 처리되고 다음으로 내부 공식인 (3 * 2)가 처리 되는 방식

평가 순서가 바깥에서 안쪽으로 향한다.

 

-지연 초기화

사용 시점까지 개체 생성을 연기하는 최적화 기법

멤버에 대한 액세스가 이뤄지기 전에는 초기화 되지 않는 개체를 정의하는 것이다.

C#에서는 Lazy<T> 클래스가 있다.

 

Lazy<Data> data = new Lazy<Data>(() => new Data());

Lazy를 이용하면 위와같이 data 변수를 정의한 후에도 초기화가 이뤄지지 않는다.

IsValueCreated 속성을 이용해서 data 변수가 초기화 되었는지 확인 가능하고

Value 속성을 이용해 인스턴스의 값을 가져올수 있다.

 

(data.Value as Data).Count

멤버 Count에 대한 접근을 하려 하면 값을 가져오기 위해 내부적으로 Data의 생성자를 먼저 호출하여 초기화를 한 후

Count 값을 가져오게 된다.

 

-지연의 장단점

장점

불필요한 기능을 위한 초기화 시간 절약 가능실행 
순서의 중요성이 떨어지는 경우로 프로그램을 더 효과적으로 실행 
가능지연을 이용해 보다 효율 적인 코드 작성 가능

단점

프로그램 흐름을 예측하기 어렵고 제어가 어려워 질 수 있다
지연을 구현하는 코드의 복잡성에 따른 성능 하락이 발생할 수 있다.

 

-사전 연산

캐시 기법의 하나로 조회 테이블을 이용하기 위해 초기 연산을 수행하는 방법이다.

조회 테이블은 특정 작업을 실행 할 때 반복적인 연산이 발생하는 것을 피하기 위한 것이다.

입력받는 값의 n승을 계산하여 반환하는 기능이 있다면

매번 실행될때마다 pow 과정을 실행하는 것이 아닌

int[] powerOfTwos = new int[100];

for (int i = 0; i < 100; i++)
	powerOfTwos[i] = (int)Math.Pow(i, 2);

처럼 미리 연산해둔 값을 저장하여 가지고 이후에는 해당하는 값에 조회만해서 가져오는 방식 

 

-메모화

특정 입력 값에 대한 함수의 처리 결과를 기억하는 과정

특정 함수에 입력 값을 전달할 때마다 프로그램이 결과를 기억해 두고 이후 입력 값이 같다면 함수를 다시 실행 하지 않고 저장된 곳에서 결과를 얻는다

Dictionary<int, int> memoizeDict = new Dictionary<int, int>();

if (memoizeDict.ContainsKey(number))
{
	return memoizeDict[number]
}
else
{
	int i = number * A()....
    	memoizeDict.Add(number, i)
    	return i;
}

처럼 A()함수의 처리 결과가 딕셔너리에 이미 저장되어 있다면 A() 함수 처리를 하지 않고
딕셔너리에서 바로 값을 반환 하는 형식이다.

 

이번 챕터를 공부하면서 느낀 점은

지연, 사전 연산, 메모화 등의 기법은

함수형 프로그래밍에 한해서 이용하면 더욱 더 효율적인 코드를 작성할 수 있다라기 보다는

우리가 전반적으로 코드를 작성하면서 이런 기법들을 잘 사용한다면 최적화된 효율적인 코드를 작성할 수 있다라는 생각을 많이 하게 되었고,

정식적인 기법 명칭을 인지하고 있지 않았을 뿐, 일부는 코드를 작성하면서 사용하고 있었던 것들도 있었다 

728x90
Posted by 정망스
,


맨 위로
홈으로 ▲위로 ▼아래로 ♥댓글쓰기 새로고침