Google Cloud Well-Architected Framework のサステナビリティの柱におけるこの原則では、エネルギー消費とサーバー負荷を最小限に抑えるソフトウェアを作成するための推奨事項が示されています。
原則の概要
ベスト プラクティスに沿ってクラウド アプリケーションを構築すると、クラウド インフラストラクチャ リソース(AI、コンピューティング、ストレージ、ネットワーク)で使用されるエネルギーを最適化できます。また、データセンターの水の必要量と、エンドユーザー デバイスがアプリケーションにアクセスする際に消費するエネルギーを削減することもできます。
エネルギー効率に優れたソフトウェアを構築するには、設計と開発からデプロイ、メンテナンス、アーカイブまで、ソフトウェアのライフサイクル全体にサステナビリティの考慮事項を統合する必要があります。AI を使用してクラウド ワークロードの環境への影響を最小限に抑えるソフトウェアを構築する方法の詳細については、 Google Cloud の電子書籍「Build Software Sustainably」をご覧ください。
推奨事項
このセクションの推奨事項は、次の重点分野にグループ化されています。
- 計算作業を最小限に抑える: 冗長なロジックを排除し、不要な計算や機能の肥大化を回避する、無駄のない、焦点を絞ったコードを優先します。
- 効率的なアルゴリズムとデータ構造を使用する: CPU 負荷を軽減し、メモリ使用量を最小限に抑える、時間効率とメモリ効率に優れたアルゴリズムを選択します。
- コンピューティングとデータ オペレーションを最適化する: CPU、メモリ、ディスク I/O、ネットワークなど、利用可能なすべてのリソースを効率的に使用することを目標に開発します。たとえば、ビジーループをイベント ドリブン ロジックに置き換えると、不要なポーリングを回避できます。
- フロントエンドの最適化を実装する: エンドユーザー デバイスで消費される電力を削減するには、画像やアセットの最小化、圧縮、遅延読み込みなどの戦略を使用します。
計算作業を最小限に抑える
エネルギー効率に優れたソフトウェアを作成するには、アプリケーションが実行する計算処理の総量を最小限に抑える必要があります。不要な命令、冗長なループ、余分な機能はすべて、エネルギー、時間、リソースを消費します。最小限の計算を実行するソフトウェアを構築するには、次の推奨事項を使用します。
無駄のない、目的に特化したコードを作成する
必要な結果を達成するために不可欠な最小限のコードを記述するには、次のアプローチを使用します。
- 冗長なロジックと機能の肥大化を排除する: 重要な機能のみを実行するコードを記述します。計算オーバーヘッドと複雑さが増加するものの、ユーザーに測定可能な価値を提供しない機能は避けてください。
- リファクタリング: エネルギー効率に優れた状態を継続的に維持するには、アプリケーションを定期的に監査して、未使用の機能を特定します。必要に応じて、このような機能を削除またはリファクタリングする実践をしてください。
- 不要なオペレーションを回避する: 結果が必要になるまで、値を計算したり、アクションを実行したりしないでください。遅延評価などの手法を使用して、アプリケーションの依存コンポーネントが出力を必要とするまで計算を遅らせます。
- コードの読みやすさと再利用性を優先する: 読みやすく、再利用可能なコードを記述します。このアプローチにより、重複が最小限に抑えられ、DRY(Don't Repeat Yourself)の原則に従うことで、ソフトウェアの開発とメンテナンスによる二酸化炭素排出量を削減できます。
バックエンド キャッシュを使用する
バックエンド キャッシュにより、アプリケーションが同じ処理を繰り返し実行しないようにします。キャッシュ ヒット率が高いと、リクエストあたりのエネルギー消費量がほぼ線形に減少します。バックエンド キャッシュを実装するには、次の手法を使用します。
- 頻繁にアクセスされるデータをキャッシュに保存する: 頻繁にアクセスされるデータを、一時的な高パフォーマンスのストレージ ロケーションに保存します。たとえば、Memorystore などのインメモリ キャッシュ サービスを使用します。アプリケーションがキャッシュからデータを取得すると、データベース クエリとディスク I/O オペレーションの量が削減されます。その結果、バックエンドのデータベースとサーバーの負荷が軽減されます。
- API レスポンスをキャッシュに保存する: 冗長でコストのかかるネットワーク呼び出しを回避するため、頻繁に行われる API リクエストの結果をキャッシュに保存します。
- インメモリ キャッシュを優先する: 低速なディスク I/O オペレーションと複雑なデータベース クエリを排除するため、データを高速メモリ(RAM)に保存します。
- 適切なキャッシュ書き込み戦略を選択する:
- ライトスルー戦略では、データがキャッシュと永続ストアに同期的に書き込まれます。この戦略により、キャッシュ ヒットの可能性が高まり、永続ストアがエネルギーを大量に消費する読み取りリクエストを受け取る回数が減ります。
- ライトバック(ライトビハインド)戦略により、書き込み負荷の高いアプリケーションのパフォーマンスが向上します。データは最初にキャッシュに書き込まれ、データベースは後で非同期的に更新されます。この戦略により、低速なデータベースへの即時書き込み負荷が軽減されます。
- スマートなエビクション ポリシーを使用する: キャッシュを無駄なく効率的に維持します。古いデータや有用性の低いデータを削除し、頻繁にリクエストされるデータに使用できるスペースを最大化するには、有効期間(TTL)、最も長い間使われていないデータ(LRU)、最も使用頻度の低いデータ(LFU)などのポリシーを使用します。
効率的なアルゴリズムとデータ構造を使用する
選択したアルゴリズムとデータ構造によって、ソフトウェアの生の計算の複雑さが決まります。適切なアルゴリズムとデータ構造を選択すると、タスクの完了に必要な CPU サイクル数とメモリ オペレーション数を最小限に抑えることができます。CPU サイクルとメモリ オペレーションが少ないほど、エネルギー消費量が少なくなります。
最適な時間計算量が得られるアルゴリズムを選択する
必要な結果を最短時間で達成するアルゴリズムを優先します。このアプローチにより、リソースの使用期間を短縮できます。リソースの使用量を最適化するアルゴリズムを選択するには、次の方法を使用します。
- 複雑さの軽減に重点を置く: 複雑さを評価するには、ランタイム指標だけでなく、アルゴリズムの理論的な複雑さも考慮します。たとえば、バブルソートと比較すると、マージソートは、大規模なデータセットの計算負荷とエネルギー消費を大幅に削減します。
- 重複する作業を避ける: 選択したプログラミング言語またはフレームワークで、組み込みの最適化された関数を使用します。これらの関数は、C や C++ などの低レベルでエネルギー効率の高い言語で実装されることが多いため、カスタムコード関数と比較して、基盤となるハードウェア向けに最適化されています。
効率を重視してデータ構造を選択する
選択したデータ構造によって、データの取得、挿入、処理の速度が決まります。この速度は CPU とメモリ使用量に影響します。効率的なデータ構造を選択するには、次の方法を使用します。
- 検索と取得を最適化する: アイテムが存在するかどうかの確認や特定の値の取得などの一般的なオペレーションでは、速度が最適化されたデータ構造を優先します。たとえば、ハッシュマップやハッシュセットを使用すると、ほぼ一定の時間でルックアップを実行できます。これは、配列を線形に検索するよりもエネルギー効率に優れたアプローチです。
- メモリ使用量を最小限に抑える: 効率的なデータ構造により、アプリケーションの全体的なメモリ使用量を削減できます。メモリアクセスとメモリ管理を削減することで、消費電力を抑えることができます。また、メモリ プロファイルがスリム化されると、プロセスの実行効率が向上するため、リソースのアップグレードを延期できます。
- 特殊な構造を使用する: 特定の問題に特化したデータ構造を使用します。たとえば、文字列の接頭辞を高速に検索するには trie データ構造を使用し、最大値または最小値のみに効率的にアクセスする必要がある場合は優先度付きキューを使用します。
コンピューティングとデータ オペレーションを最適化する
ソフトウェアを開発する際は、技術スタック全体でリソースを効率的かつ比例的に使用することに重点を置きます。CPU、メモリ、ディスク、ネットワークは、制限付きの共有リソースとして扱います。リソースを効率的に使用することで、費用とエネルギー消費量を大幅に削減できることを認識する。
CPU 使用率とアイドル時間を最適化する
CPU がアクティブな電力消費状態で、意味のある処理を行わずに費やす時間を最小限に抑えるには、次の方法を使用します。
- ポーリングよりもイベント ドリブン ロジックを優先する: リソースを大量に消費するビジーループや継続的なチェック(ポーリング)を、イベント ドリブン ロジックに置き換えます。イベント ドリブン アーキテクチャでは、アプリケーションのコンポーネントは、関連するイベントによってトリガーされた場合にのみ動作します。このアプローチにより、オンデマンド処理が可能になり、リソースを大量に消費するポーリングが不要になります。
- 常に高周波数で動作しないようにする: CPU が常に最高周波数で動作しないようにコードを記述します。エネルギー消費を最小限に抑えるため、アイドル状態のシステムは低電力状態またはスリープモードに移行できる必要があります。
- 非同期処理を使用する: アイドル待機時間中にスレッドがロックされないようにするには、非同期処理を使用します。このアプローチにより、リソースが解放され、全体的なリソース使用率が向上します。
メモリとディスク I/O を効率的に管理する
メモリとディスクの使用が非効率な場合、不要な処理が発生し、消費電力が増加します。メモリと I/O を効率的に管理するには、次の手法を使用します。
- 厳格なメモリ管理: 未使用のメモリリソースを事前に解放するアクションを実行します。必要以上に大きなオブジェクトをメモリに保持しないようにします。このアプローチにより、パフォーマンスのボトルネックが防止され、メモリアクセスに消費される電力が削減されます。
- ディスク I/O を最適化する: アプリケーションが永続ストレージ リソースに対して読み取りと書き込みを行う頻度を減らします。たとえば、中間メモリバッファを使用してデータを保存します。一定の間隔で、またはバッファが特定のサイズに達したときに、データを永続ストレージに書き込みます。
- バッチ オペレーション: 頻繁に発生する小規模なディスク オペレーションを、より少ない大規模なバッチ オペレーションに統合します。バッチ オペレーションは、多数の個別の小規模なトランザクションよりも消費電力が少なくなります。
- 圧縮を使用する: 適切なデータ圧縮手法を適用して、ディスクに書き込まれるデータ量やディスクから読み取られるデータ量を削減します。たとえば、Cloud Storage に保存するデータを圧縮するには、解凍トランスコーディングを使用します。
ネットワーク トラフィックを最小限に抑える
ネットワーク リソースは、データ転送オペレーション中に大量のエネルギーを消費します。ネットワーク通信を最適化するには、次の手法を使用します。
- ペイロード サイズを最小限に抑える: リクエストに必要なデータのみを転送するように API とアプリケーションを設計します。数個のフィールドのみが必要な場合は、大きな JSON 構造や XML 構造を取得または返さないようにします。返されるデータ構造が簡潔であることを確認します。
- ラウンドトリップを減らす: ユーザー アクションを完了するために必要なネットワーク ラウンドトリップの数を減らすには、よりスマートなプロトコルを使用します。たとえば、HTTP/1.1 よりも HTTP/3 を優先し、REST よりも GraphQL を選択し、バイナリ プロトコルを使用し、API 呼び出しを統合します。ネットワーク呼び出しの量を減らすと、サーバーとエンドユーザー デバイスの両方のエネルギー消費量を削減できます。
フロントエンドの最適化を実装する
フロントエンドの最適化により、エンドユーザーがダウンロードして処理する必要があるデータが最小限に抑えられ、エンドユーザー デバイスのリソースの負荷が軽減されます。
コードとアセットを最小限に抑える
エンドユーザーがダウンロードして処理する必要があるリソースが小さく、効率的に構造化されているほど、デバイスの消費電力は少なくなります。エンドユーザーのデバイスでのダウンロード量と処理負荷を最小限に抑えるには、次の手法を使用します。
- 最小化と圧縮: JavaScript、CSS、HTML ファイルの場合は、適切な最小化ツールを使用して、空白文字やコメントなどの不要な文字を削除します。画像などのファイルが圧縮され、最適化されていることを確認します。CI / CD パイプラインを使用すると、ウェブアセットの最小化と圧縮を自動化できます。
- 遅延読み込み: 画像、動画、重要でないアセットは、実際に必要になったとき(これらの要素がウェブページのビューポートにスクロールされたときなど)にのみ読み込みます。このアプローチにより、初期データ転送の量とエンドユーザー デバイスの処理負荷が軽減されます。
- JavaScript バンドルの縮小: 最新のモジュール バンドラとツリー シェイキングなどの手法を使用して、JavaScript バンドルから未使用のコードを削除します。この方法では、ファイルが小さくなり、読み込みが速くなり、サーバー リソースの使用量が少なくなります。
- ブラウザ キャッシュ: HTTP キャッシュ ヘッダーを使用して、静的アセットをローカルに保存するようユーザーのブラウザに指示します。ブラウザ キャッシュを使用すると、その後のアクセスで繰り返しのダウンロードや不要なネットワーク トラフィックを回避できます。
軽量なユーザー エクスペリエンス(UX)を優先する
ユーザー インターフェースの設計は、フロントエンド コンテンツをレンダリングする際の計算の複雑さに大きな影響を与える可能性があります。軽量な UX を提供するフロントエンド インターフェースを構築するには、次の手法を使用します。
- 効率的なレンダリング: リソースを大量に消費する頻繁なドキュメント オブジェクト モデル(DOM)の操作を避けます。レンダリングの複雑さを最小限に抑え、不要な再レンダリングを排除するコードを記述します。
- 軽量設計パターン: 適切な場合は、静的サイトまたはプログレッシブ ウェブアプリ(PWA)を優先します。このようなサイトやアプリは読み込みが速く、必要なサーバー リソースも少なくなります。
- ユーザー補助とパフォーマンス: レスポンシブで読み込みが速いサイトは、多くの場合、持続可能性とユーザー補助に優れています。最適化された、すっきりとしたデザインにより、コンテンツのレンダリング時に消費されるリソースが削減されます。パフォーマンスと速度が最適化されたウェブサイトは、収益の増加に役立ちます。Deloitte と Google の調査研究「Milliseconds Make Millions」によると、サイトの速度が 0.1 秒(100 ミリ秒)向上すると、小売サイトのコンバージョンが 8.4% 増加し、平均注文額が 9.2% 増加します。