Goで「配列のスライス」を「スライスのスライス」に変換する時は少し注意が必要
久しぶりにGoでハマった。下記のようなコードで。
The Go Playground
チェーン業界外の人のために注釈を付けておくと、本コードのcommon.Hash
はtype Hash [32]byte
で定義されている配列です。
ループ変数(の一部)が配列である時に、これをスライシングするとループ変数を指すスライスが出来てしまい、期待とは違った動作になる、というお話。
じゃあなぜBytes()
を使うと回避できるのかというと、こいつは値レシーバなのでループ変数のコピーをスライシングする形になるからです。
ただの便利メソッドじゃなかったのね...必ず使うようにします。
追記: ていうか、下記のやつの派生パターンに過ぎないのか。
How to "go get" from private repo via https
The 2nd way in the following comment.
cmd/go: github private repos require special configuration · Issue #26134 · golang/go · GitHub
$ cd ~ $ touch .netrc $ chmod 600 .netrc $ vim .netrc machine github.com login YOUR_NAME password YOUR_API_TOKEN
How to install yarn via pacman without node
Background
I decided to install NodeJS not using pacman but using a version manager (nvm at the moment).
However, according to Yarn official, I should install Yarn itself via an OS package manager for several reasons. https://yarnpkg.com/en/docs/install#alternatives-stable
At last, I have to install yarn via pacman skipping its only dependency, nodejs.
Solution
pacman -S --assume-installed nodejs yarn
Dell XPS13(9370)でSATA ModeをRAID OnからAHCIへ変更する時の注意
きっかけ
Dell XPS13(9370)を購入した。
Linuxで使うが、BIOS更新とかファーム更新のためにWindowsが必要になるかもなので、Dual Bootにすることにした。
Linuxをインストールして動かすには、UEFI設定画面に入って(POSTでF2連打)、SATA ModeをAHCIに変更する必要がある。
https://wiki.archlinux.org/index.php/Dell_XPS_13_(9370)#UEFI
が、これを単に実行したところ、Windowsが起動せず自動再起動するようになった。
とりあえずSATA ModeをRAID Onに戻して、Windowsが起動する状態には復旧できた。
しかし前述の通りSATA ModeはAHCIにしなきゃいけないし、Windowsも起動できないと困る。
どうすればいい?
対処
Windowsを一度セーフモードで起動すると、以降は問題なくなるらしい。
https://qiita.com/ak1t0/items/e7d574ae071143bbf10a
そこでまず、以下の手順に従ってセーフモードで起動するように設定してから再起動する。
https://support.microsoft.com/ja-jp/help/12376/windows-10-start-your-pc-in-safe-mode
(Win+R => msconfig => 「ブート」タブ => 「セーフブート」をオン)
UEFI設定画面に入り、SATA Modeを変更(RAID On -> AHCI)し、保存して抜ける。
Windows10がセーフモードで起動するので、セーフモードをオフに設定して、再起動する。
以降は問題なし!!!
追記
LVFSという制度があって、DellはLinux向けのfirmware updateを供給してくれているようだ。
但し、ここに協力的なメーカーはまだ少数派のように見える。
例えば、今回のXPS13のSSDはSamsung PM981だったのだが、Samsungはまだfirmwareをここにアップした実績は無いようだ。
というわけでWindows温存(Dual Boot)の方針が無難のようだ。
ALLOC_FAIRフラグについて
概要
Linuxカーネルのメモリページ割り当てに関するメモ。
ALLOC_FAIR
というフラグについて説明する。
ページ割り当て処理
関数冒頭のコメントで This is the 'heart' of the zoned buddy allocator.
と書かれている通り、ページ割り当て処理の中心は関数 __alloc_pages_nodemask()
が担っている。
__alloc_pages_nodemask()
の処理はざっくり言うと以下の通りである。
- First allocation attempt
- 関数
get_page_from_freelist()
でページ割り当てを試みる。ALLOC_FAIR
が指定されるのはこちら(ここだけ)。
- 関数
- Slow path
- First allocation attemptで失敗した場合、関数
__alloc_pages_slowpath()
でページ割り当てを試みる。
- First allocation attemptで失敗した場合、関数
First allocation attempt
First allocation attemptの際に、割り当て条件として指定されるフラグの一つが、 ALLOC_FAIR
である。
First allocation attemptの処理をbreak downすると以下の通りである。
- ローカルノードでの割り当て試行
- zonelist(説明割愛)で指定されたzone(説明割愛)の内、呼び出し元CPUと同じNUMAノード(ローカルノード)に属するzoneからの割り当てを試行する。
- 全ノードでの割り当て試行
- zonelistで指定された全zoneについて、zonelistの登録順序で、再度割り当てを試行する。
このローカルノードでの割り当て試行を行う際、フラグ ALLOC_FAIR
が指定されていると、1つのzoneがlow watermarkに達するまで消費してから次のzoneへ進む「のではなく」、各zoneにおいて一定量 x
(= high_wmark_pages(zone) - low_wmark_pages(zone)
) で表される量を割り当てたら、もう次のzoneへ進む、というロジックで割り当て試行処理が行われる。
ローカルノードに属するzone群でのページ消費負担を フェア
に分散するロジックと理解できる。
他方、全ノードでの割り当て試行では、単純にzonelist順に一つのzoneがlow watermarkに達するまで消費して、次のzoneへ進む、というロジックとなる。
ちなみにローカルノードでの割り当て試行において、全てのzoneでの割当量が一旦 x
に達すると、各zoneでいくら割り当てたかのカウンタがリセットされ、再び各zoneにおいて x
までは割り当て可能となる。
JJUG CCC 2016 Fall感想
概要
JJUG CCC 2016 Fallに参加してきました。とても有意義で、幾つかのアクションの起点となりそう。 但し大遅刻で着いたのは昼、少し風邪気味で早退、今回聴講した講演は以下のみです。
- Spring超入門-Springを1年半使ってみて
- Java + spring-boot で書く! LINE BOT ライブコーディング。
- Spring CloudでDDD的なマイクロサービスを作ってみる
- JAX-RS REST Client で Cognitive Service や Excel を操作しよう
- Spring Cloudアプリケーションの開発にDockerを活用し、Kubernetes上にデプロイするまで
一つ一つ感想だけ書きます。内容は紹介しません(できない、とも言う)。
Spring超入門
Spring/Spring Bootについての入門的な内容でした。 短めのセッションなので、コードがっつりではなく概念・用語の簡単な紹介。 発表は分かりやすく、内容も盛り込み過ぎない感じで、気楽に聞けました。 Bootがっつり使ってるわけじゃないから、ところどころ知らない話もあって有意義でした。
Java + spring-boot で書く! LINE BOT ライブコーディング。
Spring Bootによってhello world的なLINE BOTを作る流れを紹介するライブコーディング。 BOT自体に特段の捻りはなかったと記憶しているけど、ライブコーディングはスムースでよく準備されてました(4回予行練習したとか)。 Spring BootでLINE BOT作るのが簡単だということが分かったし、自分でも作ろうと思ったので、個人的には刺激的で有意義でした。
Spring CloudでDDD的なマイクロサービスを作ってみる
DDDは仕事でもいつかガッツリやってやりたいと思いつつ、試みつつ、なかなか忙しさに流されてゴールできてない感じ。 そんなわけでこの発表聴いてみたけど、前半はDDDの基本概念を平易に説明する内容が中心で、分かりやすかったけど割と知ってる内容でもあり疲れてしまい、集中力が切れて、後半が頭に入りませんでした(弱い)。
JAX-RS REST Client で Cognitive Service や Excel を操作しよう
冒頭の、スピーカー寺田さんの、Cognitive Serviceは世界をより良くできるテクノロジー、というお話には考えさせられました。 そういう熱い思いを持ってる人がいるということと、一方で、ウチの仕事は世界を別に良くするものではないよなー、ってのと。 技術内容については、JAX-RS REST Clientというのがあるんだなー、REST叩く時は使ってみよう、Cognitive Serviceとか、ML系のクラウドサービス、色々組み合わせて遊びたいなー、などと思いました(小並感)。でもこれはこれでモチベ貰えて有意義でした。
Spring Cloudアプリケーションの開発にDockerを活用し、Kubernetes上にデプロイするまで
盛りだくさんの内容で、むしろ個々については詳しく理解できなかったんですが、Spring Cloudってライブラリの名前であってクラウド基盤ではないということが分かったのと(低レベルw でも使ってみようと思えたので良かった)、k8sは一度試してみようというモチベが得られて、有意義でした。
アクションアイテム
最後に、今回の聴講をきっかけとして、直近でやろうと思ったことを簡単に。
- Spring Boot使ってLINE BOT作る
- JAX-RS REST Client試す
- Cognitive Service、とは限らないけど、ML系のクラウドサービス使って遊ぶ
- Spring Cloud試す
- Kubernetes試す
個別にやるのもいいけど、出来れば全部を魔合体して何か作りたいw 一石五鳥を目指して。
Golang覚書
概要
make(chan ...)でキャパを指定しないとunbufferedになる
- キャパを指定しないと、適度なデフォルト値でもなく、1でもなく、0なので。
- 同期処理用ということ。非同期プログラミングにおいては、むしろマイナー用途かも。
channelのバッファのキャパは送信側が管理すること
- 溢れるかどうかは、受信側ではどうしようもないので、送信側がなんとかする。
- 原則として、channelが溢れたらchannelを殺す(closeする)(しかない)。
- channelのバッファはそのつもりで十分確保すること。
送信側はchannel溢れを直ちに検知できるよう(間違ってもブロックなどせぬよう)、送信時は原則として以下のイディオムを使う。
select { case chChild <- value: default: /* Kill the child */ }
channelは原則として送信側が閉じること
- closed channelに送信すると実行時例外が発生してしまうので、送信側でclosedか否かの管理をする。
- 受信側では例外など出ずに自然にcloseをハンドリングできる。
送信側だと、panic-recoverを用いた以下のイディオムでハンドリングは可能。もしどうしても必要なら。原則として避ける。
func sendToUnstableChannel() { defer func() { recover() } ch <- v }
closeはブロックしない
- cap=10のchanに10個のデータが既に詰まってる状況だと、更にsendしようとするとブロックするが、closeはブロックしない。
- closeは非同期処理において安全(ブロックしない)処理だと言える。
channel受信は原則として、特別形式の受信式もしくはfor-range受信で行うこと
以下のようにすると、chがcloseされた場合、無限busyループするので注意。
Loop: for { select { case v := <-ch: /* Use a received value */ case <-timeout: break Loop } }
正しくは特別形式で受信
for { select { case v, ok := <-ch: if !ok { break Loop } /* Use a received value */ case <-timeout: break Loop } }
もしくはfor-range受信(タイムアウトが設定できないのが難点)
for v := range ch { /* Use a received value */ }
ループの途中でコレクションの要素を削除する場合の注意
mapの場合は
delete文を使えば問題無い
for k, v := range m { if cond(v) { delete(m, k) } }
sliceの場合は
- downwardループを使う
- append式での要素削除は、i以降の要素をshiftするため、i以降の要素に対してループを継続すると、おかしなことになる。
downwardなら問題は起こらない。
for i := len(s) - 1; i >= 0; i-- { if cond(s[i]) { s = append(s[:i], s[i+1:]...) } }
原則としてbusyループは避けること
- Goroutineは任意の箇所でプリエンプト出来る(される)わけではないので、下手をするとCPUを占有する可能性がある。
- よほど理由があり問題無いと分かっているのでなければ、busyループは避ける。
- ループがbusyループになりうるのではないか、気を付ける。