Clojure const 메타데이터 사용하기
2022년 09월 20일
Clojure
📕 목차
const 메타데이터 사용하기
이번 글은 클로저에서 ^:const
를 사용해서 메타데이터를 정의하면 어떠한 이점이 있는지 알아보도록 하겠습니다. ^는 매크로 캐릭터이며 클로저에서 메타데이터를 나타냅니다. 자세한 내용은 여기를 참고하시면 됩니다.
const 메타데이터를 사용하는 것은 컴파일 타임에 const 메타데이터를 참조하고 있던 부분을 def의 값으로 변경하여 Var.getRawRoot
호출을 스킵함으로써 성능에 이점이 있습니다.
인터넷에서 찾은 몇 가지 예시를 통해서 실제 차이점을 살펴보겠습니다.
(def pi 3.14)
(defn circ [r] (* 2 pi r))
위의 경우에는 circ가 호출될 때마다 런타임에 pi를 derefernces 하기 위해서 Var.getRawRoot
함수를 호출합니다.
(def ^:const pi 3.14)
(defn circ2 [r] (* 2 pi r))
위의 경우, circ2
는 다음과 같은 코드로 컴파일됩니다.
(defn circ2 [r] (* 2 3.14 r)) ;; pi -> 3.14로 치환
테스트 결과
user> (time (dotimes [_ 1e5] (circ 1)))
"Elapsed time: 16.864154 msecs"
user> (time (dotimes [_ 1e5] (circ2 1)))
"Elapsed time: 6.854782 msecs"
주의사항
^:const
를 통해서 컴파일타임에 값으로 변환되는 경우는 원시 타입(primitive type)인 경우에만 동작합니다.
궁금증
keyword는 primitive type인가? 실제 성능상에 차이가 있는지 직접 테스트해보자.
user=> (def foo :table-name)
#'user/foo
user=> (def ^:const bar :table-name)
#'user/bar
user=> (time (dotimes [i 1e9] (name foo)))
"Elapsed time: 1323.538459 msecs"
user=> (time (dotimes [i 1e9] (name bar)))
"Elapsed time: 712.810625 msecs"
honeysql로 테스트해보자. 차이가 있는지 모르겠다.
(ns constant
(:require [honey.sql.helpers :as h]))
(def foo :table-name)
(def ^:const bar :table-name)
(comment
(time (dotimes [_ 1e8] (h/from foo))) ;; => "Elapsed time: 13910.838625 msecs"
(time (dotimes [_ 1e8] (h/from bar))) ;; => "Elapsed time: 13863.620292 msecs"
)