機械翻訳について

ループ、メソッドおよび文字列を使用するパフォーマンスのベスト・プラクティス

Groovyスクリプトを使用してオブジェクトを構成する場合は、設計ディシジョンのパフォーマンスへの影響を理解し、考慮する必要があります。

次の概念を見てみましょう:
  • ループ
  • 方法
  • 文字列

ループ

ループ構造体(whileやforなど)が、最小限のコードで一連の操作を繰り返し実行する機能は、非常に強力な機能です。 正しく使用されないと、ループによって重大なパフォーマンスの問題が発生する可能性があります。

ビュー結果セットのループ

ビューの結果セット内の行をループするときは注意してください。 ビューの結果セットをループする必要がある場合、ビュー基準を使用して、ビューの問合せの範囲を可能なかぎり制限します。 すべての行をフェッチし、スクリプトで実際に必要な行を決定することが望ましい場合があります。 不要な行の問合せおよびフェッチは、アプリケーションに重大なパフォーマンス影響を与える可能性があります。

ループを適切な条件付きで実行

ループの実行前に論理的に満たす必要がある条件がある場合は、ループ全体を1つ以上のif句に囲んで、それらの前提条件を適用します。 そうしないと、アプリケーションの処理が不要になり、処理速度が低下します。

ループを最小の反復数に制限

各ループの反復にはコストが伴います。各反復にコストがかかると仮定しても、決して間違うことはありません。 ループを実装するたびに、次の質問を停止して自分自身に尋ねます:
  • ビューの結果セットを反復処理する場合、結果セットをビジネス要件を満たす可能性のある最小行数に制限しますか。
  • 反復回数を減らすことはできますか? たとえば、特定の値を検索する場合は、残りの反復を完了するのではなく、値が見つかったときにループを終了します。 ただし、この手法を、ビュー基準を使用して問合せの範囲を制限しないようにする口実として使用しないでください。

方法

メソッドの実行にコストがかかるものとします

コードでnewView、getAttributeなどのメソッド、または実装していない他のメソッドが実行されるたびに、メソッドの実行コストが無視できると思われる場合でも、最悪であると想定します:
  • メソッドのパフォーマンス特性は、様々なファクタによって変化する可能性があります。
  • メソッドの実行コストは、ループ内、属性レベルのスクリプト内および同様のコンテキスト内で繰り返される実行によって拡大されます。
  • メソッドの実行は迅速ですが、共有メモリーやデータベース接続などのアプリケーションのスケーラビリティに影響を与える限られたリソースを使用することがあります。
どのメソッド・コールも無料(またはほぼそう)であると考えてはいけません。 たとえば、getAttributeをコールして行から属性値を取得すると、常にコストがかかります - 実際には、属性の実装によってはコストが非常に高い場合があります。 任意のメソッドを呼び出すコードを実装するときは、常にコンテキストについて考える必要があります:
  • ループ内で、ループの外部でメソッドを実行した場合と同じようにコードが機能しますか。 たとえば、ループが反復するたびにメソッドが別の値を返す場合、そのメソッドはループ内でコールされる必要があります。 メソッドが常に同じ値を返す場合は、ループが入力される前にコールする必要があります。
  • ループの内側か外側かに関係なく、スクリプトはメソッドを検出するたびにメソッドをコールする必要がありますか。 または、メソッドの実行結果は、特定の条件が満たされた場合にのみ役立ちますか。 その場合は、前提条件が満たされないかぎり、メソッドの実行を妨げるif句内にメソッドを配置する必要があります。

次の2つのコード例を考えてみます。 最初のバージョンは、常にLineEditedCount属性を更新します。 2番目のバージョンでは、新しい値が元の値と異なる場合にのみ更新が実行されます。 属性値の設定は、簡単な操作のように見える場合があります。 ただし、属性の設定には常にコストが関連付けられます。 一部の属性は、他の属性よりもコストがかかります - たとえば、設定時に複雑なビジネス・ロジックをトリガーする属性です。 また、属性の値を設定すると行がダーティになり、行検証がトリガーされ、コミットのために保留中のトランザクションに行が追加されます - 新しい値が元の値と同じ場合でも。

無条件setAttributeコール

setAttribute('Priority',newPriority)

条件付きsetAttributeコール

//値が必要なものでない場合にのみ値を割り当てます

if (Priority != newPriority) {
setAttribute('Priority', newPriority)
}

属性を設定する前に現在の属性値をチェック

新しい値が現在の値と同じ場合は、行のsetAttributeメソッドをコールしないでください。 setAttributeコールが必要かどうかの確認に失敗すると、次の問題が発生する可能性があります:
  • パフォーマンスへの影響 - setAttributeをコールすると、行がダーティとしてマークされます - setAttributeが属性の値を変更しない場合でも。 これは、属性の現在の値が5で、setAttribute(<attribute name>, five)をコールした場合でも、行はダーティとしてマークされます。
    • ダーティな行がトランザクションに追加され、検証の対象となります。
    • ダーティ行はデータベースに転記され、変更の保存時にコミットされます。

行を不必要に検証および転記すると、パフォーマンスに悪影響を及ぼす可能性があります - 特に、ユースケースに必要な行よりも多くの行をフェッチおよび更新する場合。

次の疑似コード例では、setAttributeメソッドをコールする前に、提案された新しい値が既存の値と異なることを確認します:

if (firstName_c != value) { setAttribute('firstName_c',value)}

キャッシュ属性値

オブジェクトから属性値を取得するには、常にコストがかかります。 コストは、その属性に実装されている特定の機能に応じて属性によって異なります。 同じ属性値を複数回使用する必要がある場合は、getAttributeを1回コールし、結果を変数にキャッシュします。 同じ値に対してgetAttributeを複数回コールしないでください。

検証スクリプトでの高価な操作の実行の回避

検証スクリプトのみを使用する必要があります。たとえば、検証スクリプトに属性値を設定しないでください - オブジェクトが最終的にコミットされる前に、検証スクリプトが複数回コールされる場合があります。 検証スクリプトでコストがかかる可能性のある操作を実行する場合は、その操作を検証スクリプトに含める必要があるか、評価の少ないスクリプトに移動できるかを検討します。

Groovyスクリプトを検証するための明示的なコールの回避

行を検証すると、そのオブジェクトに対して実装された検証に基づいて、オブジェクトごとに異なるコストが常に発生します。 検証を明示的にコールする必要はありません。 明示的にvalidateを呼び出すと、必要以上の検証サイクルが追加される可能性が高くなります。

変数の初期化

変数の初期化は、def count = 1と同様に単純です。

このような単純なスカラー変数を使用して、パフォーマンスへの影響を気にすることなく、ビジネス要件を満たす任意の場所で変数を初期化できます。 このトピックでは、複雑な操作を実行するメソッドや共有リソースを消費するメソッドなど、より高価な操作を必要とする変数初期化について説明します。

次の例は、特定のパフォーマンス上の問題がない場合でも、すべての変数初期化に適用されるベスト・プラクティスなど、変数を初期化するさまざまな方法を示しています。 アプリケーションのパフォーマンスの最大化に役立つ同じベスト・プラクティスは、各変数がどのようにいつどのように使用されるかを明確にするためにも役立ちます。これにより、より信頼性の高いコードが維持しやすくなる傾向があります。
  • ループが実際に使用されているかどうかにかかわらず、ループの反復ごとにchangeVOを初期化します。これは非効率的であり、コードが作成するビュー・インスタンスが多すぎる場合はエラーをトリガーします。
  • ループに入る前にchangeVOを初期化します。 changeVO問合せは1回の反復から次の反復に変更されないため、これははるかに効率的です。 これは、条件があってもchangeVOが作成されることを意味します。
  • 指定された条件が最初に満たされたときにnewViewをコールして、changeVOを初期化します。 これは、このコードの最も効率的なバージョンです。 このコードでは、最大で1つのchangeVOインスタンスが作成され、使用を必要とする条件が満たされていない場合は作成されません。

文字列

文字列は不変であり、作成後に変更することはできません。 2つの文字列を連結すると、結合された文字列を保持する新しいStringオブジェクトが作成されます - 新しいオブジェクトの作成には時間がかかり、ガベージ・コレクタにより多くの作業が行われます。 次の例では、一重引用符を使用して文字列リテラルを定義することに注意してください。

//Creates文字列リテラルと式の結果を保持する新しい文字列オブジェクト
def receivedMessages = 20
def readMessages = 10
def confirmation = 'You have ' +
(receivedMessages – readMessges) + ' unread message(s)'

Groovyは、文字列連結をより効率的に選択できます。 このオプションは、GStringsを使用して文字列を置換します。 GStringは、一重引用符ではなく二重引用符を使用して作成されます。

//置換式を含む二重引用符付き文字列は、GStringを作成

def confirmation = "You have ${receivedMessages - readMessages} unread message(s)"

println(confirmation) //finalここに作成された文字列

GStringsは、標準のString連結と比較して、2つの潜在的なパフォーマンス上の利点を提供します。
  • GStringは、Stringのコンポーネント部分を格納し、Stringが実際に必要になるまでStringの作成を遅延します。
  • GStringはコンポーネント式を評価しません - 文字列が作成されるまで${…} 。 式は、前述の例のように単純な算術に限定されません。Groovyコードのブロックを含めることができます(最終的な文が文字列に含める値を返すようにする必要があります)。

文字列連結の最初の例を振り返ってみると、式receivedMessages - readMessagesが評価され、変数の確認が設定されると、最終的なStringが作成されます。 GStringを使用する2番目の例では、println文が実行されるまで、式評価と最終的な文字列作成の両方が遅延されます。 置換式のないリテラル文字列を定義する場合、一重引用符を使用する方が少し効率的です。

新しいコードを記述する場合は、必要に応じてGStringsおよび一重引用符を使用 - 特定の実装が文字列の最適化からどれだけのメリット(ある場合)が得られるかを事前に特定することは非常に困難です。 しかし、最適化された文字列を実装するための追加コストはないため、ベスト・プラクティスは最初から賭けをヘッジし、最適化された文字列を実装することです。

既存のコードの改良を検討する場合は、慎重に移動し、期待を緩和します。 すべてのコード変更は、ある程度のリスクを伴います。 文字列の最適化のみでは、多くの場合、パフォーマンスが大幅に向上する(または測定可能な)ことはありません。 構成のパフォーマンスが低下している場合は、まず、このドキュメントで説明する他のベスト・プラクティスに注目する必要があります。