scoop cleanup * はいいぞ
Windows を開発機にしているユーザーの100%が導入している1と言われる Scoop ですが、インストールされているアプリケーションのアップデートが溜まってくると割とストレージを圧迫します。
そんなときは scoop cleanup *
として過去のバージョンを消してしまうと効果があります。
これで
これが
こうなる。
なお、私の場合は入れているアプリケーションは以下です。
なお、 scoop cache rm *
も効果があります。私はやりませんが。
いや別にデメリットがあるわけじゃないんですけどね。
余談ですが、前にいた会社では Scoop 経由で他にもたくさん入れていたのでこれ以上に効果がありました。
-
私調べ (N=1)↩
nim / rust / pony の速度比較
※ 最初に申し上げておきますが、とても浅はかな比較です。
Rust は言わずもがな、「最も愛されているプログラミング言語」4年連続1位1の言語です。
データの生存期間を型で管理することで、GCなしにメモリセーフなプログラミングを可能にしてますね。
トレイトやマクロなどもあり、表現力も豊富で開発も盛んなので注目ですね。
実行速度もちょくちょく C++ に勝ったり負けたりと、非常に高速なことで知られます。
Nim は柔軟な書き方ができる言語で、最近お気に入りです。
ついこの間、めでたく v1.0 を迎えて安定期に入りました🎉
遅延リストのリテラルがないのが残念です。マクロはあるのでその気になれば自力実装はできそうですが、現時点では私には Nim 力が足らず無理です。
Nim で書かれたコードは一度 C のコードに変換されてから2、C のコンパイラでさらにネイティブコードにコンパイルされます。
Pony は昨日3初めて知った言語ですが、割と前からあるみたいですね。
言語構文レベルでアクターモデルを採用しているのが、たぶん最大の特徴です。
Rust に似て変数の所有権のようなもの4を型に持たせることで、アクターモデルでありながらデータのコピーを極力減らすことができ、高速かつ安全なプログラミングを可能にしているようです。
補完にまで対応した IDE やエディタ拡張が見当らないので、それがネックですね…。
まだセルフホストには至っていない模様。
環境
$ cat /etc/os-release NAME="Ubuntu" VERSION="18.04.3 LTS (Bionic Beaver)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 18.04.3 LTS" VERSION_ID="18.04" HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" VERSION_CODENAME=bionic UBUNTU_CODENAME=bionic
$ rustc --version rustc 1.39.0 (4560ea788 2019-11-04)
$ nim --version Nim Compiler Version 1.0.2 [Linux: amd64] Compiled at 2019-10-22 Copyright (c) 2006-2019 by Andreas Rumpf git hash: 193b3c66bbeffafaebff166d24b9866f1eaaac0e active boot switches: -d:release
$ ponyc --version 0.33.0-98c36095 [release] compiled with: llvm 7.0.1 -- cc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0 Defaults: pic=true
比較
なるべく公平になるようにコードを書いているつもりです。
FizzBuzz
コード
Rust
fn fizz_buzz(n: i32) { let s = match (n % 3, n % 5) { (0, 0) => "FizzBuzz".to_string(), (0, _) => "Fizz".to_string(), (_, 0) => "Buzz".to_string(), _ => n.to_string(), }; println!("{}", s); } fn main() { for x in 1..=100 { fizz_buzz(x); } }
Nim
proc fizzBuzz(n: int) = let (x, y) = (n mod 3, n mod 5) let s = if (x, y) == (0, 0): "FizzBuzz" elif x == 0: "Fizz" elif y == 0: "Buzz" else: $n echo s for x in 1..100: fizzBuzz x
Pony
use "collections" actor FizzBuzz let _env: Env new create(env': Env) => _env = env' be fizz_buzz(x: I32) => let s = match (x % 3, x % 5) | (0, 0) => "FizzBuzz" | (0, _) => "Fizz" | (_, 0) => "Buzz" else x.string() end _env.out.print(s) actor Main new create(env: Env) => let fb = FizzBuzz(env) for x in Range[I32](1, 101) do fb.fizz_buzz(x) end
コンパイル速度
Rust
$ time -p rustc -Copt-level=3 -Clto -Cpanic=abort fizzbuzz.rs real 2.23 user 1.96 sys 0.39
Nim
$ time -p nim c -d:release --opt:speed --hints:off fizzbuzz CC: stdlib_io.nim CC: stdlib_system.nim CC: fizzbuzz.nim real 2.03 user 1.70 sys 0.48
Pony
$ time -p ponyc --verbose=0 real 2.84 user 2.03 sys 0.81
Rust | Nim | Pony | |
---|---|---|---|
real | 2.23 | 2.03 | 2.84 |
user | 1.96 | 1.64 | 2.12 |
sys | 0.39 | 0.54 | 0.70 |
コンパイル速度はそこまで差が出ないようです。
※ ただし、Nim はコンパイル結果をキャッシュするので、同じプロジェクトであれば2度目以降のコンパイルが非常に高速になります。 --forceBuild
/ -f
オプションを付けるかキャッシュを削除すると1度目と同じ条件になります。今回は毎回キャッシュを削除しました。
※ Rust は最適化オプションをいくつか外すことで、成果物のファイルサイズを犠牲(3倍近く)にかなり高速(4分の1以下)になりました。
ファイルサイズ
Rust
$ ls -l fizzbuzz -rwxrwxrwx 1 htsign htsign 928400 11月 21 21:58 fizzbuzz*
Nim
$ ls -l fizzbuzz -rwxrwxrwx 1 htsign htsign 88864 11月 21 21:59 fizzbuzz*
Pony
$ ls -l fizzbuzz -rwxrwxrwx 1 htsign htsign 163176 11月 21 21:59 fizzbuzz*
Rust | Nim | Pony |
---|---|---|
928,400 bytes (907KB) | 88,864 bytes (87KB) | 163,176 bytes (159KB) |
※ KB 表記の端数は四捨五入
Rust が圧倒的にデカいですね。
Rust のバイナリがデカい理由はデバッグシンボルが残っているからかと思われます。最適化オプション有効にしてるのに…。
ここではコマンド単体で完結することを前提とするのでこれを最終結果とします。
実行速度
Rust
$ time -p ./fizzbuzz > /dev/null real 0.01 user 0.00 sys 0.01
Nim
$ time -p ./fizzbuzz > /dev/null real 0.00 user 0.00 sys 0.00
Pony
$ time -p ./fizzbuzz > /dev/null real 0.02 user 0.00 sys 0.01
Rust | Nim | Pony | |
---|---|---|---|
real | 0.01 | 0.00 | 0.02 |
user | 0.00 | 0.00 | 0.00 |
sys | 0.01 | 0.00 | 0.01 |
まぁ当然ですが、負荷が軽すぎてほとんど差が出ないですね…。
45番目のフィボナッチ数
※ 45番目なのは計算時間的にちょうどよさそうなので。
コード
Rust
fn fibonacci(n: i64) -> i64 { match n { 1 | 2 => 1, _ => fibonacci(n - 2) + fibonacci(n - 1), } } fn main() { println!("{}", fibonacci(45)) }
Nim
func fibonacci(n: int64): int64 = case n of 1, 2: 1.int64 else: fibonacci(n - 2) + fibonacci(n - 1) echo fibonacci(45)
Pony
primitive Fibonacci fun fib(n: I64): I64 => match n | 1 | 2 => 1 else fib(n - 2) + fib(n - 1) end actor Main new create(env: Env) => env.out.print(Fibonacci.fib(45).string())
コンパイル速度
※ 実行コマンドは FizzBuzz のとほとんど変わらないので割愛
Rust | Nim | Pony | |
---|---|---|---|
real | 2.20 | 2.00 | 1.19 |
user | 1.75 | 1.64 | 0.89 |
sys | 0.45 | 0.48 | 0.29 |
なぜか Pony が FizzBuzz の場合と比べてちょっと速くなりました。
ファイルサイズ
Rust | Nim | Pony |
---|---|---|
924,024 bytes (902KB) | 84,288 bytes (82KB) | 158,696 bytes (155KB) |
こちらも相変わらず Rust がダントツにデカいです。
実行速度
Rust | Nim | Pony | |
---|---|---|---|
real | 2.88 | 3.45 | 3.52 |
user | 2.87 | 3.45 | 3.48 |
sys | 0.01 | 0.00 | 0.03 |
さっきと比べて少し差が出ましたね。
Rust、やはり速い。
並列性を上げて計算すれば Pony も速くなったりするのでしょうか。
以上、Pony はまだまだ発展途上ですし、Rust もバージョン上がるごとに最適化が進んだりするので、本当にガバガバ比較です。
結局のところ好みで選べばいいんじゃないかと思います。
並列計算が目的なら Erlang や Elixir などの選択肢もあります。が、あれはプロセス単位での並列なのでちょっと事情が違うかも。
-
https://insights.stackoverflow.com/survey/2019#technology-_-most-loved-dreaded-and-wanted-languages↩
-
いわゆるトランスパイル↩
-
投稿時間が日を跨いでしまったので…。↩
-
Pony では Reference Capabilities と呼ばれます。↩
Promise を再帰させるとどうなるか
ただの興味本位です。
以下のコードを各環境で動かしていきます。
const p = n => new Promise(resolve => { console.log(n); resolve(p(n + 1)); }); p(1);
なるべく公平を期すため、ブラウザの場合は about:blank
にて実行します。
環境
PC1:
種別 | 詳細 |
---|---|
OS | Windows 10 |
CPU | AMD Rizen 5 1600 |
MEM | DDR4 PC4-24000 16GB x2 (空き 20000MB程度) |
PC2:
種別 | 詳細 |
---|---|
OS | macOS 10.14.6 (Mojave) |
CPU | Intel Core i5 5250U |
MEM | LPDDR3 PC3-12800 4GB (空き1000MB程度) |
結果
環境 | 再帰回数 | 備考 |
---|---|---|
Edge 44 | 1000 | 20回置きにウェイトが入る。終了時は特に出力なし |
Safari | 3674 | コンソールにはエラーが表示されず、 ブラウザページにポップアップで Maximum call stack size exceeded. と表示され終了 |
Firefox 69 (PC1) | 582 | too much recursion の例外で終了 |
Firefox 69 (PC2) | 2946 | too much recursion の例外で終了 |
Chrome 77 (PC1) | 1934 | RangeError: Maximum call stack size exceeded の例外で終了 |
Chrome 77 (PC2) | 1929 | RangeError: Maximum call stack size exceeded の例外で終了 |
node 10.16.3 (PC1) | 1818 | エラーを吐かずにクラッシュして終了 |
node 10.16.3 (PC2) | 1847 | RangeError: Maximum call stack size exceeded の例外で終了 |
node 12.11.1 (PC1) | 1806 | エラーを吐かずにクラッシュして終了 |
node 12.11.1 (PC2) | 1835 | RangeError: Maximum call stack size exceeded の例外で終了 |
この規則性のない結果から、おそらく Promise における再帰については厳密な仕様はなく実装依存であることが推定されますが、仕様書のどの辺りを見ればよいのか分からず…。
同じバージョンでも環境によって結果が異なるので、メモリ等の状態によっても変わるのかなと思いますが、ブラウザのソースコード読む気力もなく、「とりあえず Promise の再帰はなるべく避けよう」という辺りで落ち着けておきます。
URLからトラッキングのアレを削除するやつ書いた
モチベーションは Neat URL がだいぶ前から動かなくなっていた*1から。
似たようなアドオンも軒並み死んでいるように思うので自分で作りました。
とりあえず単純なトラッキングタグ(って言うのかな?)は削除できるようにしてあり、最終的な目標としては Neat URL と同等の機能を持たせてあげたい。
グリモンスクリプトなので、 Edge, Firefox, Chrome, Safari 等のグリモン系アドオンがあり
'URL' in window && 'URLSearchParams' in window
が真となる環境であれば問題なく使えるはずです。
なんなら Firefox for Android であればスマートフォンでも使えます*2。
npm scripts で OS ごとに処理を分ける
npm scriptsに clean
ってコマンドを登録しようとしたんですよ。
普通なら rimraf を使うところなんですが、 cleanの過程で node_modules
ディレクトリも削除して綺麗にしたい。
かと言って npm i -g rimraf
など、環境依存になるので以ての外。
rm
コマンドは *NIX 環境に依存してしまう。
rd
コマンドは Windows環境に依存してしまう。
さて困った。
そこで閃きました。
"scripts": { "clean": "node -e \"process.exit(process.platform === 'win32' ^ 1)\" && rd /s /q node_modules || rm -rf node_modules" }
こうします。
これならば、process.platform
を評価して win32
だった場合とそれ以外で処理を分けることができます。
※ true ^ 1 // ==> 0
false ^ 1 // ==> 1
となり、シェルはこれをエラーコードと見るため、 &&
や ||
で繋げることによって処理が分かれます。