Windows10でSage(SageMath)を使う (Bash on Ubuntu on Windows編)

いろいろと試していた結果やっと環境が固まってうまく動くようになったのでその記録として記事を書いてみたいと思います。以降の自分もいろいろな用途でSageを使うことになると思いますし、それ以外の人でこれを使って何かする人(後で解説)もかなり有用だと思います。

 

Sage(SageMath)とは

公式サイトを読んで見るなりWikipediaを調べるなりすればよいと思いますが数学の幅広い処理を扱うソフトウェアで計算機代数などを使用することができます。今現在の主な用途としては数値計算や計算機代数など計算機を使う数学において研究目的で使用されます。この場合ですと数学系の課題研究で担当の教授から紹介されたり代数学の論文でこのような計算方法で行います、という場合にSageのコマンドで紹介されていることがあり以降自分の研究を進めていく上で必要となることが多いようです。

もう一つ面白い使い方としては線形代数や数式処理も行うことができるため高校レベルまでのちょっと複雑な数式について数学の先生が答えから逆算して問題を作成したり、テスト問題を考えたときに解答が正しいかどうかをチェックしたり…という用途に使用できるのではないかと考えています。逆に考えると高校生が横着して数学の宿題についてSageで出した解答をそのまま持って行く、なんてことも考えそうな気もしています。高校生が使うのであれば途中式を書いて計算はしてみたけれど答えを提出する前に確認したい、という用途では使ってよいのでは?と考えていますがどうなのでしょうね。

なお、現在の正式な名称はSageMathと呼ぶようです。インストールするパッケージもsagemathという名前で書かれているようですので気をつけていきましょう。

 

Bash on Ubuntu on Windowsの環境を整備する

Windows10において2016年8月に公開されたAnniversary Updateですが、これの大きな更新の一つとしてBashをWindows環境で利用できるようになった、というものがあります。BashはLinux環境における代表的なシェル(この場合はOSとユーザーをつなぐ役割ととればよい)で、これのおかげで一部のLinuxのプログラムを余計な変換なしにWindows上で扱うことができるようになりました。昔からLinuxとWindowsを併用してきたユーザーから見ればかなりうれしい更新です。今回はこれを使うことにします。

Bash on Ubuntu on Windowsの環境を整備するのはいろいろなサイトで紹介されているので詳しい点はそちらに譲るとして簡単に説明すると作業は以下になります。

  1. 画面左下にあるWindowsのロゴを右クリックしてメニューより[コントロールパネル]を選択。[プログラム]=>[Windowsの機能の有効化または無効化]より[Windows Subsystem for Linux (Beta)]にチェックをして有効化する。(更新により再起動が必要になることもあり)
  2. 設定の[更新とセキュリティ]から[開発者向け]を選択してアプリの受け入れに関して[開発者モード]を選択する。
  3. 画面左下にあるWindowsのロゴを右クリックしてメニューより[コマンドプロンプト]を選択。プロンプト上で
     > bash 

    (先頭の>はプロンプト状態を表すもので実際には入力しない)と入力する。以下開発者のみの機能であることに同意した後Bash上のユーザー名、パスワードを入力して環境構築が行われる。

インストールにはしばらく時間がかかりますのでのんびり待ちましょう。環境の構築が完了するとスタートメニューにBash on Ubuntu on Windowsのショートカットが作成され、そのショートカットで起動すると上記のユーザーでログインした環境から処理が始まる、という状態になります。

ちなみにBash on Ubuntu on WindowsのインストールによってBash上で動くプログラムをgccなどのコンパイラによって作成することもできるのでC言語での簡単なプログラムも作ることができます。こういう目的で環境を構築するのもありかもしれません。

ただし、Bash on Ubuntu on Windowsの環境はWindows本体とは異なり自動では更新されません。そのため環境を構築していろいろなことをやるのであれば定期的にアップデートを確認して環境を更新していく必要が出てきます。環境の更新はインターネットへの接続が確保できている状態で

 $ sudo apt-get update 

というコマンド($はターミナル状態を示すもので実際には入力しない)で行います。また、この環境内ではマシン名の名前解決ということでsudoを使う場合などで変な警告を表示されることがあるので/etc/hostsに自分のマシン名(起動直後の表示では[(ユーザー名)@(マシン名):~$]となっているのでそれを目安に)をviなどで先頭行の127.0.0.1の後ろに書き加えてしまいましょう。

以降はBash on Ubuntu on Windowsを起動した場合はその窓のターミナルはUbuntuのコマンドラインだと思って作業することがある程度可能です。すべてのソフトウェアがあるわけではないのでapt-getで取得できないものはコンパイルがうまくできればなんとかなるかも、というレベルです。そのあたりは自己責任で。

 

XmingでGUIウィンドウが使えるようにする

SageMath本体には直接影響はしないのですがこれが使えるとグラフなどを見せるときに非常に楽になります。特にSageのコマンドライン上から描画命令を発行したときにXmingが正常に動いていればxdg-openコマンドを経由して(内部の関連付けにもよりますが)ImageMagickが呼び出されて最終的にXmingにより画面上にウィンドウが表示されます。これがないとSageからの画像表示を行いたい場合に一度ファイルへ出力した後それをWindows領域から見やすい場所にコピーして画像ビューワを使う、という手順となりかなり面倒になります。ちなみにこれを入れておけばgnuplotも表示できるのでその意味でも数学の授業で扱うことを考えたり…ということもあります。

 

インストールの手順としてはBash on Ubuntu on Windows + XmingによるGUI – Qiitaが詳しいのでそちらを見てもらえばよいですが、簡単に手順を書くと

  1. 公式サイトよりXming-6-9-0-31-setup.exeとXming-fonts-7-7-0-10-setup.exe(記事を書いている時点)をダウンロードしインストール
  2. Xmingを起動する。以下一度シャットダウン処理やXmingを終了させてしまった場合は表示させる処理の前にXmingを起動させること。
  3. 環境変数DISPLAYにXmingのサーバー値を設定する。通常は0.0なので
     $ export DISPLAY=localhost:0.0 

    というコマンドを実行する。必要であれば.bashrcの終端にこの命令を記述しておくと状態が登録されるので楽になる。なお、.bashrcに登録されている場合はウィンドウを開いた後でXmingを起動させてもうまく表示されることがある。サーバー値が間違っていれば失敗するのでそこには注意すること。

となります。ちなみに参考にしたページではxeyesを使ったチェックをやっていますがこちらの場合はこの後SageMathをインストールしてチェックするのでそのタイミングでやってもよいと思います。(うまく表示されない場合の切り分けのために先にテストしてもよい)また、LXDEとVNCを併用してGUI環境を作り出す方法もありますが今回そこまでする必要はないと思いますのでこういう方法を使っていきます。

 

SageMathをインストール

SageMathの場合は本体がPythonで記述されているため今回の場合はほとんどの機能をインストールすることができます。ただ、Ubuntuの公式サイトからパッケージを取得する方法ですとうまくいかない場合があるのでリポジトリから作業を行います。こちらは以下のコマンドを実行することでインストールすることができます。

$ sudo apt-add-repository ppa:aims/sagemath
$ sudo apt-get update
$ sudo apt-get install sagemath-upstream-binary

注意点としては容量がかなり大きい(5GB前後ある)のでWindowsの本体ドライブにそれだけの空き容量が必要であること、またかなりのサイズのデータをダウンロードするため時間がかかることです。数時間くらいかかることもありますので気長に待ちましょう。

 

SageMathの初期環境を構築する

インストールが完了すればSageMathを起動させて初期環境を構築します。といっても一度目の起動の場合は勝手に環境を構築してくれるのでそれを待つだけです。つまり、

$ sage

と入力すればOKです。なお、完全なLinux上で動作している訳ではないためSageMathを起動させるたびにRuntimeWarningが出てしまいますがそれは無視しても問題ないと思います。

 

SageMathの画像表示チェック

最後に画像が表示されるかどうかを確認して完了です。適当にグラフをplotしてみればよいわけですが、わかりやすくcircle命令を使います。例えば

 sage> circle((0,0),1)

としてグラフとして中心(0,0)、半径1の円が描かれたグラフが表示されるかどうかを確認します。デフォルトであればおそらくImageMagickが起動してXming経由で画像ファイルを表示します。コマンド実行後しばらくたっても画像が表示されないときはターミナル(SageおよびBash)をいったん終了させて再度起動させてみてください。それでもうまくいかないときはWindows本体を再起動させてXmingを起動させた上でSageMathを起動させて確認してみてください。ここまでやってうまくいかないときはXmingの表示テストにより単体で動作することを確認する必要があると思います。もしかするとeogをインストールしておく必要があるかもしれませんが…。

 

SageMathで数学Ⅰや数学Ⅱにありそうな問題を解いてみる

ここからは遊びです。例えば代数学の問題(数と式など)をやってみます。

Q1. を因数分解せよ。

A1. SageMathで以下のように入力して答えを得る。

sage> a,b,c=var('a b c')
sage> y=a*(b^2+c^2)+b*(c^2+a^2)+c*(a^2+b^2)+2*a*b*c
sage> factor(y)
(a + b)*(a + c)*(b + c)

よって解答は(ちょっと並び替えを行うと)

Q2. を計算せよ。

A2. SageMathで以下のように入力して答えを得る。

sage> y=sqrt(3)/(sqrt(3)-sqrt(2))-sqrt(3)/(sqrt(3)+sqrt(2))
sage> y.simplify_full()
2*sqrt(3)*sqrt(2)

よって解答は(根号の数を整理して)

Q3. 方程式を解け。

A3. SageMathで以下のように入力して答えを得る。

sage> y=x^4+x^3+2^x-4
sage> solve(y,x)
[x == 1, x == -2, x == -I*sqrt(2), x == I*sqrt(2)]

よって解答は(iを虚数単位として)

見事に途中式を無視して解答だけ出してしまったパターンとなってしまいました。これで数学の宿題として先生に提出したらほぼ確実にやり直しですね…。なお、記事を書いているときに書いている当の本人が本当にこんなことができるのだ、と妙な感動を起こして笑ってしまったのはおいておきましょう。

上記では問題を解く方向でやりましたが問題を構築するとき(例えば因数で表された状態の式を展開して問題にする)もexpandなどでかなりうまくできます。なお、根号の状態など一部の処理は(A2のように)必ずしも簡単になるとは限りませんので解答が出た後で整理するなどが必要だったりそもそもその式をうまく表現できない場合がありますのでそこまで期待しない方がよいでしょう。もちろん研究にSageMathを使う場合は普通に必要な処理を使っていってください。

 

JOYSOUND MAXの採点を試してみた

JOYSOUND MAXの導入が開始されてからいったいどれだけかかったことか…。今回JOYSOUND MAXでの採点を試す機会があったのでいろいろな曲で採点の状態を試していました。アップデートなどによって状態が異なるかもしれませんのでこの記事を書いている日付前後の話としてください。他の人の考察などは一切読まないで独自考察になっていますのでそのあたりも前提にして下さい。

 

JOYSOUND F1の採点と全然違う…

正直にこれが第一の感想でした。前回JOYSOUND F1で採点したのが記憶が正しければ数ヶ月前という状態なのでもしかするとアップデートで採点の方式がまた変わったのかもしれませんが…。この後でも考察しますがおそらくJOYSOUND MAXで採点した方が得点が高い曲とJOYSOUND F1で採点した方が高い曲とかそういう状態があると思います。さすがに差分を調べるのはかなり難しいですが簡単にわかるものも含めてこれから書いていきます。

ちなみに面倒なので以下でMAXと表記すればJOYSOUND MAXを、F1と表記すればJOYSOUND F1を指すものとしてください。

 

音階判定表示は基本的に二小節分が表示される

F1までだと中央にバーがあってどんな曲でも一定速度でスライドしていったのですがMAXでは「基本的に」二小節分を表示して更新していく感じになったようです。音階一致判定もこの二小節単位でどのくらいだったかを画面左上に表示してくれるので個人的にはスライドより見やすいことからこれに関してはMAXが有利…かと思いきや、曲によってはこれが不利になることもあります。その例として

  • 音の先頭が小節の先頭位置と一致しているため発声すべき音階がその直前まで画面上に表示されない(画面外には表示されている)
  • 曲の内部データによって「二小節」の意味が異なるため曲によってひどく切り替わりが多い曲が存在する

があります。特にこの後者が非常にやっかいです。内部的に「二小節」なので3/4拍子の曲は4/4拍子の曲より移動速度が速くなりますし、体感的にはBPMが似たような曲でも楽譜に起こしたときに半分と見なしている(たとえば180BPMに感じる曲でも内部の楽譜データでは90BPMの曲としている場合がある)ことがあり、この場合体感として四小節表示されているのと同じ状態となります。曲のBPMが変化している場合でも体感的に速くなっているからと言って内部データも速くなるとは限らないのが難しいところです。(実際、遅くなった曲がありました)これがこの後採点で微妙な混乱を起こします。

 

曲の加点要素が一つ増えている

F1まではしゃくりとビブラートだけでしたがMAXではそれにこぶしが追加されたようです。DAM系ではそれにフォールが加わるのですがそれは置いておいて。これのおかげでF1より加点されやすくなっていると考えられます。実際、私が歌った中でも音階評価と安定度評価を除いて上限に張り付いた、という曲がありましたのでこれは入れるのではないか、と思います。加点要素の判定基準は不明ですが…。

 

ロングトーンの判定が変わった様子

というか、どうも音階表示される部分が二小節と決められていることで元々一小節を超えるような判定となっていた音符についてその場合は2拍くらいでカットする、という条件が追加されているようです。それ以外にも表示的には音階表示されていない場所は判定対象に入らないように見えるようになっています。加点要素については範囲外でも判定されたような表示がされていましたが…。

で、これのおかげで何が良くなったか?というと電波ソングなどで妙なラップがある曲、台詞が入っている曲で無理矢理音階判定が入っている場合があったのですがその範囲が大幅に狭くなったわけです。F1まではその変な音階を台詞などを無視して「あ~」と言わないと減点対象となるようですがMAXではその心配がだいぶ減りました。というわけでその手の曲で低い得点となることがだいぶ避けられるようになります。

 

内部データ的に低速の曲の方が得点が出しやすい?

これが今回一番確定できなかった部分です。というのも、逆に今回テストした曲だと内部データが高速な曲で得点が低く出ているケースが多々あったことや、画面表示ではカラオケで歌う場合の判定遅延(通常カラオケでは「曲を聴く」=>「リズムに合わせて声を出す(微妙に遅れる)」=>「マイクで音をとる」=>「音階判定の演算を行う(微妙に遅れる)」というシーケンスのため音階の開始タイミングと発声を処理するタイミングが微妙にずれるのが普通であり、それをカバーする必要があることが多い)が行われていないような感じで高速曲の場合音の出だしで音が出ていないと判断されていた様子がうかがえたからです。これが正しいとするとちょっと難しいことになるのですが、画面表示を無視して歌った場合でそれなりの得点を出しているパターンもあったことから今現在は保留の状態です。

ちなみにあくまで「内部データ的に低速の曲」です。これが実は「基本的に二小節単位」にかかっていて、自分が感じているBPMではなく曲データ上のBPMに依存します。今回歌った中で一番その差がひどかった曲が<Asphodelus>という曲です。とある場所では名曲といわれていますが…(私もかなりいい曲だと思っています)。この曲ですが私の感覚では6/8拍子160BPM(つまりカラオケ表示で3/4拍子換算だと80BPM)だと思っているのですがJOYSOUNDの内部データ的には3/4拍子160BPMという扱いらしく、カラオケ表示では4/4拍子の曲換算で213(=160/3×4)BPMという超高速曲扱いになる大変な曲と化しています。こうなると音階表示を見ながらだとかなりきついです。

もう一つ比較としては<Hφwling Sφul><HE∀ting Sφul>があります。この二曲は曲が似通っている(元々のゲームとしても)ので今回例として出します。前者が通常の体感なら4/4拍子180BPM、後者が4/4拍子182BPMのはずなのですが、内部データ上では後者はそのままですが前者が4/4拍子90BPMの扱いらしく、前者が低速曲扱いになります。こちらは音階表示をみながらだと歌いやすくなっているので両方の曲を知っている場合はチェックしてみると面白いかもしれません。

 

採点後に音域表示がされるようになった

これはありがたい要素です。いままでDAM系列でしか音域がわからなかったのですがJOYSOUND系でもわかるようになったのでいろいろと確認していっています。ただし歌った自分の音域は音名まで書いてあるのでわかるのですが曲側の音域が鍵盤への色づけだけで音名が表示されていないので自分の音域と曲側の音域がずれているときに確認が難しいこと気になるところでしょうか。

ちなみにこのときに(おそらく同一歌手の曲で)音域が似ている曲を表示するという機能があるのですが、文字が小さすぎるために意義がほとんど感じられません。メモをとるための時間もほぼないですしね。

 

電波ソング系の採点はJOYSOUND MAXが有利なのは確定ですが、それ以外は難しい

今回危うく90点に達する曲が一つもないまま店を後にする、という事態が起こりかねなかったのですがそれはなんとか回避しました。また、電波ソング系の得点が良くなったことでデータ全体の標準偏差を計算してみたところ今までの記録で2番目に低い値を出しました。つまりばらけ方がだいぶ下がった訳ですね。それがいいのか悪いのか…。

 

1/0を数学的、教育学的、プログラム的に考える

というわけで今回は三種すべてに絡んだネタをやっていきたいと思います。この頃いろいろな場所でこの「ゼロ除算」の話が書いてあったのでそれがあり得る環境としてこの3つで書いていきたいと思います。というかゼロ除算で問題になるのは大別するとこの3つになるような気がしないでもありません。

 

数学的にはどのように扱うか、をおさらいする

まずこれが大切です。ここの認識が誤るとその後ろの2つについて「なぜそういうことになるのか?」を説明できなくなる可能性が高いからです。注意していきましょう。

まず数学的に1/0を考える場合、この演算を代数学を主としてとらえるか解析学を主としてとらえるか、によってかなり差が出ます。基本的には代数学の考え方が主となるのですが…。

 

代数学の考え方で1/0を考える

代数学で1/0を考えるときはたいていの場合『「1」や「0」は整数と考えてその中に自然に定義される(乗法の逆演算である)除法を用いて演算せよ』という問題と考えることができます。ここで問題となるのが「整数」や「乗法」および「逆演算」をどう考えるか、にあります。そもそも代数学では0や1は実は形式的な記号と考えることができます。また、今回の場合「整数」を対象としたとしてその場合乗法だけではなく加法も同時に定まっていると考えるとこの演算は「整数環、あるいは有理数体(実数体、複素数体)上で」という制約がさらに考えられる、というところまで考えないと正しく考えることができません。

で、ここまで来て初めて公理系から考えていきます。必要となるのは環の公理および体の公理です。まずは環の公理から。Wikipediaにある環の公理とは少し違いますが私の持っている教科書の書き方に準拠します。

加法と乗法という2つの演算が定義された集合Rが環(ring)であるとは次の4つの条件を満たすことである。(この4つを「環の公理系」と呼ぶ)

  1. Rは加法に関して加群である。
  2. Rは乗法において結合法則を満たす。
  3. Rは乗法と加法において分配法則を満たす。
  4. Rは加法の単位元0Rとは異なる元1Rが存在しに対してを満たす。

ちなみにこの公理から証明される定理として

加法の単位元0Rに対してとなる

があります。次に体の公理です。

環Rにおいて以下の条件を満たすときRは体(field)であるという。

  1. Rは加法の単位元0Rを除いて乗法において逆元を持つ。
  2. Rは乗法において交換法則を満たす。

で、ここまで見てみると気がつくと思いますが「0の逆元は体の公理系では定義されていない」ということです。わざわざ体の公理でも「0Rを除いて」ということになっています。このことから代数学的には体において「1/0」という操作は定義されていない(考えない)ということになっています。また環で考えたとしても除法は乗法の逆演算として考えるのでこの場合は考えることができない代物になっています。演算ができないというよりは考えない、が正しいです。ちなみに上記からわかると思いますが0/0も代数学の環や体では定義されません。

 

解析学的の考え方で1/0を考える

で、これで終わらないのが数学です。代数学的にはそうなのですが、解析学的には極限の考え方を用いることでいくつかの場合によって値を考える、ということを行います。つまり直接的に1/0は定義されていないとしても0にごく近い範囲で考えれば除数は0ではない限り乗法において逆元を持つ(有理数体や実数体より)ので0に近づけていくとどうなるのか?というものです。

たとえば関数を考えてxを1から0に正の数として近づけて答えを考えます。すると

  • 1/1=1
  • 1/0.1=10
  • 1/0.01=100
  • 1/0.001=1000

のように答えが大きくなっていっているのがわかります。これを続けていくなら1を一つ前に考えていた数より0に近い正の数で除法を行うと一つ前の答えより正の数として絶対値が大きな値となりどこかで一定の答えとなることはないという結論が得られます。で、これを極限の書き方で書くととなるわけです。なお、この無限大(∞)という書き方ですがこれは数ではなく状態を表す記号です。そのため数としての演算は一切できません。

ただ注意が必要なのが今は「正の数として」近づけていきましたが逆に「負の数として」近づけてみるとどうなるでしょうか。xを-1から0に負の数として近づけて答えを考えると

  • 1/(-1)=-1
  • 1/(-0.1)=-10
  • 1/(-0.01)=-100
  • 1/(-0.001)=-1000

ということで想像できると思いますがこの場合は1を一つ前に考えた数より0に近い負の数で除法を行うと一つ前の答えより負の数として絶対値が大きな値となる、という結論が得られます。上と同様に極限の書き方で書くととなります。

関数の場合は実は近づけ方によってx=0の極限を考えると複数の答えが出てきてしまします。そのため、解析学的にも極限を考えたとしてもx=0における極限は存在しない、という解答となります。(ただし正の方向から近づけた場合は正の無限大、負の方向から近づけた場合は負の無限大という解答にはなります)

おまけですが、解析学的として0/0を考えるといろいろと大変なことが起こります。たとえば関数を考えるとx=0では0/0の形となり値を持たない、となりますが極限を考えれるととなりnの値に依存した答えとなります。これだけ見てもわかりますが0/0を見ただけでは極限値がどうなるか解析学的にもわからないわけです。

 

教育学的にはどう扱うべきなのか考察する

問題なのはここですよね…。このセクションについては完全に個人的見解となりますのであしからず。

まず、学習指導要領および学習指導要領解説にゼロ除算について何か書いていないかな~?と思って読み解いてみたのですがさすがにその部分は疑問に持つ児童や生徒はいたとしても学習する項目としてではないので言及は一切ありませんでした。このことからもし児童や生徒に質問された場合どう返すか、は先生の考え方および力量に依存することになると考えられます。

次に除法を学習するときにどうやって学習するか、からゼロ除算について何かいい言い方がないのか?を考えてみました。気になったのが小学校三年生での除法と乗法、減法との関係についてで、12÷3の意味を考える場合に、

包含除は3×□=12の□を求める場合であり、等分除は□×3=12の□を求める場合である。また、実際に分ける場合でも、包含除も等分除と同じ仕方で分けることができることなどにも着目できるようにしていくことが大切である。

とあるので、ゼロ除算を除法について習ったばっかりなら「じゃあ1÷0の答えを考えてみるけれども0×□=1となる□に当てはまる数はある?ないよね?だから0でわることはできないんだよ」というよくある教えかたがこの場合は適しているといえそうだ、と考えました。また減法との関連であまりが出る除法においてもカードを分ける場合などの例を使うことから「カードを配るやり方で1÷0を考えてみるけれども、一枚のカードを一人に一枚も配らないで配ったふりだけをすると何回できる?(なんか不思議に思えるけれども)何回でもできるよね。そうすると答えが出そうにないね」という言い方になるのでは?と考えました。

ちなみにこの考え方ですが、前者の言い方は数学的には代数学における「0の逆元は定義されていない」を形式的に説明したものであり、後者はどちらかというと解析学における正の数から0に近づけていったときの極限の考え方に近い説明だと考えています。

なお、この後文字と式で文字を使った式を学んだり、方程式で式の変形を考え始めるといつの間にかゼロ除算をしてしまったパターンが現れてくることもあり、そのときは「ゼロ除算となる場合を分けて考えないと正しくない答えが入ってしまうね。これはゼロで割る、ということができない(定義されていない)からこうなるのだよ」という説明はできそうな気がします。高校レベルでも極限までいければだいぶ説明がしやすいですが…。

 

最後にプログラム的にはどうなる?

実はいろいろなコメントを読んでいて一番気になったのがこのポイント。「1/0はDivision by zeroでエラーとなる」という発言が多かったです。ただし注意してほしいのが(たいていの人は理解して言っていると思いますが)対象のプログラミング言語や変数の型によっては必ずしもそうではない、ということを書いておきたいと思います。

以下のC言語のサンプルコードはすべてゼロ除算をしているパターンです。

#include <stdio.h>

int main(void)
{
        printf("1.0/0.0=%f\n",1.0/0.0);
        printf("1.0/(-0.0)=%f\n",1.0/(-0.0));
        printf("0.0/0.0=%f\n",0.0/0.0);
        printf("1/0=%d\n",1/0);
        return 0;
}

これをgccでコンパイルしてみました。ファイル名をtest.cとしてやってみると

$ gcc test.c
test.c: 関数 ‘main’ 内:
test.c:8:21: 警告: ゼロ除算が発生しました [-Wdiv-by-zero]
  printf("1/0=%d\n",1/0);

となります。警告が出ているのは実は一番最後の1/0だけで残りの1/0や0/0は警告が出ていません。さらにできたファイルを実行してみます。

$ ./a.out
1.0/0.0=inf
1.0/(-0.0)=-inf
0.0/0.0=-nan
Floating point exception (コアダンプ)

例外により強制終了しているのは最後の演算だけで、残りの3つに関しては(一応)答えを出しています。これはどういうことかというと浮動小数型の場合はIEEE754にもありますが無限大やNaN(Not a Number、非数)が定義されていて、浮動小数として演算を行った場合はそれぞれ解析学的に極限を考えたものとして計算する、という動作になります。0/0の場合は極限が不定となるためにその状態として非数を用いて表現されているのがわかります。浮動小数型の場合は科学技術演算や3D演算等で無限大を考える必要があるのでこの方が都合がよいのでしょう。また整数型の場合は代数学的に計算されると考えればよいようで、そうなるとゼロ除算の結果は定義されていないため直接わかる場合はコンパイラによる警告が表示され、実行時では例外となるわけです。

 

ちなみにスクリプト言語としてPHPでやってみるとどうなるか、というとこういうコードで試してみました。

<php
  print 1/0;
?>

これをtest.phpに保存して実行してみると

$ php test.php
PHP Warning:  Division by zero in /…/test.php on line 2
INF

ということでゼロ除算による実行警告は出てきましたが計算結果としてはINF、つまり正の無限大となったわけです。なのでこの処理をただの警告と処理するなら常に「エラーとなる」訳ではなく、処理をそのまま継続することが可能です。このあたりは注意して扱う必要があります。

 

本当に考えるのであれば数学上での考え方を基礎にして考える必要があり

高校以下の数学および算数やプログラムでもそうですが、元となる数学の考え方をそれぞれに落とし込んだものですので元の考え方を知らないと理由を突き止めることは難しくなります。もちろん結果そうなるだけで納得できるならそれはそれでもいいですが、深く知っておくと似たような場面で理由が連想できるようになり、人に説明するときにうまく説明できるようになったり自分で問題が起こったときに学ぶことができるようになると思います。

VPSのサービス比較を行ってみた 2017年1月版

「謹賀新年」

ということで新年初めての記事がこちらです。前回blogの高速化についてphp-fpmを導入してみましたのでだいぶよくなったのですが最終的にはVPSのサービスそのものを別のものに切り替えることや自前管理から高速なレンタルサーバーに変更することで高速化を図る、という方法があります。今回はVPSのサービスについて再度比較してみることで高速化できないかどうか、という話になります。

 

VPSのサービスも微妙に進化しているが…

あくまで「微妙」です。前回調べたときにはどのくらいサーバーを使うのか、といった要件がわからなかったのでメモリ量などから考えてみました。で、この数年間で進化した項目というと…ほとんどないのですよね。いくつかのサービスではストレージ部分にSSDを使用することで高速化を図ったりデータセンターの指定によりサービスを提供する領域において最適な場所にサーバーを置くようにしたり、複数のサーバーを同時に使用することを前提にして同一領域にあるVPSサーバー間の速度をインターネットの接続速度より引き上げる、といったことができるようになりました。

これらは進化といえば進化なのですが今回注目している観点が

  • 基本的に1つのVPSサーバーのみで稼働させる事を前提とする
  • サーバーメモリは2GBのプランを指定する
  • サーバーのストレージ容量は実際に使ってみると10GBも使わなかったので実験用途を考えたとしても50GBもあれば十分
  • サーバーの高速性のためHDDとSSDの選択も要点の一つとする
  • 支払い方法に柔軟性があるかどうかも要点の一つとする

というところになります。高速性という観点では仮想CPUの能力がどのくらいあるのか?というのは気になるところなのですがこの場合仮想のコア数にあまり意味はなく実際に負荷がかかったときにどのくらい割り振られるのか?というところにあるのでデータセンター側の能力依存となり調べにくいですし、またサポートについてもメンテナンスが頻発するといったことや自分で対応できない現象が起こったときの対応がよっぽど悪くなければいいので相変わらず要点には加えていません。

 

比較表はこちら

サービス名 初期費用 利用料金(月支払時) 利用料金(年一括支払時[月額換算]) CPU メモリ HDD/SSD 再販 アダルト 無料期間 備考
GMOクラウドVPS 2GBプラン \4,000(\0) \1,480 \1,280 仮想3コア 2GB HDD 100GB 15日間 株主優待あり
お名前.com VPS メモリ2GB \0 \1,315 \1,099 仮想3コア 2GB HDD 200GB 15日間 株主優待あり
Conoha メモリ2GB \0 \1,750 仮想3コア 2GB SSD 50GB × 15日間 日割支払型(クレジットカードのみ) 株主優待あり
さくらのVPS 2G \2,160 \1,706 \1,565 仮想3コア 2GB SSD 50GB × 14日間 複数台対応、データセンター指定あり
KAGOYA クラウドVPS SSDタイプB \0 \1,728 仮想6コア 2GB SSD 160GB × なし 銀行振込不可 ストレージは自動拡張あり
ServerMan@VPS Standard \0 \934 2GB HDD 100GB × × なし 支払いはクレジットカードのみ
FC2VPS LinuxVPS レギュラー \0 \1,380 \1,180 (Physical 1Core 26%) 2GB HDD 80GB (+Backup 80GB) なし(新規30日まで返金あり)

調べてみると支払い方法においてクレジットカートのみ、となっている箇所がいくつかあるので注意です。またネット上から申し込んだ場合は銀行振り込みや口座振替の指定ができない場合もありますのでクレジットカードを持っていない、あるいは作れない人であればそのあたり気にしないとまずそうですね。

 

もし乗り換えるのであればConohaかさくらのVPSなんだけど…

SSDの高速性はいろいろな場所で出ていますので多少高くなるのを覚悟の上で乗り換える、というのはありと考えています。そうなるとSSDを使えるプランはかなり魅力的になります。KAGOYAの場合はちょっと特殊な条件(自動拡張や保証メモリ量という考え方)なので今回はなしと考えると二択となります。問題は両者ともアダルトサイトに対して約款により不可とされているところで、今回調べた中ではGMOクラウドのみが許されているということでConohaの場合は同じGMO系列ながら微妙な差がありちょっと難しいところです。(両者の規約を比較して確認しました)まあ、このサイトやblogの記事を見てもらえばわかりますがアダルトな内容は記憶している限り全くないということですし、せいぜいリンク云々でまずい可能性があるかもというレベルなのでどうなんでしょ?という話となります。

あと、利用料の支払いに関してGMO系列の場合は株主優待で使用料の一部を充当する、ということが可能です。この場合は長期投資を前提として株を保有して…とやると株価の変動で損失を出す可能性もありますが6ヶ月単位で優待により5,000円分がサービスの使用料として充当でき、GMOの業績がよければ配当金までもらえる、ということでうまく回せばConohaはかなりよいサービスなのでは?と考えています。単元での株を保有できるだけの資金があれば、の話ですが。

 

blog(WordPress)の高速化を行ってみた php-fpm編

というわけでまたもやblog(WordPress)の高速化に挑んでみます。前回はWordPressの内部の設定でできる限りの高速化を行う、ということをやってみました。前回の場合だとWordPressを運営しているほとんどのサイトで取り入れることができる方法ですのである意味一般向けになっています。そして今回はWordPressをVPSなど自分でサーバーのソフトウェア環境を変更することができる場合だとどのようにすれば高速化ができるか?ということに着目してやってみました。

 

PHPの実行速度を向上させるには?

当たり前ですがWordPressはPHPを使って動いています。そのため、PHPの実行速度を向上させることができれば応答速度を上げることができ、すなわちPageSpeed Insightsなどで高得点をとることができるようになり、最終的にはユーザー数向上につながります。問題なのはどうやればPHPの速度を向上させることができるのか、です。たとえばPHP5系を使っている場合ですとPHP7系は速度が向上しているらしいので有効である可能性があります。この方法の問題点はPHP7が導入可能な環境なのか(サーバーやパッケージ管理の問題)およびWordPressでPHP7に対応していないプラグインを使用していないかどうか(WordPress本体は4.4でPHP7に対応したらしいですが周りはそうとは限らない)を考える必要があることです。もう一つはPHPの実行に関してPHP側のキャッシュ処理を有効にする方法があります。この方法は意外と汎用的なのですが…。

 

mod_suphpの処理のためにキャッシュ処理が利用できなかったのでphp-fpmに変更した

phpを実行するときのユーザー環境をmod_suphpによって処理していたためphpの実行は「一つのphpファイルにつきphpを起動させて終了させる」というシーケンスとなり、通常キャッシュを使うために必要となる「何回もphpのファイルを実行させる」ができなかった、という問題点がありました。で、今回これを解決するためにphpをFastCGIとして実行できるようにするためサーバー型で実行するphp-fpmを導入して実行ユーザーをphp-fpm側で管理できるようにする、という方式をとることにしました。が、これを使うための設定で難儀したためphpが実行されない状態になるといった状態が途中でできてしまいました…。そのあたりも説明していきたいと思います。

 

php-fpmおよびphpキャッシュ処理モジュールを導入する

実はPHPの「Fatal error: Can’t use function return value in write context」に要注意という記事を書きましたがこれの対応のためにPHPのバージョンを5.4系から5.5系へと変更しています。そのため、キャッシュ処理のパッケージも微妙に異なることに注意しながらやってみます。CentOS6系の場合、PHP5.5などCentOS6標準のPHPより上位のバージョンをパッケージインストールする方法として別のリポジトリを使う方法が有効です。今回はその中でもRemiを使った場合で行います。php-fpmのほかに実行キャッシュ処理などのためにphp-opcacheおよびphp-apcuを導入します。もちろん先にRemiが有効になっていることが条件ですが…。

 

パッケージのインストール

# yum install php-fpm php-opcache php-apcu --disablerepo=base,updates --enablerepo=remi-php55

面倒なのはRemiが提供しているPHPはPHP5.4系、PHP5.5系、PHP5.6系、PHP7系と複数あるのでどのリポジトリを参照するのか指定しなければならないこと、CentOS標準のリポジトリを無効にした状態でパッケージをインストールしなければならないことですね。

 

php-fpmの設定

今回重要なことはPHPを外部で実行できることと同時にPHPを実行するディレクトリで異なるユーザーを設定できることがあります。つまりmod_suphpの役割を設定から担えることがあります。というわけでそれに準拠した設定をするとこうなります。/etc/php-fpm.confはデフォルトでも問題ありませんが/etc/php-fpm.d/以下の設定ファイルに細工が必要です。設定ファイルは各ユーザーごとに設定ファイルを用意して記述する必要があるわけです。たとえばユーザーとしてhtmluserで動作させたい場合はこうします。

[htmluser]
listen = /var/run/php-fpm/htmluser.sock
listen.owner = apache
listen.group = apache
listen.mode = 0600
user = htmluser
group = htmluser
pm = dynamic
pm.max_children = 8
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 12
pm.max_requests = 10000
request_terminate_timeout = 600
request_slowlog_timeout = 100
slowlog = /var/log/php-fpm/htmluser-slow.log 
php_admin_value[error_log] = /var/log/php-fpm/htmluser-error.log
php_admin_flag[log_errors] = on
php_value[session.save_handler] = files
php_value[session.save_path]    = /var/lib/php/session
php_value[soap.wsdl_cache_dir]  = /var/lib/php/wsdlcache

同一サーバー上で動作させる場合はsocketで受け渡した方がよいと思われます。別サーバーに分離する場合はlistenの記述を通常のサーバーの待ち受けにすればOKです。

また、listenに設定するlisten.ownerやlisten.groupはPHPを実行するサーバープロセスの実行者がわかっている場合はそれを設定することでlisten.modeを0600と最小限の設定にすることができます。これがわからなかったり別の実行者からもアクセスできるようにするのであればlisten.ownerおよびlisten.groupにnobodyを設定してlisten.modeを0666としておくのがよいと思います。

次に実行ユーザーの設定はuserおよびgroupで行います。対象のsokcetに接続した場合はここで設定したユーザーで実行することになります。これを利用して実行者を分離することが可能になります。ただし、設定ファイル1つにつき1つのユーザーが対応するのでユーザー数だけこのファイルを(セクション名やユーザー名を変更して)増やしていく必要があるわけですね。

残りはphp-fpmのサーバー動作に関する設定です。pm系はphpを同時に処理できる数などを設定します。基本的にはPHPを実行するサーバープロセスの設定に従ってして置けばOKです。その他PHPの実行設定もある程度はここで行えます。

最後にphp-fpmはdaemonとして動作するので起動処理および起動設定を行います。CentOS6系だとこうなります。

# service php-fpm start
# chkconfig php-fpm on

CentOS7系などsystemctlを使う場合はそちらの処理に置き換わります。また、php-fpmはサーバー動作なので設定ファイルを変更した場合はそれをphp-fpmに通知する必要がありますので注意してください。面倒なら再起動すればOKでしょう。

 

php-opcache、php-apcuの設定

次にキャッシュに関する処理です。php-opcacheはPHP5.5系からPHPの実行キャッシュ処理を担当しています。設定ファイルは/etc/php.d/opcache.iniになります。

zend_extension=opcache.so
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.fast_shutdown=1

注意としてはenable_cliは1に設定することが重要です。これがないとphp-fpm経由で実行したときにキャッシュされない、ということになりますので。

php-apucも設定してしまいましょう。こちらはPHPのデータキャッシュを担当しています。設定ファイルは/etc/php.d/apcu.iniになります。

apc.enabled=1
apc.enable_cli=1
apc.shm_size=64M
apc.ttl=86400
apc.gc_tll=86400

こちらでもenable_cliは1に設定します。また、shm_sizeはキャッシュサイズですのでサーバーのメモリ容量と相談して設定します。

 

Apacheでphp-fpmを使えるようにする

今回難航したのがこの手順です。nginxからphp-fpmを使う手順はいろいろなところにあるのですがApacheから使う手順があまりないこと、またApacheの2.2系と2.4系で使用方法がかなり異なるので苦労しました。今回はCentOS6系だと標準がApache2.2系ですのでそちらに準拠して説明します。Apache2.4系だといくつか変更が必要になりますので注意してください。

 

mod_fastcgiを導入する

php-fpmを使うためにはCGIを実行するモジュールが必要になります。php-fpmの場合はmod_fastcgiを使うとよいと思います。ただし、標準のリポジトリからだとインストールできないのでrpmforgeを使えるようになっていることが条件です。その場合のインストールは

# yum install mod_fastcgi

でOKです。

 

mod_fastcgiを設定する

実は今回一番苦労したのがこの設定です。すべての設定を正しく行ったはずなのにPHPがうまく実行されない場合はこの部分の設定が誤っていることがほとんどだと思います。

まずPHPをFastCGI経由で実行することになるのでモジュールとして連結している設定を解除します。いままでPHPの設定は/etc/conf.d/php.confおよび/etc/conf.d/suphp.confが担当していたのでこれらのファイルを読み込まないように設定します。単純に拡張子を変えてしまえば読み込まれなくなりますので

# mv /etc/httpd/conf.d/php.conf /etc/httpd/conf.d/php.conf.bak
# mv /etc/httpd/conf.d/suphp.conf /etc/httpd/conf.d/suphp.conf.bak

とすればOKです。また設定が終わったらmod_suphpは削除しても問題ないと思います。

次にFastCGIの設定をします。設定ファイルは/etc/httpd/conf.d/fastcgi.confになります。肝となる設定は以下のようになります。

FastCgiWrapper Off
DirectoryIndex index.php
AddType application/x-httpd-php .php
Alias /php-htmluser /var/www/fcgi-bin/php-htmluser
Action php-fpm-htmluser /php-htmluser
FastCGIExternalServer /var/www/fcgi-bin/php-htmluser -socket /var/run/php-fpm/htmluser.sock

ここで間違いやすいポイントを挙げると

  • FastCGIWrapperは基本Offにする。
  • FastCGIExternalServerで設定する呼び出し用仮想実行ファイルは存在する必要はないがその上のディレクトリ(この場合は/var/www/fcgi-bin/)までは実際に存在する必要がある
  • ActionだけではなくAliasも設定しておくこと
  • 複数ユーザーに対応する場合SetHandler(AddHandler)はここでは設定しない
  • 複数のユーザーが存在する場合はその数だけAlias、Action、FastCGIExternalServerを設定する(htmluserの部分を変更する)

があります。実は今回の更新で画面を真っ白にしてしまった理由がFastCGIWrapperをデフォルトの設定であるOnのままにしたためでした。いろいろと調べて判明しましたがまさかこれが原因だとはわかりませんでしたよ…、ということです。あとFastCGIExternalServerで設定したファイルは実際には作成されません。あくまでApache内部でアクセス処理を行ったときに実行ファイルとして内部的に見えるようになっているためです。そのためファイルが作成されていない、と慌てないでくださいね。

ちなみにApache2.4系以降だとDirectoryディテクティブから/var/www/fcgi-bin/にgrant allを設定しないと実行できないようです。アクセス権限が厳密になっているようですね。

 

Directoryの設定を変更する

FastCGIでの実行はCGIの実行権限がディレクトリに必要になります。ファイルを実行可能にする必要はありませんが…。例として/var/www/以下をhtmluser権限でPHPを実行できるようにします。

<Directory "/var/www/">
    Options ExecCGI
    <FilesMatch ".+\.php$">
        SetHandler php-fpm-htmluser
    </FileMatch>
    AllowOverride All
    Order allow,deny
    Allow from all
</Directory>

注意点としては

  • OptionsにExecCGIが必要になる(これがよく忘れる)
  • SetHandlerを拡張子phpに設定する。このときhtmluser権限で実行することをActionの設定から持ってくる

があります。これを使って各ディレクトリの設定でSetHandlerとActionの定義よりユーザー名を指定して実行する、ということが可能になるわけです。

 

Apacheに設定変更を通知する

変更した設定が正しいかどうか確認してから再読込を行いましょう。

# service httpd configtest
# service httpd reload

 

で、速度は向上したの?

これで向上しなかったら悲しいですよね…。速度測定はPageSpeed InsightsよりWPtouchを使っているモバイルのサイトを対象にしたところ2.8秒から0.6秒まで向上させることに成功しました。ポイントも70点を上回る結果となりまあ改善した甲斐はあったかな、というところです。これ以上の向上を考えるとPHPのバージョンアップやVPSの乗り換えとかそんな話になりそうですね。