雑なNimメモ+rootless docker

最初るーとれすどっかーって書こうと思ったんですが、どっかーん　と書きたくなる気持ちがどうしても抑えきれず、しょうがなくちゃんとした表記にしました。

## 概要
各種文字列結合手段のベンチマークを取ったのでそのコード
ついでにそれをrootless dokcerで動かす(実際に運用するときはバイナリ持ってきて動かすだろうから実用的ではない)
$

## $
これが頭についた変数は文字列として扱われるようになります。~~rubyで言うところの.to_sかね。~~
実装追ったら magic付でIntToStrって書いてあったので、恐らくC言語(Cコンパイラ)のIntToStrに放り込むだけだと思われます。
なお、Nimの`{.magic}`はコンパイラマジックを表すそうです。
自分の解釈では、ある機能を実現するためにその言語の持つ機能だけでは実装出来ない場合にコンパイラさんが持つ機能に依存しますよって意味で、
少なくともNimで単にアプリケーションを作りたいだけの人にとってはmagicってついてたら「ああコンパイラさんにお願いしてるのね」くらいで覚えておけば困らないんじゃないかと思います。

ただ、実挙動でちょっと首をかしげたのが

```nim
import typetraits

var oops = 1
var oops2 = `$`oops

echo oops # 1
echo oops2 # 1

echo `$`oops.type.name # int
echo oops2.type.name # string
```
~~直でいったときと代入した時で吐く型が違うんだよな…
Cコード追うか…？~~

ちなみに

```bash
nim c --compileOnly:on --nimcache:[[path/to/csrc]] [[path/to/nimsrc]]
```
これでバイナリ作成せずに中間のCコードだけを得られます。(--nimcacheに指定したdirにファイルができる)
nimcacheのデフォルトdirがどこか分かれば良かったんだけど分からなかったんだよね！

## 20191230追記
コメントにて教えていただきました。感謝。
`$`はチェーンが全て終わったあとに評価されるそうです。

```ruby:ruby
pp 1.to_s.class
```
と同義とするためには

```nim
echo (`$`oops).type.name
```
こうですね。元のコードの状態で上のrubyコードみたいに評価されてると勝手に思い込んでしまいました。

## 文字列結合方式とべんち
自分が調べた限りだとNimには特にベンチツールというものはなく、自分でコード書いて計測せいやバカヤロウ方式っぽいので雑にやりました。もしメジャーなツールが実はある場合はそっと教えてください。

```nim
import times, strutils, random

const alpha = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]

template benchmark(benchmarkName: string, code: untyped) =
  block:
    let t0 = epochTime()
    code
    let elapsed = epochTime() - t0
    let elapsedStr = elapsed.formatFloat(format = ffDecimal, precision = 3)
    echo "CPU Time [", benchmarkName, "] ", elapsedStr, "s"


proc randStr(): string =
  randomize()
  return alpha[rand(25)]

benchmark "&":
  var joined: string
  for i in 1..1000000:
    joined = joined & randStr()

benchmark "&=":
  var joined: string
  for i in 1..1000000:
    joined &= randStr()

benchmark "add":
  var joined: string
  for i in 1..1000000:
    joined.add(randStr())

benchmark "join":
  var strs: array[1000000, string]
  for i in 0..999999:
    strs[i] = randStr()
  discard strs.join("")
```

実はこの時点で、`&`はmagicのconStrStr、それ以外はAppendStrStrに行き着いてるのを発見していて、__なんとなくオチが読めていましたが__、賭郎のお屋形様も「世の中には本の知識だけでは知りえない、実際にやってみなければわからないことっていうのがある」って言ってたのでとりあえず続行することにします。

## ﾙｰﾄﾚｽ ﾄﾞｶｰ
CentOS 7.7.1908
kernel 3.10.0-1062.1.1.el7.x86_64

この1個前のカーネルだと動かないとかそういう噂も見ましたがどうなんでしょうね。
ローカルだしセットアップはrootで。
screenが入ってない方は入れといたほうが吉。
なお、こんな記事読まんでも、インストールシェル叩けば足りない設定は__全部シェルのほうが教えてくれます。__

```bash
vi /etc/sysctl.d/51-rootless.conf
```
`user.max_user_namespaces = 28633`と書いて保存。
保存したら

```bash
sysctl --system
```
で反映。
反映したらユーザ名前空間を設定します。既にdocker使ってる人はもう設定済みかな。
これも設定されていなかったらシェルのほうが教えてくれます。(確か)
subuid/subgidが既に存在しないか、してたら中身確認してから設定しましょう。

```bash
echo "[[コンテナ実行するユーザ名]]:100000:65536" >> /etc/subuid
echo "[[コンテナ実行するユーザ名]]:100000:65536" >> /etc/subgid
```

ここまでやったらrootからログアウトしてコンテナ実行したいユーザでログインします。
ログインしたら

```bash
curl -fsSL https://get.docker.com/rootless | sh
```
でいけるかなと。うまくいくと、起動コマンドと通しといたほうが良いPATHと環境変数をそっと教えてもらえるので設定しましょう。
利用するには起動し続ける必要があるのでscreen叩いて起動、デタッチしときます。

## Dockerfile
ベンチコードをコンテナ内に取り込む手段として、自分は考えるのが面倒になったのでgitにおいてビルド時にcloneしてしまう方式を取りました。privateなのでトークンまでわざわざ発行したよ。ﾌﾌ。
こんなコードがprivateな理由は、self hosted github-actionsをついでにやろうとしたらとてもパブリックでやれるような方式ではなかったため、しょうがなくでございます。

```dockerfile
FROM alpine:latest
RUN apk update && \
    apk --no-cache add wget git gcc bash bash-completion musl-dev && \
    cd /opt && \
    wget https://nim-lang.org/download/nim-1.0.4-linux_x64.tar.xz && \
    tar Jxf nim-1.0.4-linux_x64.tar.xz && \
    rm -f nim-1.0.4-linux_x64.tar.xz && \
    mv nim-1.0.4 nim && \
    cd nim && \
    bash ./build.sh && \
    bin/nim c koch && \
    ./koch tools && \
    cd /opt && \
    git clone https://[[token]]:x-oauth-basic@github.com/[[repo url]].git nimtest
ENTRYPOINT ["/opt/nim/bin/nim","c","-r","--verbosity:0"]
CMD ["/opt/nimtest/src/[[ベンチコードファイル名]].nim"]
```
こんなん自分でやらんでも公式にあるやろ、と思ったんですが、自分が確認した当時だとalpineイメージでのnimのバージョンがえっらい古くて悲しい気持ちになった覚えがあります。
あと、alpineだと現状`choosenim`が入らないようです。入れる方法も探したんですが結局「ﾑﾘﾝﾇ」ってなった記憶。

あとはビルドしてrunすればベンチ結果がやっと見れます。
ちなみにビルドクッソ長いしalpineなのにビルド後のサイズが1.01GBとかあってつらい思いをしました。

## 結果
```
CPU Time [&] 96.336s
CPU Time [&=] 1.106s
CPU Time [add] 1.068s
CPU Time [join] 1.179s
```
ですよね。
完。