オントロジーファンクションパフォーマンスおよび制限パフォーマンスの最適化

注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。

パフォーマンスの最適化

このセクションでは、Functionsでユーザーが経験する最も一般的なパフォーマンスの問題を説明し、ボトルネックを回避するためのコードの最適化方法を文書化します。

Performanceタブを使用してFunctionのパフォーマンスを向上させる

パフォーマンスタブは、Functionsのパフォーマンスの問題を分析し、特定するためのツールを提供します。これは、Functionが実行された後のFunctionsヘルパーで見つけることができます。

functions-performance-tab

ウォーターフォールグラフは、X軸上の時間にわたって水平バーに伸びた操作を表しています。各操作には、時間の使い方を示すマーカーがあります。

  • Execute Function は、Functionコードの実行に費やされたCPU時間を示します。
  • Load objects from arguments および Load objects from links は、オントロジーのバックエンドデータベースサービス(OSS)を呼び出すのにかかる時間を示します。

パフォーマンスを向上させるために、以下のことができます。

  • Objects APIを使用して、Typescriptよりも高速にオブジェクトを集約し、リンクをたどる(下記で説明)。
  • オントロジーのバックエンドサービスの呼び出しを並列化して、逐次ロードを回避します。複数の async/await コールがある場合は、Promise.all を使ってすべてのコールを並列に await します。
    • 例えば、一般的なパターンは、リスト内の各オブジェクトが .map() を使ってコールをPromiseにマップし、結果のリストに Promise.all を使用することです。
  • 実行時間を増加させることがある不必要なネストされたループを避けます。

可能な場合はObjects APIの使用を推奨

Workshopの派生プロパティ を使用する際の一般的なパラダイムは、オブジェクトのリンクを集計してプロパティの値を計算することです(例:関連オブジェクトの数をカウントする)。

以下のコードは機能しますが、Function自体がすべてのリンクされたオブジェクトを取得し、集計を実行する必要があります(この場合、長さを計算する):

Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Function() // 非同期で社員のプロジェクト数を取得する関数 public async getEmployeeProjectCount(employees: Employee[]): Promise<FunctionsMap<Employee, Integer>> { // 各社員の作業履歴を非同期で取得する const promises = employees.map(employee => employee.workHistory.allAsync()); // 非同期処理が全て終わるのを待つ const allEmployeeProjects = await Promise.all(promises); let functionsMap = new FunctionsMap(); // 社員ごとにプロジェクト数をFunctionsMapに格納する for (let i = 0; i < employees.length; i++) { functionsMap.set(employees[i], allEmployeeProjects[i].length); } // FunctionsMapを返す return functionsMap; }

上記では、非同期APIと非同期関数(リンクトラバーサルの最適化を参照)を利用していますが、Objects APIが提供する集計方法を利用することが多くの場合、有益です:

Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Function() public async getEmployeeProjectCount(employees: Employee[]): Promise<FunctionsMap<Employee, Integer>> { const result: FunctionsMap<Employee, Integer> = new FunctionsMap(); // 引数のemployeesから従業員IDが一致するプロジェクトをすべて取得し、各従業員IDにマップされたプロジェクト数をカウントする const aggregation = await Objects.search().project() .filter(project => project.employeeId.exactMatch(...employees.map(employee => employee.id))) .groupBy(project => project.employeeId.byFixedWidths(1)) .count(); const map = new Map(); aggregation.buckets.forEach(bucket => { // bucket.key.minは、各バケットのサイズが1であるため、従業員IDを表す。 map.set(bucket.key.min, bucket.value); }); employees.forEach(employee => { const value = map.get(employee.primaryKey); if (value === undefined) { return; } result.set(employee, value); }); return result; }

この方法では、先に全てのリンク先のプロジェクトを取り込むことなく、一つのステップで集約を実行することができます。

集約の通常の制限が依然として適用されることに注意してください。特に、文字列 ID 上の .topValues() は上位 1000 の値のみを返します。現在、集約は最大 10K のバケットに制限されているため、望ましい結果を取得するためには複数の集約を実行する必要があるかもしれません。詳細は集約の計算を参照してください。

リンクのトラバーサルを最適化する

Functions のパフォーマンス問題の最も一般的な原因は、リンクを効率的でない方法でトラバースすることです。しばしば、これは多くのオブジェクトをループ処理し、ループの各イテレーションで関連するオブジェクトをロードする API を呼び出すコードを書くときに発生します。

Copied!
1 2 3 4 for (const employee of employees) { // 従業員の過去のプロジェクトを取得 const pastProjects = employee.workHistory.all(); }

この例では、ループの各イテレーションで個々の従業員の過去のプロジェクトが読み込まれ、データベースへの往復が発生します。この遅延を回避するために、多くのリンクを一度にたどる場合は、非同期リンク移動 API(getAsync() および allAsync())を使用できます。以下は、リンクを非同期で読み込むように記述された関数の例です:

Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Function() public async findEmployeeWithMostProjects(employees: Employee[]): Promise<Employee> { // 社員のプロジェクトを非同期でロードするPromiseを作成 const promises = employees.map(employee => employee.workHistory.allAsync()); // すべてのPromiseを並行して実行 const allEmployeeProjects = await Promise.all(promises); // 最もプロジェクト数の多い社員を見つけるために、結果を反復処理する let result; let maxProjectsLength; for (let i = 0; i < employees.length; i++) { const employee = employees[i]; const projects = allEmployeeProjects[i]; // 現在の最大プロジェクト数よりも多い場合、結果を更新 if (!result || projects.length > maxProjectsLength) { result = employee; maxProjectsLength = projects.length; } } return result; }

この例では、ES6 の async function を使用しています。これにより、.getAsync() および .allAsync() メソッドから返される Promise の戻り値を処理するのが便利になります。