Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Elixir におけるリスト反転処理の実装を覗いてみよう

Elixir におけるリスト反転処理の実装を覗いてみよう

2019/07/01 【学生限定】Akatsuki Geek Live Vol.3【LT会:7/1(月)】 にて発表した資料です。

Shohei Kajihara

July 01, 2019
Tweet

Other Decks in Technology

Transcript

  1. Elixir
    における
    Elixir
    における
    リスト反転処理の実装を
    リスト反転処理の実装を
    覗いてみよう
    覗いてみよう
    2019/07/01 Akatsuki Geek Live Vol.3
    アカツキ サーバーエンジニア 梶原星平 (@s-capybara)

    View full-size slide

  2. ⾃⼰紹介
    ⾃⼰紹介
    2017
    年 アカツキに新卒⼊社(3
    年⽬)
    Elixir
    でゲームサーバーを書いています

    View full-size slide

  3. 今⽇の⽬的
    今⽇の⽬的
    Elixir
    の紹介
    プログラミング⾔語の中⾝の学び⽅

    View full-size slide

  4. Elixir
    とは
    Elixir
    とは
    プログラミング⾔語
    関数型
    動的型付け
    不変性 (immutability)
    Erlang VM
    上で動作
    並⾏性 (concurrency)
    耐障害性 (fault-tolerance)
    Erlang
    の関数を呼び出せる

    View full-size slide

  5. 不変性
    不変性
    予想外の値変更による実装ミスが起きにくくなる
    defmodule Sample do
    def foo() do
    bar = 1
    if :rand.uniform() > 0.5 do
    bar = 2
    IO.puts("Modified bar to 2!")
    end
    bar
    end
    end
    iex(1)> Sample.foo()
    Modified bar to 2!
    1

    View full-size slide

  6. List
    List
    iex(2)> numbers = [1, 2, 3]
    [1, 2, 3]

    View full-size slide

  7. List
    の基本操作
    List
    の基本操作
    Lisp
    と同じく Car, Cdr, Cons
    処理ができる
    Car:
    リストの先頭を取り出す
    Cdr:
    リストの残り部分を取り出す
    Cons:
    リストの先頭に要素を⾜す
    iex(3)> [head | _] = numbers
    iex(2)> head
    1
    iex(4)> [_ | tail] = numbers
    iex(4)> tail
    [2, 3]
    iex(5)> [0 | numbers]
    [0, 1, 2, 3]

    View full-size slide

  8. Enum.reverse
    Enum.reverse
    リスト(など)を反転する関数
    標準の関数が⾼速なので、
    ⾃前実装は NG
    と⾔われている
    iex(6)> Enum.reverse(numbers)
    [3, 2, 1]

    View full-size slide

  9. 気になるところ
    気になるところ
    1.
    リストはどういうデータ構造なのか?
    2.
    標準の関数はなぜ⾼速なのか?

    View full-size slide

  10. 解決策1:
    本を読む
    解決策1:
    本を読む
    The Beam Book
    ( )
    Erlang VM
    の中⾝が紹介されている
    (プロセス、スケジューラ、型システム、...

    無料で読める(ただし英語)
    書籍は著者視点による説明
    説明の順序にストーリーがあるので⼊⾨に最適
    公式ドキュメントなどより先に書籍にあたるのが
    おすすめ
    https://github.com/happi/theBeamBook

    View full-size slide

  11. Erlang VM
    の型システム
    Erlang VM
    の型システム
    64 (
    または32) bits
    で 1 word
    末尾 2 bits (
    あるいはもっと)
    で型を表現
    リストは「アドレス + 01
    」という形の 1 word
    00 Header (on heap) CP (on stack)
    01 List (cons)
    10 boxed
    00 11 Pid

    View full-size slide

  12. 単⽅向リストの実体
    単⽅向リストの実体
    リストはポインタであり、
    参照先の隣のアドレスがまたポインタになっている

    View full-size slide

  13. 解決策2:
    ソースコードを
    解決策2:
    ソースコードを
    読む
    読む
    Elixir:
    Erlang:
    https://github.com/elixir-lang/elixir
    https://github.com/erlang/otp

    View full-size slide

  14. Enum.reverse/1
    の実装
    Enum.reverse/1
    の実装
    簡単なやつだけ Elixir
    レイヤーで定義
    結局、Erlang
    のコードを呼び出している
    @spec reverse(t) :: list
    def reverse([]), do: []
    def reverse([_] = list), do: list
    def reverse([element1, element2]),
    do: [element2, element1]
    def reverse([element1, element2 | rest]),
    do: :lists.reverse(rest, [element2, element1])

    View full-size slide

  15. lists:reverse/2
    の実装
    lists:reverse/2
    の実装
    結局、C
    ⾔語実装の Built in Funciton (BIF)
    を呼び出し
    ている
    %% Shadowed by erl_bif_types: lists:reverse/2
    -spec reverse(List1, Tail) -> List2 when
    List1 :: [T],
    Tail :: term(),
    List2 :: [T],
    T :: term().
    reverse(_, _) ->
    erlang:nif_error(undef).

    View full-size slide

  16. lists_reverse_2
    lists_reverse_2
    erts/emulator/beam/erl_bif_lists.c
    というそれっぽい名前のファイルに
    lists_reverse_2
    というそれっぽい名前の関数を発⾒

    View full-size slide

  17. lists_reverse_2 (
    抜粋)
    lists_reverse_2 (
    抜粋)
    CAR, CDR, CONS
    をやっているらしい
    (説明は後で図⽰)
    while (alloc_top < alloc_end) {
    //
    リストをポインタ化する
    Eterm *pair = list_val(list);
    //
    確保したヒープ領域の先頭を
    [
    リストの先頭
    |
    反転済みリスト
    ]

    tail = CONS(alloc_top, CAR(pair), tail);
    //
    続きは
    Cdr
    から
    list = CDR(pair);
    ASSERT(is_list(list) || is_nil(list));
    // 2 words
    分ヒープ領域のへのポインタを進める
    alloc_top += 2;
    }

    View full-size slide

  18. リスト操作のマクロ
    リスト操作のマクロ
    CAR, CDR, CONS
    の定義(説明は割愛)
    #define TAG_PRIMARY_LIST 0x1
    // 01
    の⾜し算でポインタをリストに、引き算でリストをポインタに
    #define _unchecked_make_list(x) ((Uint)(x) + TAG_PRIMARY_LIST)
    #define _unchecked_list_val(x) ((Eterm*) ((x) - TAG_PRIMARY_LIST)
    //
    ポインタの指すアドレスに値を仕舞ったのちリストにする
    #define CONS(hp, car, cdr) \
    (CAR(hp)=(car), CDR(hp)=(cdr), make_list(hp))
    //
    ポインタの指すアドレスが
    CAR,
    その隣が
    CDR
    #define CAR(x) ((x)[0])
    #define CDR(x) ((x)[1])

    View full-size slide

  19. lists_reverse_2
    の概観
    lists_reverse_2
    の概観
    (1/4)
    (1/4)

    View full-size slide

  20. lists_reverse_2
    の概観
    lists_reverse_2
    の概観
    (2/4)
    (2/4)

    View full-size slide

  21. lists_reverse_2
    の概観
    lists_reverse_2
    の概観
    (3/4)
    (3/4)

    View full-size slide

  22. lists_reverse_2
    の概観
    lists_reverse_2
    の概観
    (4/4)
    (4/4)

    View full-size slide

  23. 標準の関数は
    標準の関数は
    なぜ速いのか?
    なぜ速いのか?
    そもそも C
    ⾔語なので速い
    不変性を保つ必要がない
    result
    は C
    ⾔語上のローカル変数

    View full-size slide

  24. 結局、理解できて
    結局、理解できて
    何が嬉しいのか?
    何が嬉しいのか?
    すぐに役⽴つわけではないが...
    「この⾔語ではどういう操作のコストが⾼いの
    か?」など理解したうえでコードが書けるようにな
    る(かも)
    トラブルシューティングに強くなる(かも)

    View full-size slide

  25. まとめ
    まとめ
    まず本を読み、その後ソースコードを読もう
    すぐには役に⽴たないが、中⾝を知っておいた⽅が
    安⼼

    View full-size slide