Clojure const 메타데이터 사용하기

2022년 09월 20일

Clojure

# clojure# optimization# metadata

📕 목차

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"
  )

참고자료

profile

박민기

단순하게 살아라. 현대인은 쓸데없는 절차와 일 때문에 얼마나 복잡한 삶을 살아가는가? - 이드리스 샤흐

© 2023, 미나리와 함께 만들었음