CLOVER🍀

That was when it all began.

システムコンポーネント(CPU、メモリ、ディスク、ネットワーク等)のレイテンシとタイムスケールなどなど

これは、なにをしたくて書いたもの?

CPUサイクルに対してメモリアクセスのレイテンシ比とか、メモリアクセスとディスクアクセスのレイテンシ比がどこかに
まとまっていたな、と時々思いながら、どこにあるか忘れて探すことになるので。

各リソースアクセスへのスケール感を把握するのに役に立つと思うので、メモしておくことにしました。

出典は、「詳解 システム・パフォーマンス」です。

O'Reilly Japan - 詳解 システム・パフォーマンス

ディスクやネットワークに関しても、まとまっていました。

あと、JCacheでもメモリ、ネットワークに関しては参考になりそうな図があったので、こちらもメモとして。

システムコンポーネント(CPU、メモリ、ディスク、ネットワーク等)のレイテンシとタイムスケール

3.3GHzのCPUのレジスタアクセスから、メモリ、ディスク等のおよそのレイテンシと、CPUのレジスタアクセスを1秒とした時に
その比がどれくらいになるか(タイムスケール)を示した表です。

操作 レイテンシ 対比
1 CPU(3.3GHz)サイクル 0.3 nano秒 1秒
レベル1 キャッシュアクセス 0.9 nano秒 3秒
レベル2 キャッシュアクセス 2.8 nano秒 9秒
レベル3 キャッシュアクセス 12.9 nano秒 43秒
メインメモリアクセス(DRAM、CPUから) 120 nano秒 6分(360秒)
SSD I/O(フラッシュメモリ 50 〜 150 μ秒 2〜6日
回転ディスク I/O 1〜10 m秒 1〜12ヶ月
インターネット: サンフランシスコ - ニューヨーク 40 m秒 4年
インターネット: サンフランシスコ - イギリス 81 m秒 8年
インターネット: サンフランシスコ - オーストラリア 183 m秒 19年
TCPパケット再送 1〜3秒 105〜317年
OS仮想化システムリブート 4秒 423年
SCSIコマンドタイムアウト 30秒 3,000年
ハードウェア仮想化システムリブート 40秒 4,000年
物理システムリブート 5分 32,000年

出典は、「詳解 システム・パフォーマンス」の「2.3.2 タイムスケール/表2-2 システムレイテンシのタイムスケール例」。

O'Reilly Japan - 詳解 システム・パフォーマンス

これとほぼ同じものが、こちらのページで見れますけど。
※微妙に数字が違いますが

f:id:Kazuhira:20210501145509p:plain

Compute Performance – Distance of Data as a Measure of Latency | Formulus Black | In-Memory Storage

このページを追うと、「詳解 システム・パフォーマンス」の著者のページにたどり着きます。

Systems Performance Book

というか、第2版があったんですね。欲しい…。

Systems Performance 2nd Edition Book

Amazon | Systems Performance (Addison-Wesley Professional Computing Series) | Gregg, Brendan | Performance Optimization

ディスクI/Oのレイテンシのタイムスケール

次は、ディスクI/Oのタイムスケールのイメージです。オンディスクキャッシュヒットした時のレイテンシを1秒と
した場合に、それぞれの値がどれくらいになるかも書かれています。

イベント レイテンシ 対比
オンディスクキャッシュヒット < 100 μ秒 1秒
フラッシュメモリからの読み出し 〜100から1,000 μ秒(小規模なI/Oから大規模なI/Oまで) 1〜10秒
回転ディスクからのシーケンシャル読み出し 〜1 m秒 10秒
回転ディスクからのランダム読み出し(7,200rpm) 〜8 m秒 1.3分
回転ディスクからのランダム読み出し(遅く、キューイングされる) > 10 m秒 1.7分
回転ディスクからのランダム読み出し(キューで大量に待っている) > 100 m秒 17分
最悪の条件の仮想ディスクI/O(ハードウェアコントローラー、RAID 5、キューイング、ランダムI/O) 〜1 m秒 2.8時間

出典は、「詳解 システム・パフォーマンス」の「9.3.2 タイムスケール/表9-1 ディスクI/Oレイテンシのタイムスケールのイメージ」。

O'Reilly Japan - 詳解 システム・パフォーマンス

ネットワークのレイテンシのタイムスケール

ネットワークのレイテンシのタイムスケール。ICMPエコー要求からエコー応答を受け取るまで、要するにpingでの
レイテンシとそのスケールを表にしたものです。

localhostでのpingを1秒とした時に、他の値がそれぞれどのくらいになるかの対比もまとめられています。

送信 受信 媒体 レイテンシ 対比
localhost localhost カーネル 0.05 m秒 1秒
ホスト ホスト(同じサブネット) 10GbE 0.2 m秒 4秒
ホスト ホスト(同じサブネット) 1GbE 0.6 m秒 12秒
ホスト ホスト(同じサブネット) WiFi 3 m秒 1分
サンフランシスコ ニューヨーク インターネット 40 m秒 13分
サンフランシスコ イギリス インターネット 81 m秒 27分
サンフランシスコ オーストラリア インターネット 183 m秒 1時間

出典は、「詳解 システム・パフォーマンス」の「10.3.5 レイテンシ/表10-1 ping遅延の例」。

O'Reilly Japan - 詳解 システム・パフォーマンス

キャッシュオブジェクトへのアクセス

上記の情報とは違ってキャッシュに閉じた話ですが、以下の対比についてもメモとして。

f:id:Kazuhira:20210501145340p:plain

https://www.jfokus.se/jfokus16/preso/How-to-Speed-Up-Your-Application-using-JCache.pdf

JCache Using JCache

Dockerfile内で複数行の文字列を作りたい

これは、なにをしたくて書いたもの?

時々、Dockerfile内で複数行の文字列を作りたくなる時があるのですが、どうやったらいいのかすぐに忘れるのでメモして
おこうかな、と。

環境

今回の環境は、こちらです。

$ docker version
Client: Docker Engine - Community
 Version:           20.10.6
 API version:       1.41
 Go version:        go1.13.15
 Git commit:        370c289
 Built:             Fri Apr  9 22:47:17 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.6
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       8728dd2
  Built:            Fri Apr  9 22:45:28 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.4
  GitCommit:        05f951a3781f4f2c1911b05e61c160e9c30eaa8e
 runc:
  Version:          1.0.0-rc93
  GitCommit:        12644e614e25b05da6fd08a38ffa0cfe1903fdec
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

結論

試した結果としては、複数行の文字列を書く…とはちょっと違いますが、シェルのグルーピング(Compound Commands)を
使うのがよいでしょう。

Command Grouping (Bash Reference Manual)

bash(1): GNU Bourne-Again SHell - Linux man page

こんな感じです。

RUN { \
    echo '文字列1'; \
    echo '文字列2'; \
    echo '文字列3'; \
}

{}は、()にしてもOKです。

RUN ( \
    echo '文字列1'; \
    echo '文字列2'; \
    echo '文字列3'; \
)

echoを繰り返しているだけでは?と思うかもですが、コマンドをグルーピングするとリダイレクトを1回で済ませることが
できます。

RUN { \
    echo '文字列1'; \
    echo '文字列2'; \
    echo '文字列3'; \
} > /tmp/test1

RUN ( \
    echo '文字列1'; \
    echo '文字列2'; \
    echo '文字列3'; \
) > /tmp/test2

他の方法としては、echoコマンドの-nオプションを使って以下のように書けばよいみたいです(dash)。Ubuntu Linuxなど。

RUN echo -n "文字列1\n\
文字列2\n\
文字列3\n"

RUN echo -n '文字列1\n\
文字列2\n\
文字列3\n'

もしくは、こちら(bash、ash)。CentOSやAlpine Linuxなど。

RUN echo -n $'文字列1\n\
文字列2\n\
文字列3\n'

bashやashの場合は、$が必要で、クォートは'(シングルクォート)である必要があります。"(ダブルクォート)ではうまく
いきません。

echoの-nオプションは、\nのような\の部分をエスケープとして解釈してくれるようになります。

enable interpretation of backslash escapes

echo(1): line of text - Linux man page

If -e is in effect, the following sequences are recognized:

\\
backslash
\a
alert (BEL)
\b
backspace
\c
produce no further output
\e
escape
\f
form feed
\n
new line
\r
carriage return
\t
horizontal tab
\v
vertical tab
\0NNN
byte with octal value NNN (1 to 3 digits)
\xHH
byte with hexadecimal value HH (1 to 2 digits)

-nの有無で、こういう差が出ます。

$ echo '\n'
\n
$ echo -e '\n'

つまり、\n\で改行と継続行です。

ちなみに、クォートの閉じの前を\にするとエラーになります。

RUN echo -n "文字列1\n\
文字列2\n\
文字列3\n\"
/bin/sh: 1: Syntax error: Unterminated quoted string

最後の\を削除するか、以下のように改行を加えましょう。

RUN echo -n "文字列1\n\
文字列2\n\
文字列3\n\
"

お題

複数行の文字列、ということで。今回は以下の内容を複数行の文字列で記述して、/root/.bashrcに追記するということを
やってみます。

alias helloworld="echo 'Hello World!!'"
alias greeting="echo 'Welcome!!'"

### もしくは

alias helloworld='echo "Hello World!!"'
alias greeting='echo "Welcome!!"'

グルーピング(Compound Commands)を使った例

最初に、結果を。ここに書いたものをecho -nで試行錯誤するのが、後ろに続きます。

RUN { \
    echo ''; \
    echo "alias helloworld='echo "'"'"Hello World!!"'"'"'"; \
    echo "alias greeting='echo "'"'"Welcome!!"'"'"'" ; \
    echo; \
} >> /root/.bashrc

変数展開を使った例。

ARG message1='Hello World!!'
ARG message2='Welcome!!'

RUN { \
    echo ''; \
    echo "alias helloworld='echo "'"'"${message1}"'"'"'"; \
    echo "alias greeting='echo "'"'"${message2}"'"'"'" ; \
    echo; \
} >> /root/.bashrc

結果は、どちらも以下になります。

# tail -n 6 /root/.bashrc  
#    . /etc/bash_completion
#fi

alias helloworld='echo "Hello World!!"'
alias greeting='echo "Welcome!!"'

Ubuntu Linux 20.04 LTS、CentOS 8、Alpine Linux 3.13(.bashrcはないですが)のいずれでも同じことができます。

このあとは、echo -nを使って頑張ることに興味がある人は読んでみてください。

Ubuntu Linuxで試す

Ubuntu Linux 20.04 LTSのイメージを使って、いろんなバリエーションでやってみましょう。

Dockerfile

FROM ubuntu:20.04

RUN echo -n '\n\
alias helloworld="echo '"'"'Hello World!!'"'"'"\n\
alias greeting="echo '"'"'Welcome!!'"'"'"\n\
\n\
' >> /root/.bashrc

いろいろくっついていますが、'で文字列を開始した中に'を入れようと頑張っているだけです。

alias helloworld="echo '"'"'Hello World!!'"'"'"\n\
alias greeting="echo '"'"'Welcome!!'"'"'"\n\

Dockerイメージをビルド。

$ docker image build -t kazuhira/ubuntu:20.04 .

実行。

$ docker container run -it --rm kazuhira/ubuntu:20.04
root@710cc3521c1a:/# 

確認。

# tail -n 6 /root/.bashrc  
#    . /etc/bash_completion
#fi

alias helloworld="echo 'Hello World!!'"
alias greeting="echo 'Welcome!!'"

OKですね。

# helloworld
Hello World!!
# greeting
Welcome!!

クォートを"にしてみます。

RUN echo -n "\n\
alias helloworld='echo "'"'"Hello World!!"'"'"'\n\
alias greeting='echo "'"'"Welcome!!"'"'"'\n\
\n\
" >> /root/.bashrc

確認。

# tail -n 6 /root/.bashrc
#    . /etc/bash_completion
#fi

alias helloworld='echo "Hello World!!"'
alias greeting='echo "Welcome!!"'

aliasを使った実行結果は同じなので、省略。

'"の差は想像に難くないですが、変数展開ですね。

FROM ubuntu:20.04

ARG message1='Hello World!!'
ARG message2='Welcome!!'

RUN echo -n '\n\
alias helloworld="echo ${message1}"\n\
alias greeting="echo ${message2}"\n\
\n\
' >> /root/.bashrc

RUN echo -n "\n\
alias helloworld='echo ${message1}'\n\
alias greeting='echo ${message2}'\n\
\n\
" >> /root/.bashrc

結果。

# tail -n 10 /root/.bashrc
#    . /etc/bash_completion
#fi

alias helloworld="echo ${message1}"
alias greeting="echo ${message2}"


alias helloworld='echo Hello World!!'
alias greeting='echo Welcome!!'

CentOS、Alpine Linux

続いて、CentOS 8で試してみましょう。

Dockerfile

FROM centos:8.3.2011

RUN echo -n $'\n\
alias helloworld="echo Hello World!!"\n\
alias greeting="echo Welcome!!"\n\
\n\
' >> /root/.bashrc

Ubuntu Linuxの時のように、'の中に'を含めるのはちょっとムリそうでした…。

Dockerイメージのビルド。

$ docker image build -t kazuhira/centos:8.3.2011 .

確認。

# tail -n 6 /root/.bashrc 
    . /etc/bashrc
fi

alias helloworld="echo Hello World!!"
alias greeting="echo Welcome!!"

aliasの実行結果は割愛します。

クォートが'しか使えないので、変数を含めた場合は

FROM centos:8.3.2011

ARG message1='Hello World!!'
ARG message2='Welcome!!'

RUN echo -n $'\n\
alias helloworld="echo ${message1}"\n\
alias greeting="echo ${message2}"\n\
\n\
' >> /root/.bashrc

展開されずにそのまま残ってしまいます。

# tail -n 6 /root/.bashrc
    . /etc/bashrc
fi

alias helloworld="echo ${message1}"
alias greeting="echo ${message2}"

最後は、Alpine Linux 3.13。Alpine Linuxには.bashrcがないので、適当にファイルに出力して確認。

Dockerfile

FROM alpine:3.13.5

ARG message1='Hello World!!'
ARG message2='Welcome!!'

RUN echo -n $'\n\
alias helloworld="echo Hello World!!"\n\
alias greeting="echo Welcome!!"\n\
\n\
' >> /tmp/test

RUN echo -n $'\n\
alias helloworld="echo ${message1}"\n\
alias greeting="echo ${message2}"\n\
\n\
' >> /tmp/test

ビルド。

$ docker image build -t kazuhira/alpine:3.13.5 .

確認。

$ docker container run -it --rm kazuhira/alpine:3.13.5 cat /tmp/test

alias helloworld="echo Hello World!!"
alias greeting="echo Welcome!!"


alias helloworld="echo ${message1}"
alias greeting="echo ${message2}"

CentOS 8と同じですね。

とまあ、echo -nを使って頑張って複数行の文字列にした割には、グルーピング(Compound Commands)に比べると
報われないので。

素直にいきましょう。