ひとり勉強会

ひとり楽しく勉強会

最適化の流れ

だいぶ間が空いてしまったので、まずは流れのおさらいから。

  • yarvcore_eval_parsed @ yarvcore.c
    • th_compile_from_node @ yarvcore.c
      • yarv_iseq_new_with_opt @ iseq.c
        • iseq_compile @ compile.c
          • iseq_compile_each @ compile.c
          • iseq_setup @ compile.c

第3回〜第9回まで読んだのがiseq_compile_each、Ruby構文木YARVの命令のリストに変換する関数でした。その次に来るのが、iseq_setup。この関数の前半は、生成された命令リストの最適化を行っています。

static int
iseq_setup(yarv_iseq_t *iseq, LINK_ANCHOR *anchor)
{
  iseq_optimize(iseq, anchor);
  if (iseq->compile_data->option->instructions_unification) {
	iseq_insns_unification(iseq, anchor);
  }
  if (iseq->compile_data->option->stack_caching) {
	set_sequence_stackcaching(iseq, anchor);
  }
  ...続く...

コメントやチェックコードなどは勝手にわたしが除いて転載しております。大筋はこんな感じということで。さて、最適化ステップは3段階に分かれているようです。最初のiseq_optimizeはさらに内部で3つに分かれています。

static int
iseq_optimize(yarv_iseq_t *iseq, LINK_ANCHOR *anchor)
{
  LINK_ELEMENT *list;
  const int do_peephole =
    iseq->compile_data->option->peephole_optimization;
  const int do_si =
    iseq->compile_data->option->specialized_instruction;
  const int do_ou =
    iseq->compile_data->option->operands_unification;
  list = FIRST_ELEMENT(anchor);
    
  while (list) {
    if (list->type == ISEQ_ELEMENT_INSN) {
      if (do_peephole) {
        iseq_peephole_optimize(iseq, list);
      }
      if (do_si) {
        iseq_specialized_instruction(iseq, (INSN *)list);
      }
      if (do_ou) {
        insn_operands_unification((INSN *)list);
      }
    }
    list = list->next;
  }
  return COMPILE_OK;
}

命令1個1個をwhileループで回って、各命令に対して最適化をほどこしています。
まとめると、この段階で行われる最適化は5つ。

最適化 関数名
覗き穴最適化 iseq_peephole_optimize
特化命令 iseq_specialized_instruction
オペランド融合 insn_operands_unification
命令融合 iseq_insns_unification
スタックキャッシング set_sequence_stackcaching

それぞれ順番に見ていきます。