並列化コンピューティングとプロジェクトマネジメント

#概要
 最近，小規模なプロジェクトマネジメントをすることがあり，日が浅いなりに思うことがあったので，まとめた．それまで，「**プロジェクトマネジメントってなにするの？**」とか，「**うまいプロジェクトマネジメントってなに？**」と思っていた．実際に，プロジェクトマネジメントをする側になってみたとき，その内容がうまく言語化できなかった．しかし，自分になじみのある**並列化コンピューティングの文脈で解釈する**と納得することが出来た．そこで，その解釈論について書いてみようと思った．


#スケール"アップ"とスケール"アウト"
プログラムの処理速度を物理的に上げる方法として，スケールアップとスケールアウトがあります．
スケールアップとは，**サーバー自身の能力を向上させることで処理速度の向上を図ることです．**下の画像では「高性能なCPUへ換装」と書かれていますが，それだけにとどまらず，HDDの増設や，メモリの増設がこの範疇に含まれます．一方でスケールアウトは**サーバーの増設によって処理速度の向上を図ることです．**これも簡単で，1台で受けていたリクエストを2台で処理したほうが負荷が下がるだろう．ということは想像に難くないと思います．
しかし，**スケールアウトで性能を出すことは非常に難しいです．**その理由を次節から解説します．

![image](https://qiita-image-store.s3.amazonaws.com/0/139643/dd5b25cb-edab-122d-55a5-ff9301f59a5c.png)

参考：[用語集｜スケールアップ](https://www.idcf.jp/words/scaleup.html)


#アムダールの法則
*プログラムの並列化できる部分の実行時間の割合を P としたとき、並列化不可能な部分は (1 − P)であり、N個のプロセッサを使ったときの全体の性能向上率は次の式で表される。*

```math
S(N) = \frac{1}{(1-P)+\frac{P}{N}}
```

参考：[アムダールの法則](https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%A0%E3%83%80%E3%83%BC%E3%83%AB%E3%81%AE%E6%B3%95%E5%89%87)

という関係式が並列化の分野ではよく知られています．ここで，以下の問題を考えてみます．

*あなたはあるプロジェクトのマネージャーに任命されました．プロジェクトは４人月で見積もられており，4人のメンバーがアサインされました．このプロジェクトの並列化可能な部分は70%でした．このプロジェクトが終了する時期を求めなさい．*

これを実際にN=4,P=0.7で計算すると

```math
S(N) = \frac{1}{(1-P)+\frac{P}{N}} \\
S(4) = \frac{1}{(1-0.7)+\frac{0.7}{4}} \\
S(4) \simeq 2.1
```

ここからこのプロジェクトの効率は1人でやるより2.1倍効率的だと分かります．そのため，このプロジェクトは**約1.9カ月**で終わることが分かります．

ここでこんな声が聞こえてきます．

**「4人月のプロジェクトだろ？？4人のメンバーがいたら，1カ月で終わるだろ？？」**

**答えはNoです．**そういう人に一度このロジックを見せて，感想を聞かせてください．
ここで，問題を書き換え，並列化できる部分が全体の90%だった場合の計算をしてみます．

```math
S(N) = \frac{1}{(1-P)+\frac{P}{N}} \\
S(4) = \frac{1}{(1-0.9)+\frac{0.9}{4}} \\
S(4) \simeq 3.1
```

ここから並列化できる部分が全体の90%だった場合,1人でやるより3.1倍効率的だと分かります，そのため，この条件下では，約1.3カ月で終えることが出来ます．
プロジェクトを効率的に終わらせるために必要なことは，人員の投下も大事ですが，それと同時に，**並列化できる部分の割合を増やす**ことが急務となることが分かります．
しかし，これは非常に難しく，この割合を上げるために高性能計算の学者が頭を抱えています．


#並列化による高速化のノウハウ
##並列化部分の増加
これは，前節のアムダールの法則で議論したこととほぼ同じで，"既存の仕事"のうち，"並列化できない仕事"の割合を下げることです．ただ，ひとつ違うことは"改良後の仕事"のほうが全体の仕事量が増えても構わないという点です．
前節の例を挙げると，70%が並列化できる仕事の場合，1.9カ月で終わることが分かっていました．この例では，**90%の並列化に仕事の作り替えをして，仕事量が5人月になってもやるべきです．**先ほどの例であれば，90%の並列化に取り組んだ場合，3.1倍の速度で仕事が進みます．そのため，**全体が5人月になっても1.6カ月で終わる**計算になります．
これは実は並列化計算の分野でもよくあることで，1CPUで処理をすると，よく知られたアルゴリズムより1.2倍程度遅い．でも，複数のCPUで処理した場合，CPUの数に比例して速くなる．ということは往々にしてあります．

![image](https://qiita-image-store.s3.amazonaws.com/0/139643/77d2fb26-2849-2402-91e5-6cb43f3d4b43.png)


#タスクサイズの均質化
このような経験はないでしょうか．仕事をしていると，前段階の仕事が終わらない．そのためによくわからない事務仕事をしていたり，やらなくてもいいような枝葉のような仕事をしていて，いざ仕事が来たらタスクをこなすのですが，結局残業する．ということがあると思います．これはタスクが来ないためにCPUが遊んでいるような状態で，これも並列化の計算効率に悪影響を及ぼします．そのため，タスクのサイズはできるだけ統一し，待ちが発生しないようにするべきです．

![image](https://qiita-image-store.s3.amazonaws.com/0/139643/c02850d6-4b6c-3463-4305-27f37f3b56c4.png)

#解決方法
##仕事の依存関係を明らかにする

個人的にはアローダイアグラムを書いて，仕事の依存関係を整理し直します．今までの議論から，気にするべきは，「並列化できる部分を最大化」することです．そのため，どの部分が並列化可能か？もっと並列化できる部分がないのか？その際にボトルネックはどこになるのか？を適切にとらえる必要があります．下の図の場合，どの部分がボトルネックになるでしょうか？だいたい(9)になると思います．(4),(5),(6),(7),(8)の作業を結合する必要があり，モジュール数やテストケースも複雑化していく，難易度の高い仕事になると思います．一方で，(4),(5),(6),(7)のような仕事は並列化はしやすいですが，(9)の仕事を終わらせるためには，終了時期を合わせる必要があり，その面で難しいことがあるかもしれません．

![image](https://qiita-image-store.s3.amazonaws.com/0/139643/184ad76a-e8a3-e1a0-60d2-c81a5070570b.png)


##マイクロタスク化をする

作業をマイクロタスク化するのも良い案だと思います．「バグ修正」という大まかなタスクをあえて細かく「バグの再現」「ソースコード修正」「コードレビュー」という単位に分けます．これには2つの利点があります．1つは「次の作業に迷わない」という点です．「次の作業は何だっけ？」と迷う時間を短縮します．そして，もう1つは，「並列度を上げるきっかけ」になります．マイクロタスク化することで，「自分が何をすべきか」が分かります．そうすると，仕事の流れ自体が自分の中で明確化され，前節のアローダイアグラムが書きやすくなります．**そうすると「バグ修正」「テスト」「リリース」という順番でしか仕事ができないと思っていましたが，どうやら「UT」「IT」「ST」の部分は並列化できそうです．**このように仕事の流れや細かいタスクを明確化することで，並列化するタスクが見えやすくなってきます．また，前節で紹介した，「タスクサイズの均質化」も小さなタスク化を組み合わせることが可能で，その結果待ち時間も少なく仕事がはやく済ませることが出来ます．

![image](https://qiita-image-store.s3.amazonaws.com/0/139643/3891b112-3d3d-7cb1-d915-8d7d412d85f8.png)

##ボトルネックにはスケールアップ
「仕事の依存関係を明らかにする」の節で解説したアローダイアグラムを見てみましょう．その中で，(9)が恐らくボトルネックになるだろう．ということを述べました．マイクロタスク化をし，並列化を限界まで上げても，最終的にモジュールは結合しなければなりません．そうすると必ずボトルネックは存在します．これを解決する手立てはあるのでしょうか？至極単純で**「できるエンジニアにやってもらいましょう」**難しいタスクを安定的にこなすことが出来ないと最終的な並列度や速度が遅くなってしまいます．そのためコストをかけるべきところにはコストをかけ，スムーズに仕事を進めるのが賢明だと思います．また，**このボトルネック部分にかかる時間を最小化するように仕事を組む**のが並列化を進めるキーになります．


#現状の問題

- 冗長性の欠如
- PMに負荷が大きい

　これはあくまで並列化を主眼においた速度のみに特化した方策です．そのため，冗長性に対しては脆弱です．例えば前節では，「ボトルネックにはスケールアップ」と述べていました．しかし，そのためには優秀なエンジニアをボトルネックとなるタスクの仕事を行う必要があります．逆に言えば，優秀なエンジニアがその仕事が出来なければ速度がでないという欠点があります．
　そして，もう1つのボトルネックとなるポイントがあります．それがPMです．この方法はPMがすべてのタスクをマイクロタスク化をし，それらの依存関係を把握し，スケジューリングする必要があります．これはかなり力量が必要です．また，仕事が上手く行くことなど，稀です．そのため，タスクの進捗具合，問題の解決度合いにより，タスクの優先度を変更する必要があります．それを常に把握し，スケジューリングしなければなりません．これにクオリティも担保しなければならないので，かなり負荷が高くなります．サーバーもそうですが，そうするとPMもシャットダウンする可能性も出てくると思います．

#まとめ

- プロジェクトを迅速に回すには，並列化コンピューティングの知識を使えばうまく説明できるのでは？という仮説の下，解説を書いた．
- 4人月の仕事は4人で1カ月で済むとは限らない
- プロジェクトを迅速に回すには並列度が重要
- 依存関係の明確化・マイクロタスク化・ボトルネックのスケールアップが重要
- トップダウンのアーキテクチャであるため，PMに負荷がかかり冗長性が乏しい


#感想

この文章を書いて自分の中で割とすっきりと整理できたな．という気持ちになりました．また，その一方で，

「名プレイヤーは名監督にあらず」

という言葉の意味もなんとなく解りました．名プレイヤー，できるエンジニアは基本的に仕事の速い人で，自分が何とか頑張って，自分の行動を最適化し続けると，なれるものなのだろうな．ということを感じました．しかし，一方で，名監督・PMと呼ばれるものは前述したような並列度を考えながら，チームというシステム全体を最適化することに特化する必要があり，これは又別のセンスが必要なのだろうな．と思いました．エンジニアとしてよくできたけどリーダーでは～みたいな話も聞いたりするのは，やはりこの辺の差異があるからなんだろうな．と改めて認知した結果になりました．