bussorenre Laboratory

hoge piyo foo bar

コンテキストスイッチにどうやって立ち向かうか

「この記事は個人の見解であり、所属する組織の公式見解ではありません」

またこいつは炎上しそうな記事を書いて……と自分で思いましたが、真剣に悩んでいるので人の目に触れる所に置いて意見を貰うことにしました。

本来、コンテキストスイッチとはCPUが複数プロセスを並行して動かしているように見せるために、CPUやメモリの状態を入れ替え差し替えする機能の事を指しますが、この記事では、1人の人間が、文脈の異なるの業務を並行的にこなさなければならない状況で発生する、脳内での業務の切り替え の事を意味します。

前提のお話

コンテキストスイッチにはコストが掛かる

まず、前提として、コンテキストスイッチには、スイッチングコストがかかります。

例えば、1人の人間(P)が100 の業務量をこなせるとします。(単位はまぁ概念的なものなので適当に想像してくれ)

上司は部下にあれこれ仕事を割り振るのですが、その中でも、A-E の業務に着目し、業務量を見積もると、だいたい以下のような感じでした。

A = 70
B = 40
C = 30
D = 20
E = 10

この時、P さんに任せる仕事として最適なのは どういう組み合わせでしょうか?

単純に計算すると、 A + C = 100 だったり、 B + C + D + E = 100 だったりするので、そういう組み合わせが良いと思うかもしれません。

しかし、ここには「人間はコンテキストスイッチングに多少なりとも時間がかかる」という条件が考慮されていません。

例えば、月から金まで働くとして、月火水 は A という業務に着手し、木金は C という業務に着手する。その間に業務の切り替え(コンテキストスイッチ)の発生回数は2回で、(A からC への切り替えと、C からAの切り替え。厳密にはC からA は土日を挟むので発生しないかもしれないがそれはさておき) コンテキストスイッチにだいたい1の業務量を要する。

とすると、A とC のタスクをアサインされている人の業務量は 70 + 30 + 2 = 102 になるわけです。ちょっと残業ですね

では、B + C + D + E = 100 の業務を持つ人が 1回のコンテキストスイッチにかかる業務量を1 として、必ずB, C, D, E の順番で業務を実行していくとどうなるか?というと、業務量は105 になります。

じゃぁ実際に105か?と言われると、そんなことはないです。

例えばBの仕事を実行している時に急遽Eの仕事のクライアントから緊急の電話が掛かってきたりすると、一旦Bの手を止めてEの頭に切り替えなければなりません。要件がすめばまたBに頭を戻せば良いわけですが、この時コンテキストスイッチが2回発生しています。

業務量が更に増え、107 になりました。こうして、残業が増えていくんだなー。

大きな集中力が必要な業務

アートの製作や、複雑なロジックの実装、MVPの検討など、非常に高度な集中力を要する業務というのが存在します。例えば40 の「集中力が必要な業務」の進行中に、だいたい20まで進行したところで、上記のようなクライアントからの緊急の電話等でコンテキストスイッチが発生すると、頭がリセットされて最悪0 からになるかもしれません。0までいかなくても、「で、ええっと何の話をしたんだっけ?ああそうそう」というロスタイムが生じる経験は誰にでもあると思います。同じ議論をやり直しているかもしれない。

集中力が必要な業務中に、コンテキストスイッチが発生しないようにする工夫は必須です。

コンテキストスイッチが発生しまくる現場では、こうした集中力が必要な仕事はなかなか手がつけれません。無理です。

みんな1人月として業務を振ってくる

例えば、A という業務と B という業務、それぞれ別のチームの仕事だったとします。B のチームの人からは、Aのチーム/仕事が見えないので、ついつい P さんに1人月分(=100)の仕事を振ってしまいがちです。

同じことがA でも発生します。例えば、Aチームでは、納期に間に合うか間に合わないかくらいのギリギリなスケジュールで動いていて、「P さん、ちょっとこのチケット持ってもらえませんか?」みたいな会話が発生したりします。まだB に比較的余裕があって「ああ、OK じゃぁA のほう頑張ってくれ」とコミュニケーションできればいいですが、「え、それは困るBもパツってる」とかになってくると、しんどくなります。

そしてコレはただの経験則ですが、だいたいしんどい次期は重複するので、Aがパツってる時にB が余裕ある という事はあんまりありません。パツってる時はだいたい緊急度が高い会話をする必要が多く、それだけコンテキストスイッチも発生し、雪だるま式に業務量が膨張します。

自発的に発生するコンテキストスイッチ

所謂生理現象です。トイレ行きたいとか眠いとか腹減ったとか、そういう生理的欲求による中断の他に、僕は勝手に小さなフラッシュバックと呼んでいるのですが、何か特定の事象を見て、それに興味が奪われたり、忘れていた何かを思い出してしまったりする現象があります。

外部からの割り込み以外にも、自分の中で発生する割り込みも存在します。これも経験則なので根拠微妙ですが、精神的なダメージを負っている時によく発生するイメージがあります。

コンテキストスイッチにどうやって立ち向かうか

そもそも複数業務をアサインしないようにお願いする

上司と相談出来る環境、そして上司がそれを納得して受け入れてくれる環境なら見えてくる選択肢です。 が、だいたい1人の人間に複数種類の業務がアサインされている時はもう上司の時点でパツってるので、この方法が通ることはまずありえません。

もしどうしても、1人の人間に複数タスクをアサインしなきゃいけない時、最も効果的だなって思っているのは 80:20 くらいで、それ以外のアサイン方法でまともなのを知りません。

業務時間を固定する

例えば、80:20 で分割した場合、20のほうの業務を必ず金曜日に行う 等の合意が取れていれば、コンテキストスイッチの発生回数は極端に減ります。 さっきも書きましたが、土日が休みな場合コンテキストスイッチは土日に行われるので、101 くらいの業務量で済みます。

割り込みを回避する

特に集中力を要する仕事で有効な方法だと思っていて、誰からも声を掛けられない場所に引きこもり、チャットツールも閉じる というのが有効だと思っています。人が減りだす 20:00 - 24:00 のオフィスであったり、世間が寝静まった1:00 - 5:00 の間に最も集中力が高まることを感じるのもこれかなと思っていて、理由は割り込み回数が極端に少ないからです。

集中する部屋 みたいなのが固定デスクとは別に設けられているオフィスは良いと思うのですが、そうでなかったり、全域フリーアドレス とかになってくるとものごっつしんどいですね。

自分はどうすればいいのか

さて、じゃぁ今自分がどうなっているかというと、A+B+C+D+E= 120 くらいになっています(限界量100) コンテキストスイッチは頻繁に発生するので、実感値としては200くらいです

さて、どうすれば良いのか。

そもそも複数業務をアサインさせないようにお願いする は既に実行済みですが、「A+B+C+D にはなるかも」って感じで、うーんそれでもまだ4あるのか辛いという状態。次に有効かなと思っているのが、「じゃぁ月曜日、火曜日休みで 土日出社という感じのフレックス制度になりませんか?」 という相談。これは明日試してみるが、多分、人事や総務的な観点からお断りされると思っていて厳しい。

自分の中で、業務時間を固定してその時間はそれ以外のことを一切しない という強い意志表示をする というのが、多分自分に出来る唯一のカードだな。と思っていて、今全力で冷徹になろうとしている。(スケジューラーブロックを完全にブロックして会議を打ち込まれるのを防ぐ等。)

他にどうすればいいのか。

「お前の頭が悪い」と言われたら「すみません」としか言えません。自分の頭の悪さはコントロール出来ないので………。お前が無能だからそういうアサインのされ方をされるんじゃないか?と言われたらぐうの音も出ませんが、無能が加速するのでできれば辞めてほしい所……。

他の人がどういう感じで仕事しているのか、えるちゃん以上に私、気になってます。

どうしてwebフレームワークに苦手意識を感じるのか

創作物(ドラマやアニメ)において、「この物語はフィクションです。登場する人物・団体・名称等は架空であり、実在のものとは関係ありません。」という注意書きが必ず記載されるのと同様に、個人ブログにおいても、必ず「この記事は個人の見解であり、所属する組織の公式見解ではありません」という但し書きを書く必要が出てきたので、鬱陶しいですが毎回書きます。ご容赦を。

「この記事は個人の見解であり、所属する組織の公式見解ではありません」

私は、例えばRuby on Rails のような強力なwebフレームワークが非常に苦手で、なんでこんなにやりづらいんだろう?と思っていた。フレームワークを入れたは良いが、実現したいソフトウェアをいざ実装するぞとなったら、まるで手が動かなくなる事が多い。その原因がさっぱり見えていなかったのだがようやく分かってきたので書く。

1. そもそもちゃんとドメインモデルを構築できていない。

フレームワークどうこういう以前の問題として、私個人のモデル設計力が低い。 とにかくコレに尽きると思う。特に学生の時は深いことをさっぱり考えずに「とりあえず実装するか」とコードをしっちゃかめっちゃかに書きなぐって「とりあえず動いたからリリースしようぜ」というのが許された。(許されたわけではないと思うが、基本的に設計から実装からリリースまで全部1人だったし、趣味の園長のようなプロダクトなので、困るのは自分くらいだった)

なので、オブジェクト間の依存関係とか全然考慮されておらず(いや、多少は考慮していたが、完成度の低いモデルで)酷い設計/実装だった

2. フレームワークの学習コストの見積もりが甘い

Zendにせよ、RoR にせよ、Django にせよ、所謂「重い」と言われるフレームワークはとにかく、何人もの優秀な技術者の知恵と努力が何年も積み重なったもので、一介の素人がちょっとドキュメントを読んだだけで理解出来るものではない。(ちょっとドキュメントを読んだだけで全てが分かるのなら、世界は優秀な技術者を必要としない)

とにかく学習コストが高い。学習コストが高いと嘆いているうちは、まだ自分の道具になっていないという事だし、道具に振り回されて疲れ果てることになる。既に道具として使いこなしている先輩方から「これはこうしたほうがいいんじゃね?」と言われても、何を言われているのかわからないので、反論が出来ない。

また、上述の通り、ドメインモデルがしっかりしていないので、「このフレームワークではこうするんだ」というフレームワークの思想が優位に立ち、本当にすべき実装が見えていない。というのもある。

という事を青DDD, 赤DDDを読みながら思った。

エリック・エヴァンスのドメイン駆動設計

実践ドメイン駆動設計

青DDD は読むの物凄くしんどかった。オススメしない。赤DDD は DDD についてある程度知識がある前提で進むので、青DDD の要約スライドを見て挑むのが一番学習効率が良さそう。

kotlin を始める

最近、Android 界隈でKotlin という言語が流行ってるらしい。というくらいには老害になってきたのであるが、とにかくkotlin を始める。

Android で正式に採用されたらしい。という情報だけだったら多分さわりもしなかったんだけど、どうやらこいつは、JVM 上で動くだけでなく、js, native コードにコンパイルしてくれるらしい。それ凄くない???

開発元はJetBrains. Intelli J Idea とか作ってるすごい所。 ということで、今回はkotlin はIntelliJ Idea で触ることにした。

多分すごい爆速でコード書けそうめう。

公式 : https://kotlinlang.org/ OSS : https://github.com/JetBrains/kotlin

kotlin はネイティブコードを生成できる(らしい)が、kotlinを動作させるには、JVM環境が必要。最新版は 1.2 @ 2017/12/20時点

じゃぁ、取り急ぎハローワールドから。 jvm で実行する、js で実行する、ネイティブで実行する、 の3つを実装する。(多分コードは変わらないと思うけど)

kotlin ファイルの拡張子は、kt。小室哲哉かよって思った私はもう老害。飛行機の速度かよって思ったやつはメーデー

fun main(args: Array<String>) {
    println("Hello Kotolin!!")
}

func でもfunction でもなく、fun なのか。これはちょっと嫌だなー。と正直に思った。func か、せめてfn にしてほしい感ある。 そんな事はどうでもよく。

ここで、まず見るべきは、クラス定義がない。これは一体何クラスに属するメソッドなんだ??

kotlin が吐いた生成物を見ると、Appkt.class(App.kt という名前で生成した)になっていた。なるほど。

kotlin native を使う

参考:https://github.com/JetBrains/kotlin-native

書いてあるとおりにする。

最終的には、こんな感じのコマンドを叩いて、CとかGo みたいな実行可能ファイルが生成される。 kotlinc hello.kt -o hello -opt

うーーん、kotlin でネイティブかけるのはすごい。 私個人はネイティブ原理主義者なので、ネイティブに変換できるだけで株は大上がりですね…。

Gem を作る

今更ながらRuby について真剣にやり直そうと思う。理由はまぁ色々有るのだけど。。。どうもRuby は苦手だ。

さて、gem を作る。今までとりあえずgem install していたけど、gem ってそもそもどうなっているんだろうか??

bundle gem gemname

これで雛形を生成することが出来る。おそらくデフォルトのままで問題ないと思う。生成されるgem のファイル構成は以下のようになっている。

├── CODE_OF_CONDUCT.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── bin
│   ├── console
│   └── setup
├── lib
│   ├── testgem
│   │   └── version.rb
│   └── testgem.rb
├── spec
│   ├── spec_helper.rb
│   └── testgem_spec.rb
└── testgem.gemspec

4 directories, 12 files

testgem と言うのは私が書いた名前なので、各自の環境で変えていただきたい。ruby でrequire が呼ばれた時、このlig/testgem.rb が呼ばれる。また、このgem を呼び出すにはどうしたら良いのか。

このディレクトリとは別に、'callgem'というディレクトリを作成し、Gemfile を作成する。ローカルのgem を呼び出す時は、path属性を指定してあげる。具体的には以下のようになる。

gem 'testgem', :path => '../testgem'

さて、これでbundle install しようとするとエラーが起こる。どうやらspec ファイルがまだvalid じゃない(テストが通ってない)と言われるので、コレをアレコレ書く所から始めようと思う。

早速gemspec ファイルを開けるよく見ると中にToDo という文字列が見つかるので、そのへんを適当に改良する。以下のようになった。

# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'testgem/version'

Gem::Specification.new do |spec|
  spec.name          = "testgem"
  spec.version       = Testgem::VERSION
  spec.authors       = ["bussorenre laboratory"]
  spec.email         = ["mailaddress@bussorenre.com"]

  spec.summary       = %q{おためしで作ったgem}
  spec.description   = %q{こんなんでいいのか}
  spec.homepage      = "http://localhost:3000"
  spec.license       = "MIT"

  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
  # to allow pushing to a single host or delete this section to allow pushing to any host.
  if spec.respond_to?(:metadata)
    spec.metadata['allowed_push_host'] = "'http://mygemserver.com'"
  else
    raise "RubyGems 2.0 or newer is required to protect against " \
      "public gem pushes."
  end

  spec.files         = `git ls-files -z`.split("\x0").reject do |f|
    f.match(%r{^(test|spec|features)/})
  end
  spec.bindir        = "exe"
  spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
  spec.require_paths = ["lib"]

  # ココに依存しているgem を追加する
  spec.add_development_dependency "bundler", "~> 1.14"
  spec.add_development_dependency "rake", "~> 10.0"
  spec.add_development_dependency "rspec", "~> 3.0"
end

本当にToDO のところを書き換えただけです。

さて、肝心のこのgem ですが、ping というメソッドを実装し、呼び出されたらpong と返す。たったそれだけのgemを作成します。

まずspec

require "spec_helper"

RSpec.describe Testgem do
  it "has a version number" do
    expect(Testgem::VERSION).not_to be nil
  end

  it "response pong if ping" do
    expect(Testgem.Ping).to eq('pong')
  end
end

実装

module Testgem
  def self.Ping
    'pong'
  end
end

これで、rspec が通った! というわけで、さっき作ったcallgem から呼び出してみる。

まずはbundle install する。すると問題なくbundle install 出来たので、コードを書く。

require 'testgem'

puts Testgem.Ping

これをbundle exec ruby main.rb で実行すると、無事pong と表示される。

結論

gem が出来た。所謂有名所のgem も基本はこういう原理になっているはずなので、コードリーディング等に役立てていきたい。

サーバーを可視化しよう!その1. cacti

@bussorenre です。

この記事ではcacti にトライした時のメモを残していきます。OS はCent OS 6.6 です。実際に試した環境はsakura VPS 2G プランのインスタンス上です。

また、「私の勝手な主観」が入る事が多いので、「そこは違うで」って指摘していただけるとすごい助かります。

cacti 概要

依存するソフトウェア - Apache web server... データを確認するのに使用 - MySQL...

# rootユーザーに切り替え。
su -

# root ユーザーに切り替わった後
yum install -y cacti mysql-server

# cacti が利用するユーザー、MySQL DB などを準備してあげる。
mysql -u root -p

mysql> CREATE DATABASE cacti;
Query OK, 1 row affected (0.00 sec)
mysql> GRANT ALL PRIVILEGES ON cacti.* TO cacti@localhost IDENTIFIED BY 'password';
Query OK, 0 rows affected (0.00 sec)
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)
mysql>bye;

# cacti にdbの設定を記述 (emacs でも)
vi /etc/cacti/db.php
$database_type = "mysql"; //使用するデータベース種別. MYSQL以外も使えるっぽい。
$database_default = "cacti"; 
$database_hostname = "localhost"; 
$database_username = "cacti"; // データベース接続に使用するユーザー名
$database_password = "password"; // データベース接続に使用するパスワード
$database_port = "3306"; // データベース接続に使用するポート番号
$database_ssl = false;
:wq

# 初期データベースの作成(0.8.8b はcacti のバージョン。)
mysql -u cacti -p cacti < /usr/share/doc/cacti-0.8.8b/cacti.sql

# cacti をapache から確認出来るように設定をいじる(特にいじることがなかった\(^o^)/)
vi /etc/httpd/conf.d/cacti.conf
:wq

# crond で定期実行
vi /etc/cron.d/cacti (# 行頭コメントを外して有効化)
*/5 * * * * cacti /usr/bin/php /usr/share/cacti/poller.php > /dev/null 2>&1

:wq

service crond restart

ここまでできたらhttp://localhost/cacti/install/ にアクセスして初期設定を行う。

個人的に感じたこと * 何度も見たことのある憧れの画面で感動\(^o^)/ * 監視用のマシンを1代別途用意して、そこにmySQL を用意して監視するという選択肢は考えたけど、それならsensu のほうが今どきでよさ気かもしれへんって思った。

参考にした記事 * モニタリングツール「Cacti」でのリソース監視 - さくらのナレッジ

サーバーの状態を可視化しよう!その0. 概要

こんばんは。夏!暑い!けどオフィス寒い!社畜なんてやってる場合じゃねええ!@bussorenre です。

さて、仕事で使うサーバーも趣味(minecraft)で使うサーバーも、「もっと楽に状態管理したいなー」って思ったので、例によって今回自分用に記事を書き残します。勉強中なので「これ違うで」「こんなんもあるで」みたいな事があれば共有していただけると幸いです。

現状の選択肢

らしいというのは、詳しい方の一言コメントです。

Graphite

今度書く

Cacti

今日書く

Munin

今度書く

Sensu

今度書く