2018年12月13日

UNIX 哲学に学ぶ 高品質なソフトウェア設計

Outline
 なぜ UNUX 哲学 [1] なのか.まずはこの題意について説明したい.UNIX [2] の誕生には,先行して開発されていた Multics [3] の「新奇なアイデアを貪欲に取り込んだ [3]」結果,「巨大で複雑なものに [2]」なり,「現実解 [3]」となり得なかった歴史がある.これを踏まえ,UNIX では,まず小規模に動作するシステムを作り,巨大で複雑な問題に対しても,巨大な単一プログラムで解決することを避け,小規模なソフトウェアへと分割し,組み合わせることで,解決した.このような UNIX 開発における経験則を,UNIX 哲学と呼び,我々の求める高品質なソフトウェア設計の礎となり得る思想である.品質の高いソフトウェア開発には,ある種の哲学,つまりは設計思想が重要となる.本投稿では,UNIX 哲学を交えつつ,ソフトウェア設計に必要な設計思想について,おおよそ一般的と思われることを説明する.

※ この記事は,「みゅーもり Advent Calendar 2018 (通称:みゅんカレ)」13 日目の記事です.十分に検証されたものではなく,私の理解を大雑把にまとめたものです.したがって,事実と異なる可能性があります.また,本文中の擬似コードは C/C++ または Python です.

2018年9月29日

C++ による non-blocking socket を用いた http サーバの実装

Abstract
  http サーバは http daemon (httpd) とも呼ばれ,socket 通信を用いてブラウザと通信するサーバ側のプログラムである.以前は,接続数と同じ数のプロセスを立ち上げ,blocking を行う実装が一般的であったが,nginx (エンジンエックス) [1] の登場以来,特に静的な web ページでは,non-blocking な実装により,1 つのプロセスで複数の接続を処理することで,コンテキストスイッチと呼ばれるプロセス切り替えのオーバーヘッドを削減する手法が広がっている.しかしながら,non-blocking の実装サンプルは少なく,実装しても些細なミスで致命的に動作せず,また,存在するサンプルも,httpd の実装と,socket 回りの実装が密結合となっており,分離が難しいことが分かる.加えて,nginx のソースコードも,動作を理解するには,非常に大規模で複雑であった.したがって,本投稿では,定数時間でイベントを監視可能な epoll を用いて,httpd の socket 通信部分と httpd 部分とが疎結合となる見通しのよいサンプルを実装する.結果として,簡単な HTML と CSS を用いた画像付きのサンプルページを配信可能な httpd を実装した.また,request URL の確認において,hash table を用いた照合によりパスを確認することで,ディレクトリトラバーサル [2] のような,古典的で基本的なセキュリティ問題に対処した.

2018年8月17日

std::vector<T> で MATLAB や numpy のように配列計算する

Abstract
  C++ において,クラスのコンストラクタ中でメモリを確保し,ディストラクタ中でメモリを解放することで,メモリ寿命をクラス寿命としてスコープにより制御すること (RAII※) は一般的である.C++ において,単なる配列を RAII に従い実装する場合,新たなクラスを定義することはせず,std::array<T> や std::vector<T> などの標準実装のコンテナ型を用いることが一般的である.これらのコンテナ型は,単なる配列としてだけでなく,数値計算やデータ解析における配列としても多用される.このとき「要素の追加」や「配列全体への演算」といった操作が頻繁に発生する.しかしながら,現状では std::vector<T> に演算子が定義されておらず,MATLAB における a=[1, 2, 3]; a*=4 や numpy における a=np.asarray([1,2,3]); a*=4 といった演算は,std::vector<double> a={1,2,3}; for(int i=0; i<a.size(); i++){ a[i]*=4; } のように,for 文を用いた冗長な記述となる.
  そこで,本投稿では,計算や解析で特に多用される std::vector<T> に対して演算子を定義することで,記述の簡略化を図る.より具体的には,四則演算子と剰余演算子,べき乗演算子を定義した.また,左シフト演算子 (<<) と 左シフト代入演算子 (<<=) をそれぞれ要素の結合と追加 (.push_back()) として割り当てた.この結果,先ほどの演算は,std::vector<double> a={1,2,3}; a*=4; のように簡略化された.

※ RAII は Resource Acquisition Is Initialization の省略形.「資源(リソース)の確保と解放を,クラス型の変数の初期化と破棄処理に結び付けるというプログラミングのテクニック [追記1]」のこと.

2018年7月21日

不偏分散の定義と証明,および n-1 の解釈

Abstract
  本投稿では,不偏分散の定義とその解釈について説明する. 不偏分散 $\sigma_u^2$ は, 1) 標本分散の期待値 $E[\sigma^2]=\frac{n-1}{n}\sigma_0^2$ と, 2) 母分散 $\sigma_0^2$ の偏りを無くすため,標本分散 $\sigma^2$ を $\frac{n}{n-1}$ 倍した値として定義される. このように,不偏分散は,期待値について偏りの無い分散として定義される. 数式の解釈について,サンプル数が有限の場合 (特に,サンプル数が極端に少ない場合),母分散の範囲に当たりをつけるには,分散を広めに見積もる必要がある.$\frac{n}{n-1}$ 倍は,1) と 2) を比較して,どの程度範囲を広げれば母分散の範囲を指示できるか,を計算した値と考えられる.

※ $n_0$ は 母数, $n$ は 標本の数 (サンプル数), $\overline{x}\equiv\frac{1}{n}\sum_{i=1}^n{x_i}$ は 標本平均, $\sigma^2\equiv\frac{1}{n}\sum_{i=1}^n(x_i-\overline{x})^2$ は 標本分散, $\overline{x}_0\equiv\frac{1}{n_0}\sum_{i=1}^{n_0}{x_i}$ は 母平均, $\sigma_0^2\equiv\frac{1}{n_0}\sum_{i=1}^{n_0}(x_i-\overline{x})^2$ は 母分散, $\sigma_u^2\equiv\frac{1}{n-1}\sum_{i=1}^n(x_i-\overline{x})^2$ は 不偏分散, をそれぞれ表す.

2018年6月2日

論文の紹介と検証:Marsaglia, G. 2003. Xorshift RNGs.

Abstract of this post
  This post will introduce Marsaglia, G. 2003. Xorshift RNGs. [1] which is a paper of Xorshift random number generator. And, in the reproducing process of calculating the hyperparameters reveals that [1] has a few calculation mistakes of hyperparameters. This result means that there is a parameter which enable to generate random number by only 2 calculations of 64 bits xorshifts while [1] said that at least 3 calculations are required.

  この投稿では,Xorshift 疑似乱数生成器の論文 Marsaglia, G. 2003. Xorshift RNGs. [1] を紹介する.前提条件として,参考文献 [2] 読了レベルの知識を必要とする.以下,論文の節に沿って適宜翻訳を行いながら,必要な計算の実装と実行結果を示す.また,再現実験および,[追記2] より,[1] のパラメータのいくつかとは異なる計算結果を得た.また,shift 操作 2 回で乱数を生成できるパラメータは存在しないとされていたが[1],64 bits 時において,存在することを示す計算結果を得た.

2018年3月28日

C++ から Email (Gmail) を送信する.

Abstract
  この投稿では,C++ から Email (Gmail) の送信を行う.C++ からメールを送信するには,socket 通信で接続を確立し,OpenSSL 等の暗号化ライブラリ通して通信する必要がある.socket 通信まで正常に動作させられれば,暗号化通信は socket を OpenSSL でラップするだけである.その後,メールサーバーと定型文をやり取りすることで,メールが送信される.本投稿ではメールサーバとして Gmail を利用する.Gmail をプログラムから利用するにあたり,予め Gmail の「安全性の低いアプリの許可:」を「有効」にする必要がある.また,2 段階認証を使用している場合は,本プログラム用にアプリケーションパスワードを発行しておく必要がある.

2018年2月19日

C++ から任意の Python 関数を実行する

Calling any Python function from C++
Abstract
  There is a few methods to run python functions on C++ like boost::python or using of Python.h. For example, using boost.python needs to convert C++ types like std::vector<T> frequently used as a standard implementation to Python types like numpy enable to give them as a function arguments. While this method will provide high execution efficiency, users need to prepare or convert types like std::vector<T> to Python types. For these reasons, sstd::c2py has been developed in order to call any Python functions with built-in, std::vector<T>, sstd::mat_c<T> or sstd::mat_r<T> (T is limitted by built-in types.) types from 2 lines of C++ codes.

  C++ から Python 関数を呼び出すには,boost::python や Python.h ヘッダからの API 呼び出しが知られている.例えば,boost::python を利用する場合は,C++ 側で Python 側へそのまま受け渡しができる numpy 型等を用意した上で python 関数の呼び出しを行う ([8] より推察).この手法は,高い実行効率を得られる反面,標準実装として C++ で頻繁に用いられる std::vector<T> 型をそのまま利用しようとした際に,ユーザ側で変換しておく必要がある ([9] より推察) など,実装に必要な手間も大きい.そこで,C++ の built-in 型 および std::vector<T>, sstd::mat_c<T>, sstd::mat_r<T> (ただし,T は built-in 型に限る.) と同等の numpy 型を引数に取る任意の Python 関数を C++ から 2 行で実行するライブラリを開発した.