覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

Java 8 での java.nio.Files#existsは遅いのか?

Java 7で導入されたNew I/O API(NIO2)。その時に新規クラスとして、java.nio.Files などが増えました。

このクラスで、java.io.File のコードを置き換えることなどが可能です。

一般的には、今はjava.io.Fileクラスではなく、NIO2の機能(java.nio.Filesクラス)を使うのが一般的だと思います。

例えばファイルの存在チェックをする場合は、
旧来は、java.io.File#exists と やっていたものを java.nio.Files#exists で置き換え可能です。

が、Java 8 環境だと java.nio.Files#exists は、対象パスが存在しない場合にパフォーマンスが出ない というバグがあるようです。


JMHでベンチマークとって確認してみます。

ベンチマーク取得用のコードは以下の通りです。
java.io.File#existsjava.nio.Files#exists を、対象ファイルが存在する場合と存在しない場合に分けて実行して、スループット計測しています)

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;

@Warmup(iterations=10)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@BenchmarkMode(Mode.Throughput)
@Fork(1)
public class FileExistsBenchmark {

    /** 存在するファイルのパス */
    private static final Path existsFilePath = Paths.get("sample1.txt");

    /** 存在しないファイルのパス */
    private static final Path notExistsFilePath = Paths.get("sample999.txt");

    @Benchmark
    public void Files_exists_found() {
        // 存在するファイルに対する Files#exists
        Files.exists(existsFilePath);
    }

    @Benchmark
    public void Files_exists_not_found() {
        // 存在しないファイルに対する Files#exists
        Files.exists(notExistsFilePath);
    }

    @Benchmark
    public void File_exists_found() {
        // 存在するファイルに対する File#exists
        existsFilePath.toFile().exists();
    }

    @Benchmark
    public void File_exists_not_found() {
        // 存在しないファイルに対する File#exists
        notExistsFilePath.toFile().exists();
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(FileExistsBenchmark.class.getSimpleName())
                .build();
        new Runner(opt).run();
    }

}


macOS + Java 8 でのベンチマーク

macOS + Java 8 でのベンチマークは以下の通りとなりました

# JMH version: 1.21
# VM version: JDK 1.8.0_181, Java HotSpot(TM) 64-Bit Server VM, 25.181-b13
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/bin/java
# VM options: -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=55029:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8
# Warmup: 10 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time

(中略)

Benchmark                                    Mode  Cnt    Score     Error   Units
FileExistsBenchmark.File_exists_found       thrpt    5  377.495 ± 163.927  ops/ms
FileExistsBenchmark.File_exists_not_found   thrpt    5  484.295 ±  74.178  ops/ms
FileExistsBenchmark.Files_exists_found      thrpt    5  871.356 ±  46.111  ops/ms
FileExistsBenchmark.Files_exists_not_found  thrpt    5   91.218 ±  56.958  ops/ms

結果は上から順に以下の実行パターンのスループットを表しています(以降の記載でも同様です)

  • java.io.File#exists + 対象のファイルが存在する
  • java.io.File#exists + 対象のファイルが存在しない
  • java.nio.Files#exists + 対象のファイルが存在する
  • java.nio.Files#exists + 対象のファイルが存在しない


結果から一目瞭然ですが、「java.nio.Files#exists + 対象のファイルが存在しない」のパターンの性能が出ていません。なんてこったい。


macOS + Java 11 でのベンチマーク

このバグは、Java 9 時点で解消されてるらしいです。それならばJava 11でも問題ないはず!ということで、
macOS + Java 11(AdoptOpenJDK) でのベンチマークもとってみました。結果は以下の通りとなりました。

# JMH version: 1.21
# VM version: JDK 11.0.1, OpenJDK 64-Bit Server VM, 11.0.1+13
# VM invoker: /Library/Java/JavaVirtualMachines/adoptopenjdk-11.0.1.jdk/Contents/Home/bin/java
# VM options: -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=56002:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8
# Warmup: 10 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time

(中略)

Benchmark                                    Mode  Cnt    Score    Error   Units
FileExistsBenchmark.File_exists_found       thrpt    5  396.421 ± 40.107  ops/ms
FileExistsBenchmark.File_exists_not_found   thrpt    5  484.622 ± 20.577  ops/ms
FileExistsBenchmark.Files_exists_found      thrpt    5  880.723 ± 17.791  ops/ms
FileExistsBenchmark.Files_exists_not_found  thrpt    5  907.397 ± 26.587  ops/ms

こちらも結果から一目瞭然ですが、「java.nio.Files#exists + 対象のファイルが存在しない」のパターンの性能がちゃんと出ています。
というか、めっちゃ早くなってるじゃないですか笑


macOSでやったついでにWindows環境でも以下試してみました。
今回、macOSとWindowsで端末環境が異なる(別マシン)なので、各OS間で結果(絶対値)を直接的に比較することはできません。


Windows + Java 8 でのベンチマーク

Windows + Java 8 でのベンチマークは以下の通りとなりました

# JMH version: 1.21
# VM version: JDK 1.8.0_191, Java HotSpot(TM) 64-Bit Server VM, 25.191-b12
# VM invoker: C:\Program Files\Java\jdk1.8.0_191\jre\bin\java.exe
# VM options: -javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.5\lib\idea_rt.jar=51996:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.5\bin -Dfile.encoding=UTF-8
# Warmup: 10 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time

(中略)

Benchmark                                    Mode  Cnt   Score    Error   Units
FileExistsBenchmark.File_exists_found       thrpt    5  41.620 ±  4.666  ops/ms
FileExistsBenchmark.File_exists_not_found   thrpt    5  89.797 ± 15.949  ops/ms
FileExistsBenchmark.Files_exists_found      thrpt    5  14.770 ±  2.707  ops/ms
FileExistsBenchmark.Files_exists_not_found  thrpt    5  18.481 ±  0.886  ops/ms

java.nio.Files#exists の結果が、対象ファイルが存在してても、存在しなくても性能でないという結果になりました。


Windows + Java 11 でのベンチマーク

Windows + Java 11 でのベンチマークは以下の通りとなりました

# JMH version: 1.21
# VM version: JDK 11.0.1, OpenJDK 64-Bit Server VM, 11.0.1+13
# VM invoker: C:\Program Files\Java\OpenJDK11U-jdk_x64_hotspot_11.0.1_13\jdk-11.0.1_13\bin\java.exe
# VM options: -javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.5\lib\idea_rt.jar=49828:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.5\bin -Dfile.encoding=UTF-8
# Warmup: 10 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time

(中略)

Benchmark                                    Mode  Cnt   Score    Error   Units
FileExistsBenchmark.File_exists_found       thrpt    5  44.025 ±  0.957  ops/ms
FileExistsBenchmark.File_exists_not_found   thrpt    5  86.267 ± 16.522  ops/ms
FileExistsBenchmark.Files_exists_found      thrpt    5  14.424 ±  1.863  ops/ms
FileExistsBenchmark.Files_exists_not_found  thrpt    5  19.877 ±  1.545  ops/ms

あれ?・・・速くなってない。java.nio.Files#existsの結果が、全然速くなってない。
端末のせい?なんかミスったか???謎だ・・・。Windowsだとこういう仕様??


まとめ

macOS環境では、Java 8 では、「java.nio.Files#exists + 対象のファイルが存在しない」パターンが性能が出ないことが明確になりました。
というわけで、Java 8 環境では java.io.File#exists を使った方がいい場合もあるかもしれません。

また、Java 11 では、java.nio.Files#existsは性能が出ており、改善されていることが分かりました。

Windows環境の結果は謎だ・・・。



関連エントリ

HomeBrew で AdoptOpenJDK 11をインストールしてみる

macOSにJava 11をインストールしたかったので、HomeBrewでインストールしてみました。

ちなみにインストールしたのは、AdoptOpenJDK です。


Java 11 (AdoptOpenJDK)のインストール

以下実行して AdoptOpenJDKをインストールします

$ brew cask install adoptopenjdk


実行時のログは以下の様な感じでした

Updating Homebrew...
==> Auto-updated Homebrew!
Updated 2 taps (ethereum/ethereum and homebrew/core).
==> Updated Formulae
abyss            easyengine       fx               libbtbb          pig              stone-soup       tomcat-native
azure-cli        exploitdb        grails           mercurial        ppsspp           syncthing        twoping
binaryen         fswatch          jena             percona-server   rke              taskell          up
conan            fwup             kustomize        picard-tools     smimesign        tile38           v8

==> Caveats
More versions are available in the AdoptOpenJDK tap:
  https://github.com/AdoptOpenJDK/homebrew-openjdk

  brew tap adoptopenjdk/openjdk

==> Satisfying dependencies
==> Downloading https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.1%2B13/OpenJDK11U-jdk_x64_
==> Downloading from https://github-production-release-asset-2e65be.s3.amazonaws.com/140419044/a425c980-e495-11e8-82fa-4
######################################################################## 100.0%
==> Verifying SHA-256 checksum for Cask 'adoptopenjdk'.
==> Installing Cask adoptopenjdk
==> Creating Caskroom at /usr/local/Caskroom
==> We'll set permissions properly so we won't need sudo in the future.
Password:
🍺  adoptopenjdk was successfully installed!


/usr/libexec/java_home -V を実行してJava 11が追加されたことを確認します

$ /usr/libexec/java_home -V
Matching Java Virtual Machines (2):
    11.0.1, x86_64: "AdoptOpenJDK 11.0.1"    /Library/Java/JavaVirtualMachines/adoptopenjdk-11.0.1.jdk/Contents/Home
    1.8.0_181, x86_64:  "Java SE 8"  /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home

/Library/Java/JavaVirtualMachines/adoptopenjdk-11.0.1.jdk/Contents/Home

Java 11が追加されてました。


java -version も実行してみました。

$ java -version
openjdk version "11.0.1" 2018-10-16
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.1+13)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.1+13, mixed mode)

Java 11 が利用されるようになってました。


Java 8に戻したい

Java 11インストールしといて何ですが、Java 8に戻したくもなったりするでしょう。

方法はいくつかあるとは思いますが、ここでは、jenv 使って管理します。


とりあえず、以下実行して jenv をインストール

$ brew install jenv


以下を実行してShellの起動スクリプトに追記

$ echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.bash_profile 
$ echo 'eval "$(jenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile


以下を実行してJava 8 と Java 11 をjenvで管理できるようにします

jenv add $(/usr/libexec/java_home -v 1.8)
jenv add $(/usr/libexec/java_home -v 11)


実際に実行すると以下のようになります

$ jenv add $(/usr/libexec/java_home -v 1.8)
oracle64-1.8.0.181 added
1.8.0.181 added
1.8 added
$ jenv add $(/usr/libexec/java_home -v 11)
openjdk64-11.0.1 added
11.0.1 added
11.0 added


jenv versions で管理されているJavaの一覧を確認

$ jenv versions
* system (set by /Users/yuki/.jenv/version)
  1.8
  1.8.0.181
  11.0
  11.0.1
  openjdk64-11.0.1
  oracle64-1.8.0.181


jenv global 1.8 を実行して、Java 8 を指定のバージョンにしてみます

$ jenv global 1.8


jenv versions 実行で 1.8 が選択されていることがわかります。

$ jenv versions
  system
* 1.8 (set by /Users/yuki/.jenv/version)
  1.8.0.181
  11.0
  11.0.1
  openjdk64-11.0.1
  oracle64-1.8.0.181


java -version の結果も Java 8 のバージョンを返すようになりました。

$ java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)


まとめ

めでたく、Java 11 (AdoptOpenJDK)がインストールできましたし、Java 8に戻すこともできるようになりました。


関連エントリ

Go Conference 2018 Autumn に行ってきたよ #gocon

Go Conference 2018 Autumn に参加してきましたので、自分用のメモ。

といっても、今回は実務でゴリゴリにGolang使ってる人やGolangにかなり深い知見のある人の発表で、
正直自分の知らない単語拾いに行った感じでした。


概要

  • 開催場所
    • Google Tokyo office 27F
    • Roppongi Hills Mori Tower, 6-10-1 Roppongi, Minato-ku, Tokyo, Japan
  • 日時
    • 2018/11/25 10:00〜19:00

f:id:nini_y:20181125094724j:plain


Microservices実装ガイド in Go at メルカリ

Speaker: Hidetatsu Yaginuma @yagi5(株式会社メルカリ)

  • メルカリとMicrosrtvices
    • メルカリはもともとPHP製のモノシリックAPIだった
  • どのようにMicroservicesを実装しているか?
    • Protocol Buffers定義 → サービス実装
  • Protocol Buffers 定義
    • 各サービスのインターフェースを定義
    • protoc でProtocol Buffers定義の各プログラム言語のクライアントコードを自動生成している
    • すべてのメンバはprotoファイルを読めばAPIのインターフェースを理解できる
  • サービス実装
    • テンプレートプロジェクトをclone
    • rm -rt .git
    • git init
    • サービスを実装
  • テンプレートプロジェクト
    • echo と呼ばれている
    • 基本的な機能を提供
      • ミドルウェア(認証, Logging, Recover...)
        • Server Chain(Interceotors)の仕組みを利用している
      • Logging
        • opencensus, zap(zapgrpc) を利用している
    • 独立したチームを作ることを助ける
      • RateLimit / CircuitBreaker
  • Goのいいところ
    • ネットワーク/ミドルウェアレイヤーと相性がいい
    • チューニングがしやすい(go -test bench, pprof)
  • メルカリでのMicroservices
    • 各チームがメンバーの採用や技術スタックの選定にもオーナーシップを持つ


よくあるJava IT企業で新規プロジェクトをGoで立ち上げてみてる話

Speaker: 武田 洋平(株式会社メディアドゥ)

  • 抱えていた課題
    • 10年以上構造に変化がないプロダクト
      • 追加機能・不具合習性が難しい
  • ビジネスとして成り立つサービスにしたい
    • 安定した運用を行いたい。
  • Javaをやめよう
    • 優先度の高いものから置き換える
    • パフォーマンス、開発のしやすさからGolangを導入へ
  • 経営層、マネジメント層への説明
    • JVMではない点で楽な所もある
    • Javaで進める方が将来的にリスクが高い
    • 学習コストが低いので、Golangもすぐ書ける
  • スモールスタート
    • 論より証拠、さっさと動くものを作成
  • Golangの採用でよかったもの
    • 書き方がかなり制限される
  • Good Performance
    • みんなの開発が速い
  • error handling
    • 本格的なサービス運用で、いろんな課題が改めて露呈するはず
  • Golang利用企業としてブランディング
  • よくあるJavaメインのIT企業の会社もGolangは導入できる


OpenCensus による APM の実現と、未来

Speaker: @munisystem

  • アプリのパフォーマンスを気にするのか?
    • ユーザ体験が重要な指標
    • ユーザ体験とパフォーマンスには重要な関係があるはず
  • APM
    • Application Performance Management
    • パフォーマンス及びその改善位必要なデータの取得
    • 取得したデータの可視化、活用できる基盤の構築
    • 取り扱う代表的なデータ
      • Tracing
      • Metrics
      • Logging
      • Error reporting
  • Tracing
    • Dstadog APM
  • Metrics
    • Datadog
  • OpenCensus
    • APMに必要なデータを収集し外部サービスに提供するフレームワーク
    • アプリケーションはデータを収集する部分を実装する
    • OpenCensusの欠点
      • バックエンド固有の最適化のためのメタデータが付与できない
  • ベンダーに依存しない実装により、APMはさらなるエコシステムの成長を遂げるのではないか


Profiling Go Application

Speaker: orisano

  • 画像変換サーバの例
    • 高速化のために
      • Boundary Check Elimination (BCE)
    • pprofで遅い原因を特定したら
      • ベンチマークを取る
  • 普段使っているものもカリカリにチューニングされてるわけではない
  • Golangはパフォーマンスチューニングしやすい言語


Biscuit Code Reading

Speaker: Yoshiki Shibukawa


Golang で作られた Biscuit というOSのお話。

  • Biscuit
    • Monolithic, POSIX-subset OS kernel in Golang
      • Multicore
      • Kernel-supported threads
      • Journaled FS with concurrent, deferred, and group commit
      • Virtual memory for copy-on-write and lazily mapped anonymous/file pages
      • TCP/IP stack
    • In addition to that, we can read the OS implementation in Go
  • 当たり前なんですが、OSに関する内容で、だいぶ理解が追いつきませんでした笑


Consider pluggable CLI tool implementation

Speaker: @izumin5210

  • grapi
    • GoのAPI Serve開発体験を高めるCLI+package

  • plugin機構
    • ユーザが外から拡張できるようにする
    • Extensibility
  • Plugin Experience
    • 複数人のプロジェクトでも利用できるか?
    • 実装・デバッグは簡単か?
    • 配布は簡単か?
  • 使いやすい・作りやすいplugin機構


フィードバック制御によるGoroutine起動数の最適化

Speaker: monochromegane

  • 並行処理とチューニングにおける課題
    • 最適な並行数を求めたい
      • ブロッキング発生する状況においては、開発者の経験と地道なチューニングに依存
      • 従来手法は、短期稼働で即時に反応できる場合に辛い
  • Kaburayaアーキテクチャ
    • 処理やランタイムの特性に依存せずにマシンの負荷に応じて反応的かつ継続的に、並行数を最適化する
    • フィードバック制御
    • PID制御(P:比例制御、I:積分制御、D:微分制御)
    • 制御器の2重ループにした
  • Go言語への適用
    • バッファ上限値を変更可能なElasticChannel
  • 最適な並行数とは
    • 高速化率(Speedup)
    • 実行効率(Efficiency)


Practical Go Concurrency Design Patterns

Speaker: moriyoshi

  • Goのsync.Mutex
    • LockしたGoroutine以外からUnLockできる。一般的なセマフォの代わりに使える。
      • (例えばJavaのLock系のクラスだと、ロックしたスレッドからしかアンロックできない。)
  • Monitors and Condtion Variables
    • Mesa semanntics
      • ほとんどのプログラミング言語はこちらを採用
    • Hore semantics
  • GolangでのBroadcastを実現する
    • channel の closeを利用すること大まかに再現できる
    • sync.Cond


Pains and gains of architecting microservices on local dev environment

Speaker: @creasty, @izumin5210(ウォンテッドリー株式会社)

  • 気持ちが良い開発体験を提供したい
    • 開発環境をDocker化
      • ただし、コンテナの起動状況を考えたくない(Dockerを使っていることに気づかないのが理想)
      • 解決策:rid


まとめ

若い人もかなりバリバリにGolangで開発してる感じが発表から伝わって来ました。
言語特性もあって、けっこう低レイヤな話になってるパターン多かったかな。
今度は、懇親会にも参加したいです。



関連リンク

ElectronでHello World

もう腐るほどやられてるとは思いますが、Electronで Hello Worldやっておきます。

というか、そもそも公式の electron-quick-start に従えばいいだけなんですが。


今回の私用のメモとしてののHello Worldは、以下の前回エントリの続きという感じになっています。


必要な記述をする

最終的なディレクトリ構造は、以下の様な感じになります。

.
├── index.html
├── index.js
├── node_modules/
├── package.json
└── yarn.lock


現在足りて無い、index.html と index.js を記述します。


index.html

<html>
  <head>
    <meta charset="utf-8">
    <title>Sample App</title>
  </head>
  <body>
    <p>Hello World</p>
  </body>
</html>


次は index.js を記述します。

index.js

const {app, BrowserWindow} = require('electron');

let mainWindow;

function createWindow () {
  mainWindow = new BrowserWindow({
      width: 800, 
      height: 600
    });
  
  mainWindow.loadURL('file://' + __dirname + '/index.html');

  mainWindow.on('closed', function () {
    console.log(`mainWindow on closed`);
    mainWindow = null
  })
};

app.on('window-all-closed', function() {
  console.log(`electron on window-all-closed`);
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on("active", () => {
console.log(`electron on active`);

  if (mainWindow === null) {
      createWindow();
  }
});

app.on('ready', function() {
  console.log(`electron on ready`);
  createWindow()
});


実際の作業は、Visual Studio Codeでやりました。後の実行も含めて。

f:id:nini_y:20181124115234p:plain


実行

せっかく Yarnを入れてある状態なので、以下の通りyarnで実行しています

yarn electron .


以下が表示されました。うん、シンプル(笑)

f:id:nini_y:20181124114850p:plain


まとめ

まとめもへったくれもありませんが、無事に Hello World ができました。



関連エントリ

Yarnを使ってみる

最近になって今更感かなりありますが、electronNode.js を触り始めました。
パッケージを環境に導入するには、npmコマンドを利用するのが当然だと思ってたんですが、
Yarnというツールも存在している様ですね。いやー、この界隈全然詳しくないです。

Yarnの方が高速だとかという話もあるので、Yarn使っていこうかと思います。


インストール

Macへのイントールならhomebrew使えば簡単ですね。以下実行!

brew install yarn


実行結果は以下の通りです。

$ brew install yarn
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 2 taps (ethereum/ethereum and homebrew/core).
==> New Formulae
azure-storage-cpp   gitmoji             i386-elf-gcc        mesa                ruby@2.4            um
fluxctl             healpix             kubespy             node@10             shellz
fx                  i386-elf-binutils   maven@3.5           resin-cli           sourcedocs
==> Updated Formulae

・・・(中略)・・・

==> Renamed Formulae
mat -> mat2
==> Deleted Formulae
corebird    heroku      kibana@4.4  maven@3.0   maven@3.1   nethack4    pxz         ruby@2.2    taylor      tcptrack

==> Installing dependencies for yarn: icu4c and node
==> Installing yarn dependency: icu4c
==> Downloading https://homebrew.bintray.com/bottles/icu4c-62.1.sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring icu4c-62.1.sierra.bottle.tar.gz
==> Caveats
icu4c is keg-only, which means it was not symlinked into /usr/local,
because macOS provides libicucore.dylib (but nothing else).

If you need to have icu4c first in your PATH run:
  echo 'export PATH="/usr/local/opt/icu4c/bin:$PATH"' >> ~/.bash_profile
  echo 'export PATH="/usr/local/opt/icu4c/sbin:$PATH"' >> ~/.bash_profile

For compilers to find icu4c you may need to set:
  export LDFLAGS="-L/usr/local/opt/icu4c/lib"
  export CPPFLAGS="-I/usr/local/opt/icu4c/include"

==> Summary
🍺  /usr/local/Cellar/icu4c/62.1: 250 files, 67.3MB
==> Installing yarn dependency: node
==> Downloading https://homebrew.bintray.com/bottles/node-11.2.0.sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring node-11.2.0.sierra.bottle.tar.gz
==> Caveats
Bash completion has been installed to:
  /usr/local/etc/bash_completion.d
==> Summary
🍺  /usr/local/Cellar/node/11.2.0: 3,936 files, 50.1MB
==> Installing yarn
==> Downloading https://yarnpkg.com/downloads/1.12.3/yarn-v1.12.3.tar.gz
==> Downloading from https://github-production-release-asset-2e65be.s3.amazonaws.com/49970642/a4875000-e25c-11e8-88b4-45
######################################################################## 100.0%
🍺  /usr/local/Cellar/yarn/1.12.3: 14 files, 4.7MB, built in 9 seconds
==> Caveats
==> icu4c
icu4c is keg-only, which means it was not symlinked into /usr/local,
because macOS provides libicucore.dylib (but nothing else).

If you need to have icu4c first in your PATH run:
  echo 'export PATH="/usr/local/opt/icu4c/bin:$PATH"' >> ~/.bash_profile
  echo 'export PATH="/usr/local/opt/icu4c/sbin:$PATH"' >> ~/.bash_profile

For compilers to find icu4c you may need to set:
  export LDFLAGS="-L/usr/local/opt/icu4c/lib"
  export CPPFLAGS="-I/usr/local/opt/icu4c/include"

==> node
Bash completion has been installed to:
  /usr/local/etc/bash_completion.d


Windowsの場合は、Windows用のインストーラをダウンロードして実行してください。


インストール後は、一応バージョン確認を実施

$ yarn -v
1.12.3


Yarnの操作

Yarnnpmで実行できる操作は、基本的にできるようです。


例えばプロジェクトの初期化をするなら

yarn init

詳細情報の入力を省略するなら以下です

yarn init -y

これでpackage.json.jsonが作成されます。

$ cat package.json 
{
  "name": "develop",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT"
}


開発環境に必要なパッケージをローカルインストールする(devDependenciesに追加したい時)には以下を実行します

yarn add [package] --dev

electronを対象にするなら以下の通りです

$ yarn add electron --dev
yarn add v1.12.3
info No lockfile found.
[1/4] 🔍  Resolving packages...
warning electron > electron-download > nugget > progress-stream > through2 > xtend > object-keys@0.4.0: 
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 📃  Building fresh packages...
success Saved lockfile.
success Saved 124 new dependencies.
info Direct dependencies
└─ electron@3.0.10
info All dependencies
├─ @types/node@8.10.38
├─ ajv@6.5.5
├─ ansi-regex@2.1.1
├─ array-find-index@1.0.2
├─ asn1@0.2.4
├─ asynckit@0.4.0

・・・(中略)・・・

├─ xtend@2.1.2
└─ yauzl@2.4.1
✨  Done in 54.26s.


実行環境に必要なパッケージをローカルインストールする(dependenciesに追加したい時)には以下を実行します

yarn add [package]

electron-logを対象にするなら以下の通りです

$ yarn add electron-log
yarn add v1.12.3
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 📃  Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ electron-log@2.2.17
info All dependencies
└─ electron-log@2.2.17
✨  Done in 0.74s.


これらを実行した後のpackage.jsonは以下の様になっています。

{
  "name": "develop",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "electron": "^3.0.10"
  },
  "dependencies": {
    "electron-log": "^2.2.17"
  }
}

ディレクトリ構成は以下の通りです。

ls -l
total 88
drwxr-xr-x  138 yuki  staff   4692 11 23 21:19 node_modules
-rw-r--r--    1 yuki  staff    197 11 23 21:19 package.json
-rw-r--r--    1 yuki  staff  37898 11 23 21:19 yarn.lock


まとめ

だいぶ単純な操作しかしてませんが、yarnnpm の代わり(+それ以上の操作)ができる様です。
これからはYarnを利用していこうと思います。