2025년 12월 9일 화요일

Pixies - Greens and Blues


 너바나들의 노래가 불의의 사고에 의해서 종결형으로 멈춰져 버려서 갈증이 해소되기는 커녕

죽은 자식 끌어 않고 한탄하는 듯한 허무함이 많다.

죽지 않고 버텨서 더러운 꼴 이것저것 다 겪고 난 후의 노래들은 어땠을까 궁금하기도 하다.

말해봐야 뭐하겠는가? 그 대체제로 요즘 Pixies의 노래를 자주 듣는다.

더욱이 아직 살아있어서 근래의 활동하는 모습도 볼 수 있어서 위안되기도 한다.

드러머의 티셔츠의 일장기가 영 거슬기는 하지만, 노래만은 위안이된다.

나이 들어 가면서 어쩔 수 없이 부끄럽거나 불편한 자리 싫어도 해야 할때,

이런 감정도 있었다는 것을 상기 시켜줘서 편안하다. 

"잠시 있다가 갈게 내가 알아서 갈게 너무 신경 쓰지마."


2025년 12월 7일 일요일

Python 코드의 성능을 높일 수 있는 10가지 영리한 방법

[출처: https://blog.jetbrains.com/pycharm/2025/11/10-smart-performance-hacks-for-faster-python-code/ ]

1. 내부항목 검사(Membership Test)에 효과적인 Set(집합) 활용하기

배열형 자료형의 내부항목을 검사는 다음과 같은 "내부항목 in 배열형" 연산자와 if문을 조합하여 처리한다.



하지만, 가장 손쉽게 사용하는 list를 검색하는 것은 특히 비효율적이다. list의 크기가 커질수록 내부항목검사(x in some_list)는 각 내부항목을 하나씩 스캔해서 비교해야 하므로 S/W time complexity는 선형 시간 복잡도 (O(n))를 가집니다(여기서, n은 내부항목의 개수).


위와 같이 최악의 경우를 상정한 것이기는 하지만, 총 1M 개를 내부항목으로 갖는 list와 set의 내부항목 검사에 소요되는 시간은 다음과 같다. 

True

List lookup: 0.005432s

True

Set lookup: 0.000081s


파이썬의 set은 내부항목 관리를 해시 테이블로 구현되어 평균적으로 상수 시간(O(1)) 조회 성능을 제공하여 특히 대규모 데이터셋을 다룰 때 set 내 값 존재 여부 확인할때 훨씬 빠르다.

통상적으로 set보다 list를 고민없이 사용하는 경우들이 많은데  set가 list보다 훨씬 효율적이다; set는 내부항목 검사 속도를 높일 뿐만 아니라 합집합, 교집합, 차집합과 같은 연산을 훨씬 빠르고 간결하게 수행합니다.

내부항목 검사에 list 대신 set를 사용하면(특히 성능이 중요한 코드에서) 로직을 최소한으로 변경하면서도 상당한 속도 향상을 달성할 수 있다.


2. 불필요한 복사 행위를 피하라.

list, dict 또는 array 같은 큰 객체를 복사하는 것은 시간과 메모리 측면에서 비용이 많이 들 수 있다. 복사 작업은 메모리에 새로운 객체를 생성하며, 이는 특히 대규모 데이터셋을 다루거나 많은 반복 작업을 수행해야하는 반복문 내에서 작업할 때 상당히 부담스러운 오버헤드가 된다. 

가능한 경우 객체를 복제하지 말고 그 자리에서 수정하여 새 구조체를 할당하고 채우는 오버헤드를 피하자.(메모리 사용량을 줄이고 성능을 향상시킵니다.) 파이썬의 많은 내장 데이터 구조들은 복사 작업이 필요 없는 인플레이스 메서드(예: sort, append, update)를 제공하고 이를 적극 활용하자.


당연하게도 in-place(복사행위 없는) 작업이 훨씬 속도가 빠르다.

In-place: 0.0000s
Copy: 0.0049s

성능이 중요한 코드에서는 객체가 언제, 어떻게 복제되는지 주의 깊게 살펴보고 reference(new_list = list)와 in-place 연산을 활용하면 크거나 복잡한 데이터 구조를 다룰 때 더 효율적이고 메모리 친화적인 코드를 작성할 수 있다.


3. 메모리 효율성을 위해서 객체의 구성요소관리에 __slots__ 를 사용하자.

기본적으로 Python 클래스는 인스턴스 속성을 동적 사전(__dict__)에 저장한다. 이는 유연성을 제공하지만 메모리 오버헤드가 발생하고 속성 접근 속도가 약간 느려진다.

__slots__를 사용하면 클래스에 대해 고정된 속성 집합을 명시적으로 선언할 수 있습니다. 이는 __dict__의 필요성을 없애 메모리 사용량을 줄여주며, 특히 클래스의 인스턴스를 많이 생성할 때 유용합니다. 또한 간소화된 내부 구조 덕분에 속성 접근 속도가 약간 빨라집니다.

__slots__는 동적 속성 할당을 제한하지만, 메모리 제약 환경이나 성능에 민감한 애플리케이션에서는 효율적이다. 경량 클래스나 데이터 컨테이너의 경우 __slots__를 적용하는 것이 코드를 더 효율적으로 만드는 간단한 방법이다.


하지만, 결과는 들쑥 날쑥이다. 우선 이론상 동적 메모리 관리가 정적 메모리 관리보다 훨씬 시간이 더 든다는 것은 당연한 사실이니 나름 합리적인 내용이다. 하지만 결과는 들쑥날쑥.... 

Without slots: 0.6076s
With slots: 0.5399s
...
Without slots: 0.4644s
With slots: 0.6735s

마무튼 이 내용은 보류.


4. 기본 연산자 대신 math 패키지를 사용하자.

수치 계산의 경우, Python의 math 모듈은 C로 구현된 함수를 제공하여 순수 Python으로 작성된 동일한 연산보다 더 나은 성능과 정밀도를 제공한다. 

예를 들어, math.sqrt()를 사용하는 것이 지수 연산자(**)를 사용하여 숫자를 0.5제곱하는 것보다 일반적으로 더 빠르고 정확합니다. 마찬가지로, math.sin(), math.exp(), math.log()와 같은 함수들은 속도와 신뢰성을 위해 고도로 최적화되어 있습니다.

이러한 성능 향상 효과는 특히 반복 작업을 수행하는 반복문이나 대규모 계산에서 두드러지게 나타난다. 수치 연산이 많은 작업에 수학 모듈을 활용하면 더 빠른 실행 속도와 더 일관된 결과를 동시에 달성할 수 있어, 과학적 계산, 시뮬레이션 또는 수학 연산이 많은 코드에 최적의 선택이다.


결과를 보면 명확해 진다.

Math sqrt: 0.4781s
Operator: 0.6468s


5. 공간의 크기가 명확히 정해져 있다면 메모리를 사전에 할당해서 사용하자.

동적으로 list나 array을 생성할 때 파이썬은 내부적으로 크기가 커짐에 따라 자동으로 크기를 조정한다. 편리하지만, 이러한 크기 조정은 새로운 크기의 메모리 할당과 데이터 복사 그리고 기존의 메모리 반납을 수반하여 오버헤드를 발생시킨다. 특히 대규모 루프나 성능이 중요한 루프에서 더욱 그렇다.

데이터 구조의 최종 크기를 미리 알고 있다면 매번 필요시에 동적할당을 수행하는 방식이 아닌 사전에 메모리를 미리 할당해서 사용하여 성능 향상시킬 수 있다. list나 array를 고정된 크기로 초기화하면 반복적으로 발생할 수 있는 (동적할당->복사->메모리반환) 크기 조정을 피할 수 있으며, 파이썬(또는 NumPy 같은 라이브러리)이 메모리를 더 효율적으로 관리할 수 있다.

이 기법은 수치 계산, 시뮬레이션, 대규모 데이터 처리에서 특히 유용하며 이런 작은 최적화조차도 연쇄적인 누적 효과를 낼 수 있다. 메모리를 사전 할당해서 사용하면 메모리 파편화(Memory Fragmentation)를 줄이고 캐시 지역성을 개선하며 더 예측 가능한 성능을 보장한다.


Pre-allocated: ~0.0300s
Dynamic: ~0.0400s


6. 집중 연산 구간에서 예외처리를 사용하지 말자.

파이썬의 예외 처리는 예상치 못한 동작을 관리하는 데 강력하고 깔끔하지만, 성능이 중요한 루프 내에서 적합하도록 설계된 것은 아니다. 예외 발생 및 포착에는 스택 추적(unwinding)과 컨텍스트 전환이 수반되며, 이는 상대적으로 비용이 많이 드는 작업이다.

hot loop(반복적으로 실행되거나 대량의 데이터를 처리하는 코드 구간)에서 제어 흐름을 위해 예외를 사용하면 성능이 크게 저하된다. 대신 오류가 발생하기 전에 이를 방지하기 위해 조건부 검사(if, in, is 등)를 사용하면, 훨씬 빠를 뿐만 아니라 더 예측 가능한 실행을 수행한다.

예상되는 제어 흐름보다는 진정으로 예외적인 경우에만 예외를 허용하면, 특히 성능이 중요한 타이트한 루프나 실시간 애플리케이션에서 더 깔끔하고 빠른 코드를 얻을 수 있다.


Conditional: 0.8241s
Exception: 1.3350s


7. 반복 수행과정에서 local 함수를 사용하자.

함수 내에서 특정 논리 블록이 반복적으로 수행해야 할 경우, 이를 지역(중첩) 함수(closure라고도 함)로 정의하면 성능과 가독성을 향상시킬 수 있다. 지역 함수는 이름 탐색 속도가 더 빠릅니다. 당연하게도 파이썬이 전역 범위보다 지역 범위에서 변수 혹은 함수를 조회하는 것이 더 빠르기 때문이다.

이 기법은 루프, 데이터 변환 또는 재귀적 프로세스와 같이 동일한 작업을 여러 번 적용하는 함수에서 특히 유용하고, 자주 사용되는 로직을 로컬로 유지함으로써 런타임 오버헤드와 인지 부하를 모두 줄일 수 있다.



Local function: 0.3879s
Global function: 0.6965s


8. 조합 연산에는 itertools를 사용하자.

개인적인 경험상 이를 통해 잃을 수 있는 손해가 더 많다고 생각하고 실행 속도의 실요성도 없어 보여서 제외함.


9. 정렬된 list 다룰 때 bisect를 사용하자.

정렬된 목록을 다룰 때 선형 검색이나 수동 삽입 로직을 사용하면 비효율적이다. 특히 목록이 커질수록 더욱 심해진다. Python의 bisect 모듈은 이진 검색을 사용하여 정렬된 순서를 유지하는 빠르고 효율적인 도구를 제공한다.

bisect_left(), bisect_right(), insort() 같은 함수를 사용하면 단순 스캔의 O(n) 복잡도와 달리 O(log n) 시간에 삽입 및 검색을 수행할 수 있다. 이는 리더보드 유지 관리, 이벤트 타임라인 관리, 효율적인 범위 쿼리 구현 같은 시나리오에서 특히 유용하다.

bisect 모듈을 사용하면 list 변경 후 매번 재정렬할 필요가 없어지며, 동적 정렬 데이터 작업 시 상당한 성능 향상을 얻을 수 있습니다. 이는 가볍고 강력한 도구로, 일반적인 list 작업에 알고리즘적 효율성을 제공합니다.



Bisect: 0.0004s
Loop: 0.0027s


10.  루프 내에서 반복된 함수 호출을 피하라.

이해할 수 없어서 개인적으로 제외한다.


요약

itertools, bisect, collections와 같은 특수 모듈은 복잡한 작업을 더욱 간소화하며, 전역 변수 사용 최소화, 메모리 사전 할당, 캐싱 구현과 같은 모범 사례 준수는 간결하고 효율적인 코드 실행을 보장합니다. 

2025년 11월 29일 토요일

c++에서 decltype(...)의 용도

 예전에 c언어에서 함수를 매개변수로 사용하기 위한 함수 타입을 추출하기 위해서 다음과 같은 작업을 수행해 왔다.


위와 같이 typedef로 함수 타입을 일일히 적시해 주어야 한다. 이러한 주어진 표현식(변수, 함수 등..)의 정확한 타입을 추출해서 컴파일 시점에 알려주는 기능이 decltype()이다.



2025년 11월 25일 화요일

Kanade 알고리즘(C++17 기반) - Maximum Sum Subarray Problem(최대 합을 갖는 부분 배열 찾기?)

0. 이건 뭔고?

연속된 시간 상의 영상 간의 optical flow를 추정하는데 유용하다고 하며, stereo imaging에서 depth map을 산출 할때 신뢰할 수 있는 기준점(ground control points)을 확보하는데 유용하다고 한다. 우선 이렇게 알아 두고 차츰 어떻게 적용되는지 알아 보도록 하자.

1. 내부변수

possible_subsum : 최대 부분합이 될 가능성이 있는 부분합

restart : possible_subsum 이 갱신되는 시점의 시작지점

max_subsum : 최대 부분합으로 possible_subsum과 항시 비교하여 최대값으로 유지된다.

start : max_subsum이 갱신되는 시점의 restart의 저장 변수

finish : max_subsum이 갱신되는 시점의 내부항목의 현재 위치

2. 사용법

    vector<int> a = { -8, -3, -6, -2, -5, -4 };

    auto [subsum, i, j] = kanade(a);

3. 코드


4. 컴파일 

$ g++ -std=c++17 kanada.cc -o kanada

$ ./kanada


2025년 10월 31일 금요일

Anoying NVIDIA Driver troubleshooting

Remove the current driver and reinstall it!
$ sudo rm -rf /var/lib/dkms/nvidia$ sudo apt install nvidia-driver-535 # Replace with the appropriate driver version
Additionally, install the below.
$ sudo apt install linux-headers-$(uname -r) 
 

2025년 10월 29일 수요일

2025-10-29 현시점에서 개인 개발자를 위한 가장 현실적인 인공지능 학습을 위한 GPU workstation

 GPU - Nvidia RTX 4090(24GB) : 학습데이터 메인메모리에서 GPU로 옮기다가 에러나가 싫으면 24GB는 되어야 한다고 생각한다.

CPU - 사실 별로 중요하지 않다. 이왕이면 AMD로

RAM - 뭐가 되었든 최소 64GB 이상 데이터를 그래프화 하거나 저장하거나 할때 말도 않되는 메모리 사용량에 프로그램이 멈추는 경험을 하기 싫다면 많을 수록 좋다.

메인보드 - 당장 GPU를 하나밖에 달 수 없는 주머니 사정을 어쩔 수 없더라도 누가 알겠는가 어쩌다 돈이 떨어져서 GPU를 하나 더 달 수 있는 상황이 발생할지도 그러니까 그러니 GPU를 어러게 달 수 있는 메인보드를 선택하자 당장은 어쩔 수 없이 하나만 달아서 쓰더라도.

 파워서플라이 - 850W 이상 끝.

Keras 정확히는 Tensorflow의 메모리 부족 문제 해결책

 처음에는 학습데이터의 양이 메인 메모리의 한계를 넘어가면서 메모리를 증설했고 그 다음에는 학습데이터를 GPU에 옮기는 과정에서 GPU 메모리가 부족하여 batch_size를 줄여도 보았으나 다음과 같은 에러 메시지는 해결되지 않았다. 

" InternalError: Failed copying input tensor from /job:localhost/replica:0/task:0/device:CPU:0 to /job:localhost/replica:0/task:0/device:GPU:0 in order to run _EagerConst: Dst tensor is not initialized."

 확실하진 않지만 위와 같은 메시지 였던것 같다. CPU에서 다루던 데이터를 GPU로 옮기다가 사고가 나는 것 같다. 이치상으로 보면 batch_size를 줄이면 해결되어야 하는데 해결되지 않았고 어디서 다음과 같은 DataGenerator로 조금씩 데이터를 흘려 주면 된다고 했는데 나한테는 전혀 효과가 없었다.


class DataGenerator(Sequence):
  def __init__(self, x_set, y_set, batch_size):
    self.x, self.y = x_set, y_set
    self.batch_size = batch_size

  def __len__(self):
    return int(np.ceil(len(self.x) / float(self.batch_size)))

  def __getitem__(self, idx):
    if idx == self.__len__() - 1:
      batch_x = self.x[idx * self.batch_size : ]
      batch_y = self.y[idx * self.batch_size : ]
    else:
      batch_x = self.x[idx * self.batch_size : (idx+1) * self.batch_size]
      batch_y = self.y[idx * self.batch_size : (idx+1) * self.batch_size]
    return batch_x, batch_y

train_gen = DataGenerator( X_train_split,  y_train_split,  batch_size)
test_gen = DataGenerator(X_val_split, y_val_split, batch_size)

history = self.model.fit(
 train_gen,
 validation_data=test_gen,
 epochs=epochs,
 callbacks=callbacks,
 verbose=1

 정확히 문제가 되는 지점이 학습 완료 후 평가하는 과정에서 사단이 나서 굉장히 열받는 상황이었다. 며칠을 걸려서 학습을 완료했는데 왜 평가할때 프로그램이 사망하냐구.

 아무튼 좀 더 구글링을 해서 찾은 해결책은 학습기간 동안 최적의 모델을 파일형태로 저장하도록 하고 기존 모델을 제거해서 메모리 문제를 제거하는 방법을 시도해 보았다. 그리고 최적의 모델은 다시 파일에서 로딩하는 방식으로... 하지만 다음 명령어는 효과가 없었다.

del model

 뭔가 cuda와 cuDNN 그리고 tensorflow의 backend 사이에 꼬여 있는 부분이 있는 것 같다. 여러 차례의 시도를 하여 각 tensorflow의 backend의 각세션을 학습 완료 후 재 초기화 하고 "del model"을  수행한다. 그리고 garbage collect을 통해 안쓰는 데이터를 메모리에서 해제한다. 그 결과 평가 단계로 수훨하게 넘어 갈 수 있었다. 그 기록을 여기에 남겨 둔다.

 ... 학습 완료 후...

    # reset Keras Session
    sess = tf.compat.v1.keras.backend.get_session()
    tf.compat.v1.keras.backend.clear_session()
    sess.close()
    sess = tf.compat.v1.keras.backend.get_session()
    del self.model  # this is from global space - change this as you need
    # use the same config as you used to create the session
    config = tf.compat.v1.ConfigProto()
    config.gpu_options.per_process_gpu_memory_fraction = 1
    config.gpu_options.visible_device_list = "0"
    tf.compat.v1.keras.backend.set_session(tf.compat.v1.Session(config=config))

    import gc

    gc.collect()

... 저장된 파일에서 self.model을 다시 로드한다...

2025년 7월 17일 목요일

너무나도 넘쳐나는 대 배터리의 시대

 모든 전자기기에 배터리가 기본으로 장착되는 것 같다. 서랍에 짱박아 논 외장 배터리 송풍기 등등 너무나도 넘쳐난다. 하지만 난 한번에 하나밖에 쓸 수 밖에 없다. 맘이 불편하다. 이젠 쓰고 싶지 않아도 필수로 배터리가 장착된 제품만 존재한다.

1. 대 배터리의 시대

2. 배터리없이 쓸 수 있는 제품이 없다.

3. 배터리 없는 것 쓰고 싶다. 

2025년 5월 22일 목요일

PCB Layering for 4 layers and 6 layers

- 4 Layers

Signal + Component

GND

Power

Low speed signal


- 6 Layers

Signal + Component

GND

Power

Signal

GND

Signal