메타프로그래밍은 아주 멋진 단어처럼 들리지만, 과연 좋을까?
유용할 수 있지만 많은 사람들이 메타프로그래밍을 사용하는 데 약간의 비용이 든다는 사실을 깨닫지 못합니다.
우리는 같은 페이지에 있습니다...
메타프로그래밍이란 정확히?
저는 메타프로그래밍을 다음과 같은 방법을 사용하는 것으로 정의합니다.
- 코드 구조 변경(예:
define_method
) - 실제 Ruby 코드의 일부인 것처럼 문자열을 실행합니다(예:
instance_eval
) - 일부 이벤트(예:
method_missing
)에 대한 반응으로 수행 )
그렇다면 메타프로그래밍의 비용은 얼마입니까? 나는 그들을 3개의 그룹으로 분류합니다:
- 속도
- 가독성
- 검색 가능성
참고 :네 번째 그룹인 보안이 있다고 말할 수도 있습니다. . 그 이유는 eval
입니다. 전달되는 내용에 대한 보안 검사를 수행하지 않는 메서드입니다. 직접 수행해야 합니다.
각각에 대해 더 자세히 살펴보겠습니다!
속도
대부분의 메타프로그래밍 방법이 일반 방법보다 느리기 때문에 첫 번째 비용은 속도입니다.
다음은 몇 가지 벤치마킹 코드입니다.
require 'benchmark/ips' class Thing def method_missing(name, *args) end def normal_method end define_method(:speak) {} end t = Thing.new Benchmark.ips do |x| x.report("normal method") { t.normal_method } x.report("missing method") { t.abc } x.report("defined method") { t.speak } x.compare! end
결과(Ruby 2.2.4) :
normal method: 7344529.4 i/s defined method: 5766584.9 i/s - 1.34x slower missing method: 4777911.7 i/s - 1.54x slower
두 메타프로그래밍 방법(define_method
&method_missing
)는 일반적인 방법보다 상당히 느립니다.
흥미로운 사실을 발견했습니다...
위의 결과는 Ruby 2.2.4
에서 가져온 것입니다. 하지만 Ruby 2.3
에서 이 벤치마크를 실행하면 또는 Ruby 2.4
이 방법이 점점 느려지고 있는 것 같습니다!
Ruby 2.4 벤치마크 결과 :
normal method: 8252851.6 i/s defined method: 6153202.9 i/s - 1.39x slower missing method: 4557376.3 i/s - 1.87x slower
우연이 아닌지 확인하기 위해 이 벤치마크를 여러 번 실행했습니다.
하지만 주의를 기울이고 초당 반복 수(i/s
) Ruby 2.3 이후로 일반 메소드가 더 빨라진 것 같습니다. 이것이 method_missing
의 이유입니다. 훨씬 느리게 보입니다 🙂
가독성
instance_eval
을 사용할 때 오류 메시지가 도움이 되지 않을 수 있습니다. / class_eval
방법.
다음 코드를 살펴보세요.
class Thing class_eval("def self.foo; raise 'something went wrong'; end") end Thing.foo
그러면 다음 오류가 발생합니다.
(eval):1:in 'foo': 'something went wrong...' (RuntimeError)
파일 이름이 누락되었음을 확인합니다(eval
대신) 및 올바른 줄 번호. 좋은 소식은 이 eval
메소드는 두 개의 추가 매개변수를 사용합니다.
- 파일 이름
- 줄 번호
내장 상수 사용하기 __FILE__
&__LINE__
class_eval
의 매개변수로 오류 메시지에 올바른 정보가 표시됩니다.
예 :
class Thing class_eval( "def foo; raise 'something went right'; end", __FILE__, __LINE__ ) end
이것이 기본값이 아닌 이유는 무엇입니까?
잘 모르겠지만 이 방법을 사용하려는 경우 유념해야 할 사항입니다 🙂
검색 가능성
메타프로그래밍 방법은 코드를 검색하기 어렵게 만들고, 더 나쁜 문서를 통해 액세스하기 어렵게 만들고, 디버그하기 더 어렵게 만듭니다.
메소드 정의를 찾고 있다면 CTRL+F(또는 사용하는 단축키)를 수행하여 메타프로그래밍을 통해 정의된 메소드를 찾을 수 없습니다. 특히 메소드 이름이 런타임에 빌드된 경우에는 더욱 그렇습니다.
다음 예는 3가지 방법을 정의합니다. 메타프로그래밍 사용:
class RubyBlog def create_post_tags types = ['computer_science', 'tools', 'advanced_ruby'] types.each do |type| define_singleton_method(type + "_tag") { puts "This post is about #{type}" } end end end rb = RubyBlog.new rb.create_post_tags rb.computer_science_tag
문서를 생성하는 도구(예:Yard
또는 RDoc
) 이러한 방법을 찾을 수 없고 나열할 수 없습니다.
이러한 도구는 "정적 분석"이라는 기술을 사용하여 클래스 및 메서드를 찾습니다. 이 기술은 def
를 사용하여 직접 정의된 메서드만 찾을 수 있습니다. 구문).
yard doc
를 실행해 보세요. 마지막 예에서 찾은 유일한 메소드는 create_post_tags
입니다. .
이렇게 생겼어요 :
yard
를 알려주는 방법이 있습니다. @method
를 사용하여 추가 메소드 문서화 태그가 있지만 항상 실용적인 것은 아닙니다.
예 :
class Thing # @method build_report define_method(:build_report) end
또한 grep
와 같은 도구를 사용하려는 경우 , ack
, 또는 편집기가 메서드 정의를 검색하는 경우 메타프로그래밍 메서드를 찾기가 더 어려워질 것입니다 일반 방법보다.
"내 생각에 Sidekiq은 어떤 메타프로그래밍 왜냐하면 95%의 시간을 돕는 것보다 코드를 더 모호하게 하기 때문입니다." – Sidekiq의 제작자 Mike Perham
결론
메타프로그래밍의 모든 것이 나쁜 것은 아닙니다. 적절한 상황에서 코드를 보다 유연하게 만드는 데 유용할 수 있습니다.
더 나은 결정을 내릴 수 있도록 추가 비용을 알고 계십시오.
이 게시물을 공유하는 것을 잊지 마세요. 유용하셨다면 🙂