목록2022/07 (12)
cgiosy.dev
(아직 미완성인 글이다. 추후 더 정확히 벤치마크해서 자료를 추가할 예정.) 데이터를 통신 및 스트리밍할 때, 대역폭(IO나 네트워크 속도)보다 빠른 압축 알고리즘은 보통 필요치 않다. 이 글에선 사용례에 따라 잘 알려진 압축 알고리즘들을 분류해본다. 오라클 Ampere A1과 맥북 에어 M1 CPU를 기준으로 측정했다. 일반적인 클라우드의 CPU 성능은 이 사이에 존재하는 편이기에, 중간 정도의 값을 테스트해보고 결과에 따라 압축 수준을 낮추거나 높이도록 하자. 싱글 스레드를 기준으로 하며, 멀티 스레드의 경우 대역폭을 CPU의 물리 코어 수로 나눠서 보면 될 것 같다. Disk I/O 읽기와 쓰기에 대한 대역폭이 제한된 것이므로, (원본 크기 + 압축 크기) / 소요 시간 / 1 MiB을 기준으로 계..
일급 함수 있는 언어에서 익명 재귀함수 만드는 테크닉. 재귀함수를 인자로 넘기는 상황이나 딱 한 번만 쓸 때 써봄직하다. 재귀 없는 언어에선 재귀 구현이 가능하게 해주나, 그런 언어는 안 쓰이니 실용성은 없다. Z 컴비네이터에 적당한 규격의 함수, 예를 들어 Z(self => n => (n ? n + self(n - 1) : 0)) 처럼 넣으면 n까지의 합을 구하는 익명 재귀함수를 만들 수 있다. 그래서 이걸 어떻게 만들까? 우선 f에서 자기자신을 호출하기 위해 스스로를 인자로 받아야 하므로, 첫 번째 인자인 self를 f로 채워주는 함수 M = f => f(f)를 선언한다. 그러면 M(f)를 했을 때 self에 f 자신, self => n => ... 부분이 들어갈 것이다. f에선 self(self)를..
100~200MB 정도의 로그 파일 30 ~ 100개에서 특정 문자열을 찾아 분석해야 해서, 다음과 같은 코드를 적당히 짰다. const { resolve } = require('path'); const { writeFile, readFile, readdir } = require('fs').promises; const walkDir = (fn, startingDir = '.') => { const _walkDir = (currentDir) => readdir(currentDir, { withFileTypes: true }).then((children) => Promise.all( children.map((child) => { const res = resolve(..
V8의 코드를 보며 문자열을 어떻게 관리하는지 작성하였다. 문자열은 기본적으로 1바이트 배열 / 2바이트 배열로 구분된다. 대부분의 경우 효율을 위해 1바이트를 사용하되, 표현 불가능한 문자가 포함되어 있으면 2바이트로 전환한다. 유니코드 처리를 다른 언어들, 예를 들면 Go는 rune, Rust는 grapheme처럼 별도의 타입이 존재하고 개발자가 이를 신경써야 하는 경우가 많다. 혹은 UTF-8처럼 가변 길이 인코딩을 써서 문자열 랜덤 액세스가 $O(N)$이거나... 이와 달리 JS에선 유니코드 문자열에 고통받을 일을 크게 줄였다. 개인적으로 다른 언어들에 비하면 평범한 개발자에게 매우 친화적인 구현이라고 생각한다. 물론 반대로 말하면 개발자의 선택권이 제약된단 뜻이기도 하고, 다음처럼 지식이 요구되..