Subscribed unsubscribe Subscribe Subscribe

Islands in the byte stream

Technical notes by a software engineer

ObjectBox vs Orma

greenDAOの開発元であるgreenbotが新しいNoSQL ORMのβ版をアナウンスしたようです。

greenrobot.org

いわく、マルチスレッドで使いやすく、バイトコード操作のようなマジカルな方法はとらず、マイグレーションを自動で行えてしかも爆速だと。これらの特徴はOrmaも標榜しているので、気になりますね。

ORMにとってのパフォーマンスは一番重要というわけではないものの、それなりには速くないと困るのは確か。というわけで、まずは軽くベンチマークをとってみました。コードベースはOrmaのリポジトリ内のベンチマークコードに追加した形です。

結論からいうと、読み込みはわりと速いです。

  • 書き込みはOrmaとほぼおなじ水準
    • 何度か繰り返すとOrmaより速いこともあります
    • なおOrmaは手書きの素朴なコードよりも高速に見えますが、手書きのコードでの最適化をサボっているためです
    • Realmが遅いのはよくわかりません。普通にtransaction作ってinsertしているだけだと思うんですが
  • 読み込みはOrmaよりも高速なことが多い
    • 何度か繰り返してみるとだいたい10~30%高速です。これはすごい
    • Realmがかなり遅いのは、すべてのフィールドにアクセスしているため。Realmはlazy loadを行うので、アクセスするフィールドが少ない場合には有利ですが…

具体的なコードは次のPRで確認できます (with Orma v4.1.1)。

Benchmark with ObjectBox by gfx · Pull Request #371 · gfx/Android-Orma · GitHub

詳細:

f:id:gfx:20170125220218p:plain

Rails + React + SSRのもとでレスポンシブにするにあたって現状とっている方法

SSRと絡めようとすると難しくて、いろいろ試行錯誤しました。いまはこういう方向でやろうとしていて、そこそこメンテナンス可能に書けそうだなという手応えがあります。

三行で

  • CSSのメディアクエリのみを使い、Railsでテンプレートエンジンを使うにせよReactでSSRするにせよReactでCSRするにせよ同じ方法を使う
  • PCのみ表示させる場合は .showPcOnly クラスを、スマホでのみ表示させる場合は.showSmartphoneOnlyクラスをつけたdiv囲む
  • たまに .showPcOnly などとは別に width: 100% などを指定しないといけないが、それは別途クラスを定義して与える

ヘルパーSCSSクラス

こんな感じのヘルパーを用意しておく。

$smartphoneBreakpoint: 767px;

@mixin smartphone {
  @media screen and (max-width: $smartphoneBreakpoint) {
    @content;
  }
}

@mixin pc {
  @media screen and (min-width: $smartphoneBreakpoint + 1) {
    @content;
  }
}

@include smartphone {
  .showPcOnly {
    display: none;
  }
}

@include pc {
  .showSmartphoneOnly {
    display: none;
  }
}

ヘルパーJSX components

こんな感じのJSX componentsを定義しておく。

import React from 'react';
import classNames from 'classnames';

export class SmartphoneOnly extends React.Component {
  static propTypes() {
    return {
      children: React.PropTypes.any.isRequired,
      className: React.PropTypes.string,
    };
  }

  render() {
    return (<div className={classNames(this.props.className, "showSmartphoneOnly")}>
          {this.props.children}
      </div>);
  }
}

export class PcOnly extends React.Component {
  static propTypes() {
    return {
      children: React.PropTypes.any.isRequired,
      className: React.PropTypes.string,
    };
  }

  render() {
    return (<div className={classNames(this.props.className, "showPcOnly")}>
            {this.props.children}
        </div>);
  }
}

試したこと

  • .showPcOnly クラスを@extendする→❌
    • そのクラスにdisplay: flexをつけたい場合に衝突してうまくいかない
  • react-responsive ライブラリをつかう→❌
    • サーバーサイドでJavaScriptのmedia query APIを使えないのでダメ
    • JS media queryのための情報をクライアントから送る(e.g. width: 1024)こともできるが所詮シミュレーションでしかないし、キャッシュとの相性も悪い
  • CSSでクラスごとに地道にがんばる→❌
    • DOM構造をみて(=hamlやJSXをみて)どうスイッチされるかを把握できないのでつらい
    • DOM構造に直接.showPcOnly<ShowPcOnly>で書くのがわかりやすくていい

Ruby extのためのVisual Studio Codeのclang extension用設定を書き出す

Visual Studio Codeの C/C++ Clang extensionのためのいくらかの設定をすると、Ruby extensionを開発するときに補完が効くようになり、開発が楽になります。

C/C++ Clang - Visual Studio Marketplace

ところでRuby extensionのCFLAGSなどの設定はわりと難しくて、実際のMakefileを読み込んでmakeを実行するのがてっとりばやいです。

というわけで、extconfを起動してMakefileをつくり、そこから必要なマクロを抜き出して .vscode/settings.json に書きだすためのタスクをつくってみました。

# additional Rake task for "ext/foo"
namespace :vscode do
  desc "Setup Visual Studio Code C/C++ settings (.vscode/setting.json)"
  task :setup do
    require "json"
    require "shellwords"

    FileUtils.mkdir_p("tmp/vscode")

    cflags = Dir.chdir("tmp/vscode") do
      # to create Makefile
      system("ruby", File.join(__dir__, "ext/foo/extconf.rb"))

      # Makefile macro to get CFLAGS for editors / IDEs
      task = "\n\nshow-cflags:\n\t$(ECHO) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS)\n\n"
      IO.write("Makefile", task, mode: "a")

      Shellwords.split(`make show-cflags`)
    end

    settings = {
      "clang.cflags" => cflags,
      "clang.completion.triggerChars" => [
        ".", ":", ">", "_"
      ],
    }

    FileUtils.mkdir_p(".vscode")
    IO.write(".vscode/settings.json", JSON.pretty_generate(settings))
  end
end

こんな感じで補完が出ます。

f:id:gfx:20170115121534p:plain

しかし、補完が出るのに数秒かかるので思ったほど快適ではない… 😔


追記: Microsoft公式のvscode.cpptoolsだと補完が快適でした。それ用のtaskはこんな感じです。

namespace :vscode do
  desc "Setup Visual Studio Code C/C++ settings (.vscode/setting.json)"
  task :setup do
    require "json"
    require "shellwords"

    FileUtils.mkdir_p("tmp/vscode")

    cflags = Dir.chdir("tmp/vscode") do
      # to create Makefile
      system("ruby", *Dir.glob(File.join(__dir__, "ext/**/extconf.rb")))

      # Makefile macro to get CFLAGS for editors / IDEs
      task = "\n\nshow-cflags:\n\t$(ECHO) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS)\n\n"
      IO.write("Makefile", task, mode: "a")

      Shellwords.split(`make show-cflags`)
    end

    include_path = cflags.select do |flag|
      flag.start_with?('-I')
    end.map do |flag|
      flag.sub(/\A-I/, '')
    end

    # http://code.visualstudio.com/docs/languages/cpp
    configurations = [
      {
        name: "Mac",
        includePath: include_path,
      },
      {
        name: "Linux",
        includePath: include_path,
      },
      {
        name: "Win32",
        includePath: include_path,
      },
    ]

    FileUtils.mkdir_p(".vscode")
    IO.write(".vscode/c_cpp_properties.json", JSON.pretty_generate(configurations: configurations))
  end
end

Android SDK 25.2.3から付属のsdkmanager(1)でパッケージのインストールをする

Android SDK 25.2.3にsdkmanagerとういコマンドが新しくく追加されていたようです。

これは、CLIでAndroid SDK packageを管理するためのツールのようです。Gradle Android pluginと同様にlicensesファイルに対応しているので、CIなどで扱いやすいですし、なによりandroidコマンドにはなかったパッケージのアップデートがサポートされています。このため、androidコマンドよりは扱いやすく工夫の余地が増えてると考えてよさそうです。

使い方はこんな感じです:

# homebrewだとまだパスが通らないので絶対パスで参照する

# インストール済みパッケージと利用可能なパッケージを出力する
$ANDROID_HOME/tools/bin/sdkmanager --list

# platform-toolsと旧名extra-android-m2repositoryをインストールする
$ANDROID_HOME/tools/bin/sdkmanager "platform-tools" "extras;android;m2repository"

# インストール済みパッケージをアップデートする
$ANDROID_HOME/tools/bin/sdkmanager --update

Circle CIでの導入はこんな感じです:

https://github.com/gfx/Android-Orma/pull/365

Circle CIにインストール済みのAndroid SDKは25.2.3より古いため、まずandroidコマンドでSDK toolsのアップデートをしないとsdkmanagerが使えません。このためCircle CIのAndroid SDKがアップデートされるまではandroidコマンドから脱却はできません。残念。

Ormaの2016年振り返りと2017年の展望

https://github.com/gfx/Android-Orma

2015年末に開発をはじめたOrmaも、この一年でだいぶバージョンが進んで、年明け時点で v0.20.0だったところが、年末時点でv4.0.2でした。v0.20.0...v4.0.2で21,768 additions and 6,328 deletionsということで、結構書いたなあという感じです。そのおかげでv1.0.0の頃とくらべると格段に安定していますし、機能も増えてます。

採用事例や紹介エントリも増えてきました。

GitHub search などをみるといくつか実際のユースケースをみることもできます。

2017年の展望

2016年に比べるとやることは少なくなってきましたが、まだいくつか重要なタスクが残っています。

  • マイグレーションで使っているSQLiteパーサの変更
    • 現行のANTLR4ベースのパーサは、メモリの非常に少ない端末でたまにエラーになるという問題があります
    • 現在JavaCCでSQLiteパーサを書き直しており、1~2ヶ月後くらいには実験的に使えるようにしたいところです
    • 以前の検証によるとJavaCCによるパーサはANTLR4によるパーサよりも数倍高速なので、低スペック端末を切るとしてもやる価値はあるという判断です
  • has-many / many-to-many のよりよいサポート
    • has-many関係は宣言こそ自然にできるものの、INSERTやSELECTがわりと難しいのでもうちょっとなんとかしたい
  • ドキュメント
    • 特に英語圏でのマーケティングのため

今後ともよろしくお願いします。

PR: 3月のDroidKaigi 2017ではOrmaを含めた最近のAndroidのORM事情について話す予定です!!

ライブラリのバージョニングのしかた

セマンティックバージョニングは守るとして、だいたいこんなポリシーでやってます。

  • 0.0.1 - proof of concept
  • 0.1.0 - とりあえずリリースしてプロダクションに組み込んでみる
  • 1.0.0 - プロダクションに組み込んだ
  • 2.0.0 - セマンティックバージョニングに従うので、メジャーバージョンアップは機能ではなく単にAPI互換性を失うという印

あとは、alpha, beta, rcなどを接尾詞としてつけることもあります。

  • *-alpha - 開発中
  • *-beta - 安定してきた
  • *-rc - release candidate. プロダクションに組み込んでもOK

Suffix ArrayをRustで実装した

suffix arrayを一番簡単なアルゴリズムで実装する - アルゴリズム学習(その6) - $shibayu36->blog; を読んで、ちょうど自分も何らかの形で全文検索を一部実装してみようと思っていたのでRustで真似してみました。

Rustを選んだ理由は、以下の理由からです。

  • 実際に全文検索を実装するのに耐えうるパフォーマンスであること
  • パッケージマネージャなどのエコシステムが完備されていること

Rustについてはそれほど詳しくはないのですが、GCや例外がないとのこと。であればパフォーマンスチューニングがC言語並にやりやすい可能性がありますし、一度真面目に勉強してみたいと思っていました。Goと異なり、ジェネリクスがあるのも魅力的です。

というわけでコードこんな感じになりました:

pub fn make_suffix_array(s: &str) -> Vec<i64> {
  use std::collections::HashMap;

  let mut map = HashMap::new();

  for i in 0..s.len() {
    map.insert(&s[i..s.len()], i as i64);
  }

  use std::iter::FromIterator;

  let mut suffixes = Vec::from_iter(map.keys());
  suffixes.sort();

  return suffixes.iter().map(|suffix| *map.get(*suffix).unwrap()).collect();
}

#[test]
fn test_make_suffix_array() {
  let a = make_suffix_array("banana");
  assert_eq!(a, vec![5, 3, 1, 0, 4, 2]);
}

Rustに慣れていないので、この数十行を書くのも大変でしたがとりあえず動きます。エディタはIntelliJ Rustを使いました。あまり賢くはないもののコード補完があり、普段使っているRubyMineやAndroid Studioに近い感覚でコードを書けるのでわりと好印象です。