2016/09/10


「みんなのGo言語」が本日発売となりました。

みんなのGo言語【現場で使える実践テクニック】 みんなのGo言語【現場で使える実践テクニック】
松木雅幸, mattn, 藤原俊一郎, 中島大一, 牧 大輔, 鈴木健太
技術評論社 / ¥ 2,138 (2016-09-09)
 
発送可能時間:在庫あり。

現在、プログラミング言語のカテゴリで1位の様です。めでたい。

みんなのGo言語

僕も執筆に参加させて頂いたので興味ある方はぜひ読んで欲しいです。

さて最近の僕がGo言語で何を書いていたかというと
GitHub - mattn/go-slim: Slim Template Engine for golang

Rslim template engine for golang

https://github.com/mattn/go-slim

slim template の golang 版です。忙しくてまだ完成はしていないんですが、ちょっとした物であれば使えるレベルまで出来ていると思っています。haml を golang で実装している物はいくらかあったのですが、僕は haml よりも slim が好きなので何も迷わず実装しました。

doctype 5
html lang="ja"
  head
    meta charset="UTF-8"
    title
  body
    ul
    - for x in foo
      li = x

こんな slim のテンプレートを用意して

tmpl, err := slim.ParseFile("template.slim")
if err != nil {
    t.Fatal(err)
}
err = tmpl.Execute(os.Stdout, slim.Values{
    "foo": []string{"foo""bar""baz"},
})

テンプレートを読み込んで値を渡しながら Execute を呼び出します。すると

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8"/>
    <title>
    </title>
  </head>
  <body>
    <ul>
      <li>foo</li>
      <li>bar</li>
      <li>baz</li>
    </ul>
  </body>
</html>

綺麗な HTML が生成されます。slim 内での式にも対応していて for in 文だけでなく

= foo[0] + foo[1].Bar[0]

式も書けるし以下の様なコードに対して

:= make(map[string]string)
m["baz"= "Baz!"

type Baz struct {
    Fuga string
}
var buf bytes.Buffer
err = tmpl.Execute(&buf, Values{
    "foo"struct {
        Baz []Baz
    }{
        Baz: []Baz{
            {Fuga: "hello"},
            {Fuga: "world"},
            {Fuga: "golang"},
        },
    },
    "bar": m,
})

構造体や map を参照してテンプレートから扱う事も出来ます。

= bar.baz
- for x in foo.Baz
    p = x.Fuga

ruby を内部で実行する代わりに slim 用の専用のインタプリタ言語を作っています。はじめは anko を埋め込もうと思ったのですが高機能すぎるので文法の小さい物にしています。割と綺麗に書けました。おそらくですが他のテンプレートエンジンに使いまわしたり、「プログラミング言語の実装ちょっと興味ある」という方には良い教材になると思います。vm というフォルダだけ持っていけば他のプロダクトにも埋め込められるはずです。

面白いところでは for in 文の右辺に配列だけでなく channel を指定できる様にしてあります。

ch := make(chan string)
go func() {
    for _, a := range []string{"foo""bar""baz"} {
        ch <- a
    }
    close(ch)
}()

var buf bytes.Buffer
err = tmpl.Execute(&buf, Values{
    "foo": ch,
})
こんな風に chan をテンプレートに渡して
ul
  - for x in foo
    li = x
slim から呼び出すと
<ul>
  <li>foo</li>
  <li>bar</li>
  <li>baz</li>
</ul>

ループを回してくれます。実用的かどうかなんて関係ないです。面白ければいいんです。

まだやりかけというだけあって slim から使える関数は trim, to_upper, to_lower, split 程度しかありませんが、適当に時間を見つけて作って行きたいと思います。


2016/08/17


SSEを使ってHTMLエスケープを高速化してみた - k0kubun's blog

高速なHTMLエスケープをするライブラリを作った ある日HTMLエスケープを速くしたくなって、hescapeというライブラリを作った。 github.com とにかく速いHTMLエスケープがしたい R...

http://k0kubun.hatenablog.com/entry/hescape

以前、moznion 氏の petit-html-escaper を勝手に高速化した時の話。

GitHub - moznion/petit-html-escaper: A simple and small escaper for HTML with SSE4.2 function

Author moznion ( moznion@gmail.com ) mattn License The MIT License (MIT) Copyright © 2015 moznion, h...

https://github.com/moznion/petit-html-escaper

速くしたと言っても SSE 部分ではない。ベンチマークの比較元である非 SSE な実装の方。元のベンチマーク結果が README.md に残っている。

petit-html-escaper: 3.935205 [sec]
simple-impl: 5.634651 [sec]

petit-html-escaper is faster 143.185715% than simple implementation

以下がその時の比較元のコード。思いつくままに実装された単純なコードです。

static void simple_escape_html(char *dst, const char *input, size_t input_size) {
  for (int i = 0; i < input_size; i++) {
    const char c = *(input++);
    switch (c) {
      case '&':
        memcpy(dst, "&amp;"5);
        dst += 5;
        break;
      case '>':
        memcpy(dst, "&gt;"4);
        dst += 4;
        break;
      case '<':
        memcpy(dst, "&lt;"4);
        dst += 4;
        break;
      case '"':
        memcpy(dst, "&quot;"6);
        dst += 6;
        break;
      case '\'':
        memcpy(dst, "&#39;"5);
        dst += 5;
        break;
      case '`':
        // For IE. IE interprets back-quote as valid quoting characters
        // ref: https://rt.cpan.org/Public/Bug/Display.html?id=84971
        memcpy(dst, "&#96;"5);
        dst += 5;
        break;
      case '{':
        // For javascript templates (e.g. AngularJS and such javascript frameworks)
        // ref: https://github.com/angular/angular.js/issues/5601
        memcpy(dst, "&#123;"6);
        dst += 6;
        break;
      case '}':
        // For javascript templates (e.g. AngularJS and such javascript frameworks)
        // ref: https://github.com/angular/angular.js/issues/5601
        memcpy(dst, "&#125;"6);
        dst += 6;
        break;
      default:
        memcpy(dst, &c, 1);
        dst += 1;
    }
  }
  *dst++ = *"\0";
}

ソースも見渡しが良いし悪いコードではないと思いましたが、SSE だから速くなるという言葉にカッとして勢いだけでこの実装を直してみました。そのコードが以下。

static void simple_escape_html(char *dst, const char *input, size_t input_size) {
  const char *ptr = input, *end = input + input_size;
  const static int pp[UCHAR_MAX+1] = {
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,4,0,0,0,1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,2,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,8,0,0
    /* following zero(s) */
  };
  const static char* dd[] = {NULL"&amp;""&gt;""&lt;""&quot;""&#39;""&#96;""&#123;""&#125;"};
  const static int dl[] = {054465566};
#define _ESC_AND_COPY(d,s,n) { memcpy(d,s,n); d += n; }
  while (ptr < end) {
    unsigned char c = *ptr++;
    int i = pp[c];
    if (i == 0) *dst++ = c;
    else _ESC_AND_COPY(dst, dd[i], dl[i]);
  }
#undef _ESC_AND_COPY
  *dst++ = 0;
}

memcpy を相手にしても結果は変わらないので、処理分岐を相手にした。switch 文は遅くなるので文字の ASCII コードをインデックスに使い、置換すべき文字列とオフセットを得る処理です。

ベンチマークがどの様に変わったかというと。。。

petit-html-escaper: 4.954000 [sec]
simple-impl: 4.323000 [sec]
petit-html-escaper is faster 87.262817% than simple implementation

なんと非 SSE の方が速くなってしまいました。もちろん入力データによっては SSE を使って1命令で処理できるバイト数を増やす事は出来るのですが、switch 文や if 文を削るだけでも十分 SSE と戦えるという結果を出せました。現在このコードは moznion 氏の petit-html-escaper の比較元コードとしてマージされています。一部 macOSX で使われている CPU アーキテクチャでは若干 simple_escape_html の方が遅くなるケースがある様ですが、僕が調べた限り Windows/Linux では同程度か、このコードの方が速いという結果が得られました。

SSE に頼るのも良いけど、カリカリチューニングでもまだまだやれるんですよ。皆さん。


2016/08/08


Gopher

僕がプログラミング言語「Go言語」を知り、使い始めてからそろそろ7年目に入ろうとしています。

当初 Google が作っているという鳴り物があった為、色々なメディアに取り上げられ色々な方がブログ等でGo言語を紹介し、色々な意見でGo言語が語られました。大抵の場合、プログラミング言語とは始めはチヤホヤと取り出され、落ち着いてからが本当の人気を表すという傾向にあります。皆さんもそう思っていたかもしれませんし、僕もそう思っていたと思います。

僕がGo言語を触りだした頃、まだ色々と足りない部分がありました。Linux で動いている多くの機能が Windows では未実装になっていました。しかしそんなGo言語であっても高速なビルドと実行速度で僕の好奇心を揺さぶるには十分な物でした。


その後、僕はGo言語にパッチを送る様になりました。その内幾らかはマージされました。現時点ではコアのリポジトリで79個のコミットがマージされていますが、マージされると今でも嬉しくなります。毎日朝にリポジトリを最新にしてビルドし、安定動作しているのを確認する日々が続きました。

気付いた頃には僕は新規で何かをプログラミングする場合には必ずGo言語で実装を始める様になっていました。

僕のこのサイトにも多くのGo言語の情報が書かれています。今では業務のプロダクションの一部にもGo言語を使っていますし、誰にも見せない様な個人のツールにもGo言語を使っています。

Go言語は僕にとって既に無くてはならない存在になってしまいました。

またGo言語にパッチを送った事で良い経験が出来ました。雲の上の人だと思っていた Rob Pike 氏や Russ Cox 氏、Brad Fitzpatrick 氏たちが僕の書いたパッチのレビューをしてくれたり、メールにコメントしてくれた時の事は今でも忘れられないくらい興奮したのを覚えています。そして彼らの適切すぎるソースコードレビューに感動したのも覚えています。正直いって彼らには敵いません。Go言語の開発者メーリングリストを見ていると自分の技術力との差に愕然とする事も多々あります。

そして次第に僕の周りでもGo言語が使われ始め、Twitter のタイムラインでもGo言語に関する発言が増えてきました。海外から比べると1年遅れていると言われていた日本企業でのGo言語導入も今では多くの有名企業がGo言語を採用しプロダクションでも利用される様になって来ました。僕がGo言語を作った訳ではないし大したパッチを送った訳では無いですが、昔を知っているだけにとても感慨深い思いです。

そろそろGo言語も一般のユーザに認知されてきたのだと思っています。そしてGo言語を操る有名なエンジニアも多く現れました。

そしてその彼らが1冊の本を書く事になりました。

みんなのGo言語【現場で使える実践テクニック】 みんなのGo言語【現場で使える実践テクニック】
松木雅幸, mattn, 藤原俊一郎, 中島大一, 牧 大輔, 鈴木健太
技術評論社 / ¥ 2,138 (2016-09-09)
 
発送可能時間:在庫あり。

技術評論社様から依頼を頂いた際には快諾させて頂きました。その際、執筆メンバの紹介をお願いされ、何名かをご紹介させて頂きました。結果見渡すと、Go言語界隈ではスターエンジニアと言ってもいい位に豪華なメンバが執筆に参加して頂ける事になりました。なかなかこの面子は集まらないと思います。

songmu さん、fujiwara さん、deeeetさん、lestrrat さん、suzuken さん、そして僕。計6が執筆メンバです。

彼らが元々Go言語の使い手では無かった事をご存じの方も多いと思います。Go言語の良いところも良くないところも知っているこのGo言語エンジニア達が現場で得たノウハウやテクニックを惜しげもなく書き連ねて下さっています。

僕も現場で得た開発テクニックやハマリ所などを書かせて頂きました。このブログでも出していないネタばかりです。他の執筆陣の方の記事も含め、Go言語を触った事がある方であればきっと面白いと思いながら読んで頂けると思っています。

「みんなのGo言語」というタイトルも皆で考え決めました。「みんGo」と呼んで下さい。そしてぜひお手に取って読んでみて下さい。

この本でGo言語を好きになってくれる方が少しでも増えてくれる事を、そしてこの本で誰かが抱えていた問題が少しでも解決する事を、一執筆者として願っています。