良いコードの質とパフォーマンス
前編について
前編:良いコードを書く入門編では「良いコードを書くための形」について紹介しましたが、上級編には、コードのパフォーマンスを上げるための技術について紹介します。
出典は以下の書籍を参考にしました。
質
学校で習ったプログラミング授業と企業として正式に提供するプログラムの最大の違いが、ソースコードの質です。ソフトウェアの質をどうやって決めて確保するかは「テスト」にかかっています。学生時代は乱暴にコードを書いて来た私として、テストの重要性を今になって痛感しています。テストをサボるとバグがなくなりませんし、保守のコストも継続的に上がります。そこで、アプリ開発に一番使われているユニットテストについて紹介します。ソフトウェアのテストに関して、今回の議論ではとても語りきれないですが、簡単の要点だけをここにまとめておきます。
ユニットテスト
ユニットテストは組み込み系開発では単体テストとも呼ばれています。これは、関数/メソッド、クラスなどの実装コードに対して書かれるテストのことです。
下記はテストのためのサンプルコードで説明します。
public class Calc{
public static int plus(int a, int b){
return a + b;
}
}
// ユニットテストを使わないテストコード
public class Main{
public static void main (){
System.out.println(Calc.plus(1, 2))
}
}
上記のコードに対するユニットテストコード(javaのテスティングフレームワークJUnit5とビルドツールGradleを用いて)は以下になります。
public class CalcTest{
@Test
public void testPlus(){
assertEquals(3, Calc.plus(1, 2))
assertEquals(-2, Calc.plus(5, -7))
}
}
出力結果(テスト成功の場合)
Gradle Test Executor 1 finished executing test.
….
BUILD SUCCESSFUL in 4s.
このようにユニットテストは非常に簡単で効果的であることがわかります。ユニットテストはテストを自動化したことです。
網羅的にテストとUIUX
プログラムのロジック上、全部の条件で正しく作動することが必要なので、すべての条件において網羅的にユニットテストを行うようにします。条件とは、プログラムが実行される際にユーザーが出会えるシーンを想定した可能性のことです。数値の範囲、データのタイプなども考慮する必要がありますし、その際にユーザーに提示するメッセージ内容もUIUXの一部です。ユーザー体験の中で、誤作動のインターフェースがお客様の離脱率、継続率、retention rate、さらにLTV(Life Time Value)に直接影響します。
継続的にインテグレーション(Continuous Integration/CI)
大規模なソフトウェア開発では、自動化されたテスト(バッチ処理やCI)を定期的に実行するような仕組みが必要になります。GItihub Actionsや継続的インテグレーションツールを使うと、コードのコミットと同時にすべてのユニットテストが実行され、コミットによるコードが壊されていないかどうかを自動的に発見してくれます。長期にわたって開発とリリースを繰り返すプロジェクトでは、ユニットテストがこの上ない安全装置としての役割を果たします。
パフォーマンス
コンピュータの処理はどんどん速くなり、使用できるメモリも一昔前の数十倍に増えています。さらに5G、画像処理、深層ニューラルネットワーク技術の普及により、計算機のハードウェアも進化し、今クラウド上のサーバーなどの利用することで、大量演算ができるようになっています。一方で、ユーザーがウェブアプリなどのユーザーインタラクションの応答時間に対する要求も高くなっています。特にモバイルスマートフォン・IoTシステムなどは限られたリソースでプログラミングする時に、パフォーマンスが重要となっています。
以下の3つのシーンでは、パフォーマンスがソフトウェアの品質のキーとなっています。
システムが完成してリリースしたら、使い物にならないくらい遅かった
長期間の使用によりデータ量が増え、表示速度がどんどん遅くなる
小さなファイルなら問題ないが、おおきなファイルをアップロードしたら一発でサーバーが落ちる。
これらすべてはコードを書く際にパフォーマンスを意識していないか、或いは、考慮が足りないために発生する問題です。パフォーマンスが良いコードは「とりあえず動作するコード」ではなく、「効率よく動作するコード」です。
パフォーマンスは計算量で決まる
プログラムの本質はコーダから計算機への言葉or指示なので、指示の内容と複雑さで計算機の実行時間が決まります。プログラムの計算量はコードの処理時間や使用メモリに直接影響するので、パフォーマンスは計算量の最適化にかかっています。それでは計算量を最適化する方法を紹介します。
アルゴリズムの選択
例えば、以下の例で、国のリストに条件にあった国がある場合、集計をカウントアップする計算です。条件にあった際にbreakするだけで、計算量が減り、処理時間が短縮されます。
# 計算量が多い例
for i_country in countries:
if(i_country == special_country):
country_counter += 1
# 計算量が少ない例
for i_country in countries:
if(i_country == special_country):
country_counter += 1
break
データ構造の設計
JavaScriptのReactの例でいうと、ArrayListとlinkedListの比較において(下図に示すとおり)、データ挿入の場合linkedListが早い(先頭に追加するだけ)に対し、要素の参照の場合ArrayListが早い(indexがある)ため、プログラム動作によって、データ構造を設計する必要があります。
パフォーマンスチューニングの手順
それでは実際パフォーマンスを向上させるための手順を紹介します。この作業のことをパフォーマンスチューニングといいます。
パフォーマンスの影響原因特定
影響原因に対するパラメータを設計し、調整
各パラメータによって調整し、パフォーマンス結果を測定
納得するまでStep.1-3をiterate
まとめ
今回はコードの質とパフォーマンスを上げる方法について、まとめてみました。ソフトウェアというプロダクトにとって、質(動作の正確性)とパフォーマンス(動作の効率性)がUIUXの一部で、ユーザー離脱率、継続率などのキーファクターになります。継続的にインテグレーションを実施し、ユーザーからのデータによってプロダクトを改善していくのがスタートアップの必勝法です。次回は良いコードの抽象化などについて紹介したいと思います。
この記事が気に入ったらサポートをしてみませんか?