Oct 22, 2022 - i-switched-job

블로그 공백 8개월 간 있었던 일 - 이직

블로그를 쓰는건 굉장히 오랫만입니다. 글을 올리지 않은 그 기간동안 많은 일이 있었고, 특히 이직한 경험에 대해 블로그를 쓰고 싶었습니다. 게다가 글쓰기에 대한 시각도 바뀌었습니다. 이전에는 보여주기 위함이었다면 이제는 좋은 글을 써보고 싶습니다.

2022년에 접어들며 회사에선 시장 가치가 높은 동료들이 나가는 것을 보고 저도 마음의 준비를 하고 있었습니다. 처음엔 cold call로 가벼운 마음으로 시작했지만 생각보다 내 경쟁력이 높지 않았다는 인상을 받게 되어 마음이 조급해졌습니다.

이전부터 CS는 비전공자인 저의 약점이었습니다. 이를 알고 오랫동안 틈틈히 공부해왔고 그 결과를 블로그로 남기고 싶었습니다. 하지만 제가 겪은 바, CS 내용에 대해 시장에선 별로 관심이 없는 것 같습니다. 특히 저는 MLOps, ML Product 개발자에 가깝기 때문에 BE 개발자보단 CS 중요도가 낮은 것 같습니다. 그래서 블로그를 굳이 완성해야겠다는 생각이 없어진 현재입니다.

이력서는 시중에 오픈된 샘플을 참조해 여러번 수정을 거쳐 리쿠르터의 관심을 충분히 끌었습니다. 반면 면접 결과는 다소 아쉬웠고 마음을 힘들게 했습니다. 10개 중에 1개만 합격하면 된다고 끝까지 버틴 것 같습니다. 추후에 면접에 관련된 사항을 자세하게 글 남기려고 합니다.

내가 개발자로 일하는 현재 삶에 만족합니다. 하지만 나라는 존재와 개발자라는 업은 분리되어야 한다고 생각합니다. 개발자 이전에도 나의 삶이 있어야 합니다. 지금까지는 좋은 회사, 동료, 환경을 바라보며 모든 리소스를 단 한가지에 모두 걸었습니다. 이제는 개발자로서의 성장보다는 사람으로서의 성장이 더 중요합니다. backend engineer보다는 life engineer가 되는 것이 이 세상을 사는 효과적인 방법이라 믿습니다.


합격 후 잠시 쉬는 기간을 마련해 제주도로 여행을 다녀왔습니다. 한라산 등반 영상을 보고 단순히 나도 가고 싶었습니다. 바보같이 성판악-시라오름-관음사 코스로 다녀 10시간 가량 걸렸습니다. 생각보다 몸이 힘들다기보다는 마음이 힘들었습니다. 힘든 상황에서 가려진 본심이 나온다고 합니다. 제 본모습은 걱정보단 훨씬 좋았지만 기대보단 멋이 없었습니다. 응원보단 낙담에 가까운 저의 모습을 관찰하며 자꾸 핑계를 대려하는 나를 발견했습니다. 아직도 핑계와 체면치레가 많이 남아있구나를 깨닫습니다.

20살, 대입 수능에서 아쉬운 점수를 받아 우울한 마음을 한강 트래킹으로 달래곤 했습니다. 근 10년만에 똑같은 코스에 들어서자 그 때의 감정이 똑같이 느껴지는 것 같았습니다. 지금도 나라는 사람은 똑같았습니다. 부족하고 나약한 마음은 여전히 가지고 있지만 오직 달라진 것은 그 마음을 다루는 태도인 것 같습니다.

삶의 모든 것은 레버리지할 대상이라고 믿어보려합니다. 똑똑한 사람이라면 위기조차 기회로 만들 수 있듯이, 과하게 겁을 먹고 물러서는게 아닐지 스스로 돌아보고 내린 결심입니다.

Jan 19, 2022 - 새해 새글

새해엔 작년 회고지요.

정말 시간이 금방 간다. 나이가 50이 되면은 정말 눈 깜빡하면 1년이 지나갈 것 같은데. 작년 한 해는 개발자의 삶을 아주 충실하게 살았다. 머리가 돌아가는 시간의 80% 이상을 투자했겠다. (그래서 시간이 짧은가?) 재미도 있지만 점점 짜증도 많아지는 것 같다.😡 모르는 게 줄기는 커녕 더 많아졌다.🥲

오늘은 처음으로 면접관으로 면접을 본 날이다. 확실히 대기업보다 좋은 경험을 빠르게 할 수 있다는 장점이 있다.

오늘은 글이 차암 안 써진다. 억지로 쓰는 것도 영 아닌 것 같다.

Dec 18, 2021 - Cs2

시작하며

R 과제로 시작해서 Python과 pandas를 넘어 Golang, k8s로 넘어오기까지 매번 모르는 것이 너무나 많았고 번번히 구글링 땜빵만 해왔다. 이번 기회에 내 code가 컴퓨터에서 어떻게 돌아가는지의 개념을 기초부터 정리해본다. 내가 가장 오랫동안 본 top에서부터 시작한다.

img.png

top

기본적인 설명은 자료가 많다. https://sabarada.tistory.com/146 가장 잘 보이는 cpu, memory 항목의 주요 개념과 구글링 쿼리를😅 알자.

cpu

머신러닝 학습을 빠르게 하려면 어떻게 해야하는지 항상 궁금했다.

cpu는 많이 쓸 수록 좋다. python 프로세스는 cpu가 100%로 제한되므로 multiprocessing을 통해 프로세스 N개로 나눠 띄울 수 있다. 여기서 spawn, fork 개념이 나온다. numpy, sklean와 같은 좋은 패키지는 cython nogil로 100% 제한을 해제한다. https://github.com/scikit-learn/scikit-learn/blob/844b4be24d20fc42cc13b957374c718956a0db39/sklearn/decomposition/_cdnmf_fast.pyx pytorch에선 openmp를 통해 제한을 푼다. https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/ParallelOpenMP.h#L22

cpu의 기본 명령어는 데이터 읽기/쓰기, 가감승제, and, or 연산과 같이 간단하다. 아무리 복잡한 프로그램도 이런 cpu 명령의 조합이다. 별도로 matrix 연산 속도를 위한 SSE, MMX 명령어가 있고 이를 intel mkl 라이브러리에 구현한다. numpy의 성능은 mkl과 같은 blas에서 온다.

기본적으로 cpu에게 효율적으로 일을 시킬 수 있는 compiler가 중요하다. https://sungjjinkang.github.io/c++/computerscience/2021/03/22/SIMD.html 동일한 로직도 compiler가 잘하면 성능이 좋아지며 numba jit compiler가 있는 이유다.

cpu의 효율은 cache(L1, L2)에 달려있다. 연산 성능만 따지면 cpu는 기가헤르츠 단위의 연산을 할 수 있지만 연산에 필요한 데이터를 가져오는 속도는 이를 못 받쳐준다. https://formulusblack.com/blog/compute-performance-distance-of-data-as-a-measure-of-latency/ 따라서 cache 영역에 핏한 데이터로 가공해야 연산 성능이 오른다. 이러한 전략을 cache miss를 줄인다고 하며, numexpr에서 볼 수 있다. https://numexpr.readthedocs.io/projects/NumExpr3/en/latest/intro.html# memory 할당도 cpu의 일이므로 copy보다 inplace update가 더 빠르다.

cpu는 설계 단위로 보면 socket - core - thread 로 볼 수 있고, 데이터는 cache - memory 로 볼 수 있다.
socket은 cpu가 물리적으로 붙어있는 단위로 numa를 socket 단위로 잡는다. 물리코어는 2개의 논리코어를 가지며 cache를 공유한다. m5.16xlarge는 2 socket 각 32 core 가운데 16 물리코어 구성이다.

top으로 cpu 개수를 확인할수 있고 cpu마다 논리코어인지 물리코어인지 확인한다. cat /sys/devices/system/cpu/cpu3/topology/core_id 논리코어를 끄면 chcpu -e 16,17,18,19,20... openmp를 활용한 병렬처리 성능이 향상됨을 확인했다. ml workload에서는 동일한 물리 코어의 cache를 경쟁하는 상황이 되어 hyper thread 성능이 떨어진다. 다른 방법으로 환경변수 OMP_NUM_THREADS;OMP_PROC_BIND;GOMP_CPU_AFFINITY로 동일한 효과를 줄 수 있다.

memory

top의 memory 관련 지표 가운데 buff/cacheSwap을 제외한 수치는 단어 그대로 받아들이면 된다.

buff/cache는 file read를 빠르게 하는 캐시다. 참조 그래서 메모리 사용이 증가하면 줄어든다. Swap은 virtual memory 용어로 disk를 활용해서 RAM보다 큰 데이터를 올리는 개념이다. buffer/cache에서 오랫동안 쓰이지 않은 데이터는 Swap으로 밀려난다. swapon --show으로 보면 swap의 크기와 파일 위치를 확인할 수 있다.

loop_cnt = 9999

for i in range(loop_cnt):
    fn = f"file.{i}"
    s = "".join(["1234567890"] * 99999)
    with open(fn, 'w') as f:
        f.write(s)

import os
for i in range(loop_cnt):
    os.remove(f"file.{i}")

buff/cache 값이 증가했다가 감소하는 것을 확인할 수 있다.

import numpy as np

array = np.zeros((99999, 99999))

VIRT 수치가 증가하는 것을 볼 수 있다.

array[:5000, :] = 1

RES 값이 상승한다.

2를 누르면 numa node view로 전환된다. NUMA는 cpu socket 단위로 메모리를 TBW

process

/proc

top의 데이터 소스이며 https://tldp.org/LDP/Linux-Filesystem-Hierarchy/html/proc.html os의 모든 process 정보를 가진다. sysctl은 /proc을 수정하는 명령어. https://en.wikipedia.org/wiki/Procfs

/proc/$PID/maps으로 heap, stack에 할당된 값을 확인할 수 있다.