Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

메타프로그래밍의 숨겨진 비용

메타프로그래밍은 아주 멋진 단어처럼 들리지만, 과연 좋을까?

유용할 수 있지만 많은 사람들이 메타프로그래밍을 사용하는 데 약간의 비용이 든다는 사실을 깨닫지 못합니다.

우리는 같은 페이지에 있습니다...

메타프로그래밍이란 정확히?

저는 메타프로그래밍을 다음과 같은 방법을 사용하는 것으로 정의합니다.

  • 코드 구조 변경(예: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

결론

메타프로그래밍의 모든 것이 나쁜 것은 아닙니다. 적절한 상황에서 코드를 보다 유연하게 만드는 데 유용할 수 있습니다.

더 나은 결정을 내릴 수 있도록 추가 비용을 알고 계십시오.

이 게시물을 공유하는 것을 잊지 마세요. 유용하셨다면 🙂