Windows Workflow Foundation 4 のパフォーマンス

.NET Framework 4 には、Windows Workflow Foundation (WF) のメジャー バージョンが含まれ、パフォーマンスに多大な投資が行われています。 この新しいバージョンでは、.NET Framework 3.0 と .NET Framework 3.5 の一部として付属していた以前のバージョンの WF からデザインが大幅に変更されています。 プログラミング モデル、ランタイム、およびパフォーマンスと操作性が大幅に向上するツールの基本機能に基づいて再設計されています。 このトピックでは、これらのリビジョンの重要なパフォーマンス上の特徴を説明し、以前のバージョンと比較します。

WF3 と WF4 を比較すると、個々のワークフロー コンポーネントのパフォーマンスが桁違いに向上しています。 これにより、手動でコーディングした Windows Communication Foundation (WCF) サービスと WCF ワークフロー サービスの間のギャップが非常に小さくなっています。 WF4 でワークフローの待機時間は大幅に短縮されました。 永続化のパフォーマンスは 2.5 ~ 3.0 倍向上しています。 ワークフロー追跡による状態監視のオーバーヘッドも格段に短縮されました。 これらの要因はアプリケーション内で WF4 に移行する、または WF4 を導入する理由として説得力があります。

用語

.NET Framework 4 で導入された WF のバージョンは、このトピックではこれから WF4 と呼びます。 WF は .NET Framework 3.0 で導入され、.NET Framework 3.5 SP1 を通じていくつかの小さな変更がありました。 .NET Framework 3.5 バージョンの Workflow Foundation は、このトピックでは以後 WF3 と呼びます。 WF3 は WF4 と共に .NET Framework 4 に付属しています。 WF3 の成果物の WF4 への移行の詳細については、Windows Workflow Foundation 4 への移行ガイドを参照してください。

Windows Communication Foundation (WCF) とは、サービス指向のアプリケーションを構築するための Microsoft の統一プログラミング モデルです。 当初は WF3 と共に .NET Framework 3.0 の一部として導入されましたが、現在では .NET Framework の主要なコンポーネントの 1 つになっています。

Windows Server AppFabric はインターネット インフォメーション サービス (IIS) 上で実行する Web アプリケーションおよび複合アプリケーションの構築、拡張、および管理を容易にする一連の統合テクノロジです。 監視およびサービスとワークフローの管理を行うためのツールを提供します。 詳細については、「Windows Server AppFabric 1.0」を参照してください。

目標

このトピックの目的は、WF4 のパフォーマンス特性のさまざまな状況での測定データを示し、 WF4 と WF3 を詳細に比較して、新しいリビジョンでの大幅な機能強化について説明することです。 この記事のシナリオとデータは WF4 と WF3 のさまざまな側面の基本的なコストを定量化しています。 このデータは WF4 のパフォーマンス特性を理解するうえで役立ちます。また、WF3 から WF4 への移行計画または WF4 を使用したアプリケーション開発に役立つことがあります。 ただし、この記事に記載されているデータから結論を導き出す場合には注意が必要です。 複合ワークフロー アプリケーションのパフォーマンスは、ワークフローの実装方法や異なるコンポーネントの統合方法に大きく依存します。 アプリケーションのパフォーマンス特性を確認するには、各アプリケーションを測定する必要があります。

WF4 のパフォーマンスの強化の概要

WF4 は慎重に設計され、高いパフォーマンスとスケーラビリティが実装されています。これらについて、次のセクションで説明します。

WF ランタイム

WF ランタイムの中核は、ワークフロー内のアクティビティを実行する非同期スケジューラです。 非同期スケジューラはアクティビティに高パフォーマンスで予測可能な実行環境を提供します。 この環境には実行、継続、完了、キャンセル、例外に関する明確に定義されたコントラクトと予測可能なスレッド モデルがあります。

WF4 ランタイムのスケジューラは WF3 のスケジューラより効率的です。 WCF に使用されるものと同じ I/O スレッド プールを活用し、非常に効率的にバッチ作業項目を実行できます。 内部作業項目のスケジューラ キューは一般的な使用パターンに合わせて最適化されています。 また、WF4 ランタイムでは同期およびイベント処理ロジックを最小限に抑えたきわめてコンパクトな方法で実行状態を管理するのに対し、WF3 では大規模なイベントの登録と呼び出しに依存しており、状態遷移のための複雑な同期を実行します。

データの格納とフロー

WF3 では、アクティビティに関連付けられたデータは DependencyProperty 型によって実装される依存関係プロパティを使用してモデル化されます。 依存関係プロパティのパターンは Windows Presentation Foundation (WPF) で導入されました。 通常、このパターンはデータ バインディングなどの UI 機能の簡単な操作をサポートするための高度な柔軟性を備えています。 ただし、パターンを使用するには、ワークフロー定義にプロパティを静的フィールドとして定義する必要があります。 WF ランタイムがプロパティ値を設定または取得するたびに、高負荷の検索ロジックが実行されます。

WF4 では、明確なデータ スコープ設定ロジックを使用することでワークフロー内のデータ処理方法が大幅に向上しています。 WF4 は、変数と引数という 2 つの異なる概念を使用して、アクティビティに保存されたデータとアクティビティ境界間を移動するデータを区別します。 変数と "In/Out/InOut" 引数の明確な階層スコープを使用することにより、アクティビティのデータの使用法の複雑さが大幅に緩和され、データの有効期間も自動的にスコープ設定されます。 アクティビティには、引数によって表されるシグネチャが適切に定義されています。 アクティビティを検査するだけで、予想される受け取りデータとアクティビティの実行結果として生成されるデータを知ることができます。

アクティビティは、WF3 ではワークフローの作成時に初期化されますが、 WF4 では実行時にのみ初期化されます。 これにより、新規ワークフローのインスタンスの作成時に初期化/非初期化を行わずにアクティビティのライフサイクルを簡素化できるため、実行が効率化されます。

制御フロー

他のプログラミング言語と同様、WF はシーケンス、ループ、分岐などのパターンに関する一連の制御フロー アクティビティを導入することにより、ワークフロー定義の制御フローをサポートします。 WF3 では、同じアクティビティを再実行する必要がある場合に、新規の ActivityExecutionContext を作成し、BinaryFormatter に基づく高負荷のシリアル化および逆シリアル化ロジックでアクティビティを複製します。 通常、反復制御フローはアクティビティを順次実行する場合より時間がかかります。

WF4 の処理方法はまったく異なります。 WF4 ではアクティビティ テンプレートを取得し、新規の ActivityInstance オブジェクトを作成してスケジューラ キューに追加します。 このプロセスは明示的にオブジェクトを作成するだけなので、きわめて低負荷です。

非同期プログラミング

通常、I/O や分散コンピューティング処理などの実行時間の長いブロッキング操作に非同期プログラミングを使用することにより、アプリケーションのパフォーマンスとスケーラビリティは向上します。 WF4 は基本アクティビティ型 AsyncCodeActivityAsyncCodeActivity<TResult> で非同期をサポートします。 ランタイムは非同期アクティビティをネイティブに認識するため、非同期操作が保留状態の場合にインスタンスを自動的に非永続化ゾーンに配置できます。 カスタム アクティビティをこれらの型から派生させることで、ワークフローのスケジューラ スレッドを保持したり、並行して実行される可能性があるすべてのアクティビティをブロックしたりすることなく、非同期操作を実行できます。

メッセージング

当初、WF3 のメッセージングのサポートは、外部イベントまたは Web サービスの呼び出しによるきわめて限定的なものでした。 .NET Framework 3.5 では、SendActivityReceiveActivity を使用して、ワークフローを WCF クライアントとして実装したり、WCF サービスとして公開したりすることができました。 WF4 では、WCF メッセージング ロジックを WF に密接に統合することで、ワークフローベースのメッセージング プログラミングの概念がさらに強化されました。

.NET 4 の WCF で提供されている統合メッセージ処理パイプラインにより、WF4 サービスのパフォーマンスとスケーラビリティは WF3 と比べて大幅に向上しています。 WF4 ではメッセージング プログラミングのサポートも強化され、複雑なメッセージ交換パターン (MEP) をモデル化できます。 開発者は、型指定されたサービス コントラクトを使用することで、プログラミングを簡素化できます。また、型指定されないサービス コントラクトを使用することで、シリアル化のコストをなくしてパフォーマンスを向上させることができます。 WF4 の SendMessageChannelCache クラスを使用したクライアント側チャネルのキャッシュのサポートにより、少ない手間で短時間にアプリケーションを作成できます。 詳細については、「Send アクティビティのキャッシュ共有レベルの変更」を参照してください。

宣言型プログラミング

WF4 にはビジネス プロセスおよびサービスをモデル化するための単純明快な宣言型プログラミング フレームワークがあります。 このプログラミング モデルは、完全な宣言型のアクティビティの作成をサポートし、コードの記述をなくしてワークフローの作成を大幅に簡素化します。 .NET Framework 4 では、XAML ベースの宣言型プログラミング フレームワークが 1 つのアセンブリ System.Xaml.dll に統合され、WPF と WF の両方がサポートされます。

WF4 では、XAML で完全な宣言型の記述を行い、ワークフローの定義全体を XML マークアップに定義して、.NET を使用して作成したアクティビティや型を参照できます。 WF3 の XOML 形式の場合、この操作をカスタム分離コード ロジックなしで行うことは困難です。 .NET 4 の新しい XAML スタックではワークフローの成果物をシリアル化/逆シリアル化する場合のパフォーマンスが大幅に向上し、宣言型プログラミングの魅力と信頼性が増しています。

ワークフロー デザイナー

WF4 の完全な宣言型プログラミングのサポートでは、大規模なワークフローのデザイン時のパフォーマンスに関して、より高い要件を明示的に指定します。 WF4 のワークフロー デザイナーは、WF3 のワークフロー デザイナーと比べて大規模なワークフローのスケーラビリティがはるかに向上しています。 UI 仮想化のサポートにより、WF4 のデザイナーは 1,000 のアクティビティがある大規模なワークフローを数秒で簡単に読み込むことができますが、WF3 のデザイナーは数百のアクティビティがあるワークフローを読み込むこともほぼ不可能です。

コンポーネント レベルのパフォーマンス比較

このセクションでは、WF3 ワークフローと WF4 ワークフローの個別のアクティビティを直接比較したデータについて説明します。 永続性などの主要分野は、個々のアクティビティのコンポーネントよりパフォーマンスに重大な影響をもたらします。 それでも、WF4 ではコンポーネントが手動コーディングによる調整ロジックと比肩するほど高速になっているため、個別コンポーネントのパフォーマンスの向上は重要です。 その一例を次のセクション、「サービス構成シナリオ」で説明します。

環境のセットアップ

Environment setup for workflow performance measurement

上の図は、コンポーネント レベルのパフォーマンス測定に使用するコンピューター構成を示しています。 1 台のサーバーと 5 台のクライアントを 1 つの 1 Gbps イーサネット ネットワーク インターフェイスで接続します。 測定を簡素化するため、サーバーは Windows Server 2008 x86 を実行するデュアル プロセッサでクアッド コアのサーバーの 1 つのコアを使用する構成にします。 システムの CPU 利用状況はほぼ 100% で推移します。

テストの詳細

WF3 CodeActivity は WF3 ワークフローで使用可能な最も単純なアクティビティであると考えられます。 アクティビティは、ワークフローのプログラマがカスタム コードを配置できる分離コード内のメソッドを呼び出します。 WF4 には、同様の機能を提供する WF3 の CodeActivity に直接匹敵する機能はありません。 WF4 の CodeActivity 基本クラスは WF3 の CodeActivity とは関連がありません。 ワークフロー作成者には、カスタム アクティビティを作成し、XAML のみのワークフローを構築することをお勧めします。 以下のテストでは、WF4 ワークフローで Comment という名前のアクティビティが空の CodeActivity の代わりに使用されます。 Comment アクティビティのコードを次に示します。

[ContentProperty("Body")]
    public sealed class Comment : CodeActivity
    {
        public Comment()
            : base()
        {
        }

        [DefaultValue(null)]
        public Activity Body
        {
            get;
            set;
        }

        protected override void Execute(CodeActivityContext context)
        {
        }
    }

空のワークフロー

このテストでは、子アクティビティがないシーケンス ワークフローを使用します。

1 つのアクティビティ

ワークフローは子アクティビティを 1 つ含むシーケンス ワークフローです。 アクティビティは、WF3 の場合はコードなしの CodeActivity、WF4 の場合は Comment アクティビティです。

While の 1,000 回反復

このシーケンス ワークフローには 1 つの While アクティビティが含まれ、ループ内に何も処理しない 1 つの子アクティビティを含みます。

Replicator と ParallelForEach の比較

WF3 の ReplicatorActivity には順次実行モードと並列実行モードがあります。 順次モードのアクティビティのパフォーマンスは WhileActivity と同様です。 並列実行で特に便利なアクティビティは ReplicatorActivity です。 このアクティビティに類似した WF4 のアクティビティは ParallelForEach<T> です。

次の図は、このテストに使用するワークフローを示しています。 左側が WF3 ワークフロー、右側が WF4 ワークフローです。

WF3 ReplicatorActivity and WF4 ParallelForEach

5 つのアクティビティがあるシーケンシャル ワークフロー

このテストは、複数のアクティビティを順番に実行した場合の効果を示すことを目的としています。 シーケンス内には 5 つのアクティビティがあります。

トランザクション スコープ

トランザクション スコープ テストはその他のテストと若干異なり、反復のたびに新しいワークフロー インスタンスを作成するのではなく、 ワークフローは処理のない 1 つのアクティビティを含む TransactionScope アクティビティを含む while ループで構造化されます。 while ループで 50 回反復するバッチを 1 回実行するたびに 1 つの操作としてカウントされます。

補正

WF3 ワークフローには WorkScope という名前の補正可能アクティビティが 1 つあります。 このアクティビティは単純に ICompensatableActivity インターフェイスを実装します。

class WorkScope :
        CompositeActivity, ICompensatableActivity
    {
        public WorkScope() : base() { }

        public WorkScope(string name)
        {
            this.Name = name;
        }

        public ActivityExecutionStatus Compensate(
            ActivityExecutionContext executionContext)
        {
            return ActivityExecutionStatus.Closed;
        }
    }

このエラー ハンドラーは、WorkScope アクティビティをターゲットにします。 WF4 ワークフローも同様に単純です。 CompensableActivity には本文と補正ハンドラーがあります。 明示的な補正はシーケンスの次に来ます。 本文アクティビティと補正ハンドラー アクティビティはどちらも空の実装です。

public sealed class CompensableActivityEmptyCompensation : CodeActivity
    {
        public CompensableActivityEmptyCompensation()
            : base() { }

        public Activity Body { get; set; }

        protected override void Execute(CodeActivityContext context) { }
    }
    public sealed class CompensableActivityEmptyBody : CodeActivity
    {
        public CompensableActivityEmptyBody()
            : base() { }

        public Activity Body { get; set; }

        protected override void Execute(CodeActivityContext context) { }
    }

次の図は、基本補正のワークフローを示しています。 左側が WF3 ワークフロー、右側が WF4 ワークフローです。

WF3 and WF4 basic compensation workflows

パフォーマンスのテスト結果

Table showing performance test results data

Column chart comparing WF3 and WF4 performance test data

トランザクション スコープ テスト以外のすべてのテストは 1 秒あたりのワークフローで測定されます。 上で説明したように、WF ランタイムのパフォーマンスは全体的に向上しました。while ループのような同じアクティビティの複数の実行を必要とする分野では特に向上が顕著でした。

サービス構成シナリオ

前のセクション「コンポーネント レベルのパフォーマンス比較」に示したように、WF3 と WF4 を比較した場合、オーバーヘッドは大幅に短縮されています。 WCF ワークフロー サービスは手動でコーディングした WCF サービスにほぼ匹敵するパフォーマンスを実現でき、WF ランタイムのあらゆる利点を備えています。 このテスト シナリオでは、WCF サービスと、WF4 の WCF ワークフロー サービスを比較します。

オンライン ストア サービス

Windows Workflow Foundation の特徴の 1 つは、複数のサービスを使用してプロセスを作成できることです。 この例として、2 つのサービスの呼び出しを調整して発注するオンライン ストア サービスがあります。 まず、注文検証サービスを使用して注文を検証します。 次に、ウェアハウス サービスを使用して注文を受け付けます。

注文検証サービスとウェアハウス サービスの 2 つのバックエンド サービスは両方のテストで不変です。 変更する部分は、調整を行うオンライン ストア サービスです。 1 つのケースでは、そのサービスを WCF サービスとして手動でコーディングします。 もう 1 つのケースでは、そのサービスを WF4 の WCF ワークフロー サービスとして記述します。 追跡や永続化などの WF 固有の機能はこのテストでは無効にします。

環境

Environment setup for performance measurement

クライアントは複数のコンピューターから HTTP 経由でオンライン ストア サービスを要求します。 1 台のコンピューターで 3 つのサービスのすべてをホストします。 オンライン ストア サービスとバックエンド サービスの間にあるトランスポート層は TCP または HTTP です。 1 秒あたりの操作は、オンライン ストア サービスに対する PurchaseOrder の呼び出しが完了した件数に基づいて測定されます。 チャネル プールは WF4 で導入された新機能です。 このテスト チャネル プールの WCF の部分は標準では提供されていないため、オンライン ストア サービスでは単純なプール手法の手動コーディングによる実装を使用しました。

パフォーマンス

Column chart showing online Store Service performance

チャネル プールなしでバックエンド TCP サービスに接続した場合、WF サービスはスループットに 17.2% 影響します。 チャネル プールを使用した場合、スループットは 23.8% 低下します。 HTTP では影響ははるかに小さく、プールなしの場合で 4.3%、プールを使用した場合で 8.1% の影響があります。 HTTP を使用する場合はチャネル プールのメリットがきわめて少ないことも重要な点です。

このテストでは、手動でコーディングした WCF サービスと比べると WF4 ランタイムにオーバーヘッドがありますが、これは最悪の場合のシナリオと考えることができます。 このテストの 2 つのバックエンド サービスはほとんど処理を行いません。 実際のエンド ツー エンドのシナリオでは、これらのサービスでデータベースの呼び出しなどの高負荷の操作を行うため、トランスポート層のパフォーマンスへの影響の重要性は低下します。 また、WF4 で提供されている機能の長所により、Workflow Foundation はオーケストレーション サービスの作成に有効な選択肢になります。

パフォーマンスに関する重要な考慮事項

WF4 では、このセクションの相互運用機能以外の機能分野は WF3 から大幅に変更されています。 この変更はワークフロー アプリケーションのデザインおよびパフォーマンスに影響します。

ワークフローのアクティブ化にかかる待機時間

WCF ワークフロー サービス アプリケーションでは、ブロックが発生する可能性があるため、新規ワークフローの開始または既存ワークフローの読み込みにかかる待機時間は重要です。 このテスト ケースでは、一般的なシナリオで WF3 XOML ホストを WF4 XAMLX ホストと比較して測定します。

環境のセットアップ

Environment setup for latency and throughput tests

テストの設定

このシナリオでは、クライアント コンピューターはコンテキスト ベースの相関関係を使用して WCF ワークフロー サービスに接続します。 コンテキスト相関関係は特殊なコンテキスト バインドを必要とし、コンテキスト ヘッダーまたはクッキーを使用してメッセージを適切なワークフロー インスタンスに関連付けます。 相関 ID がメッセージ ヘッダーに存在するため、メッセージ本文を解析する必要がないというパフォーマンス上の利点があります。

サービスは要求を行う新しいワークフローを作成し、ワークフローの実行にかかった時間が待機時間の測定に含まれないように、すぐに応答を送信します。 WF3 のワークフローは XOML と分離コードで構成され、WF4 のワークフローは全体が XAML で構成されます。 WF4 のワークフローは次のようになります。

WF4 Correlation Scope workflow

Receive アクティビティはワークフロー インスタンスを作成します。 受信メッセージに渡した値は応答メッセージにエコーされます。 応答の後のシーケンスにはワークフローの残りの部分が含まれます。 前の例では 1 つの Comment アクティビティのみを示しました。 ワークフローの複雑さをシミュレーションするには、Comment アクティビティの数を変更します。 comment アクティビティは何も処理しない WF3 の CodeActivity と同等です。 Comment アクティビティの詳細については、この記事の前のセクション「コンポーネント レベルのパフォーマンス比較」を参照してください。

テスト結果

WCF ワークフロー サービスのコールドおよびウォーム待機時間:

Column chart showing cold and warm latency for WCF workflow services using WF3 and WF4

前のグラフのコールドは、特定ワークフローに既存の WorkflowServiceHost が存在しない場合を示します。 つまり、コールド待機時間は、ワークフローが初めて使用され、XOML または XAML をコンパイルする必要がある場合の待機時間です。 ウォーム待機時間は、ワークフロー型が既にコンパイル済みの場合に新規ワークフロー インスタンスを作成する時間です。 WF4 ではワークフローの複雑さによって大きな差は生じませんが、WF3 ではワークフローの複雑さに応じて時間がかかります。

相関スループット

WF4 では新しいコンテンツ ベースの相関関係機能が導入されました。 WF3 ではコンテキスト ベースの相関関係のみが提供されていました。 コンテキスト ベースの相関関係は特定の WCF チャネル バインドに対してのみ実行可能です。 チャネル バインドを使用する際に、ワークフロー ID がメッセージ ヘッダーに挿入されます。 WF3 ランタイムでは、ワークフローをその ID 以外で識別することはできません。コンテンツ ベースの相関関係では、ワークフロー作成者はアカウント番号や顧客 ID などの関連するデータから相関関係キーを作成できます。

コンテキスト ベースの相関関係には、相関関係キーがメッセージ ヘッダーに存在するというパフォーマンス上の利点があります。 逆シリアル化やメッセージのコピーを行わずに相関関係キーをメッセージから読み取ることができます。 コンテンツ ベースの相関関係では、相関関係キーはメッセージ本文に格納されます。 キーの検索には XPath 式を使用します。 この余分な処理のコストは、メッセージのサイズ、本文のキーの深さ、キーの数によって決まります。 このテストでは、コンテキスト ベースの相関関係とコンテンツ ベースの相関関係を比較し、複数のキーを使用した場合のパフォーマンス低下を示します。

環境のセットアップ

Environment setup for workflow performance test

テストの設定

Correlation Throughput Workflow Test

前のワークフローは、「永続化」セクションで使用されているものと同じです。 永続化なしの相関関係テストでは、ランタイムに永続化プロバイダーがインストールされていません。 相関関係は CreateOrder と CompleteOrder の 2 か所で発生します。

テスト結果

Correlation Throughput

この図は、コンテンツ ベースの相関関係で使用するキーの数が増えた場合のパフォーマンスの低下を示しています。 TCP と HTTP の曲線の類似性は、これらのプロトコルに関連するオーバーヘッドを示しています。

永続化を伴う相関関係

永続化されたワークフローでは、コンテンツ ベースの相関関係からの CPU 圧迫はワークフロー ランタイムから SQL データベースに移行します。 SQL 永続化プロバイダー内のストアド プロシージャは、適切なワークフローを検索するためにキーの照合作業を実行します。

Line chart showing correlation and persistence results

コンテキスト ベースの相関関係は、コンテンツ ベースの相関関係よりも高速です。 ただし、永続化の方が相関関係よりパフォーマンスへの影響が大きいため、違いはあまり顕著ではありません。

複合ワークフローのスループット

ワークフローの複雑さを測定する要素はアクティビティの数だけではありません。 複合アクティビティに複数の子が含まれ、その子も複合アクティビティである場合があります。 入れ子のレベルの数が増えるほど、現在実行中の状態になり得るアクティビティの数と変数の数も増えます。 このテストでは、複合ワークフローを実行する場合の WF3 と WF4 のスループットを比較します。

テストの設定

これらのテストは、4 GB の RAM を搭載した Intel Xeon X5355 @ 2.66 GHz 4 ウェイのコンピューターで Windows Server 2008 x64 を使用して実行されました。 テスト コードは、CPU 使用率が 100% に達するように、1 コアあたり 1 つのスレッドを使用する 1 つのプロセスで実行します。

このテスト用に生成されたワークフローには、各シーケンスのアクティビティの深さと数という 2 つの主な変数があります。 各深さレベルには Parallel アクティビティ、while ループ、判断、割り当て、シーケンスが含まれます。 下図の WF4 のデザイナーは、トップレベルのフローチャートを示しています。 各フローチャートのアクティビティはメイン フローチャートと相似しています。 深さがテストのパラメーターに制限されるこのワークフローを図示する場合、フラクタルを考慮すると役立つことがあります。

特定のテストのアクティビティの数は、1 シーケンスあたりのアクティビティの深さと数によって決まります。 WF4 のテストのアクティビティの数は、次の数式によって計算します。

Equation to compute number of activities

WF3 のテストには追加シーケンスがあるため、アクティビティの数を計算する数式が多少異なります。

Equation to compute number of WF3 activities

d は 1 シーケンスあたりのアクティビティの深さ、a は 1 シーケンスあたりのアクティビティの数を表します。 これらの数式では、最初の定数 × a はシーケンスの数を表し、2 番目の定数は現在のレベルのアクティビティの静的な数を表します。 各フローチャートに 3 つのフローチャート子アクティビティがあります。 ボトム レベルの深さのフローチャートは空で、それ以外のレベルにはメイン フローチャートのコピーがあります。 各テスト バリエーションのワークフロー定義内のアクティビティの数を次の表に示します。

A table that shows the number of activities used in each test

ワークフロー定義内のアクティビティの数は、各深さレベルに従って急激に増加します。 ただし、特定のワークフロー インスタンスで実行されるパスは判断ポイントごとに 1 つのみであるため、実際のアクティビティのごく小規模なサブセットのみが実行されます。

Flowchart of the complex throughput workflow

同等のワークフローが WF3 用にも作成されました。 WF3 デザイナーは入れ子ではなく、ワークフロー全体をデザイン領域に表示するので、このトピックの表示内容としては大きすぎます。 ワークフローのスニペットを以下に示します。

Flowchart snippet of the WF3 workflow

入れ子の極端なケースを実行するため、このテストの一部となるもう 1 つのワークフローでは 100 回入れ子になったシーケンスを使用します。 最も内側のシーケンスには 1 つの Comment または CodeActivity があります。

Flowchart of a nested sequence

追跡および永続化はこのテストに含まれません。

テスト結果

Column chart showing throughput performance results

アクティビティの深さと数が多い複合ワークフローの場合でも、パフォーマンス結果は前述した他のスループット数値と整合しています。 WF4 のスループットは桁違いに高速なため、対数スケールで比較する必要があります。

メモリ

Windows Workflow Foundation のメモリ オーバーヘッドは、2 つの重要な要素であるワークフローの複雑さとワークフロー定義の数で測定されます。 メモリの測定は Windows 7 の 64 ビット ワークステーションで行われました。 ワーキング セットのサイズの測定データを取得するには、パフォーマンス カウンターを監視する、Environment.WorkingSet をポーリングする、VMMap から提供されている VMMap などのツールを使用するなど、さまざまな方法があります。 ここでは複数の方法を組み合わせて各テストの結果を取得し、検証しました。

ワークフローの複雑さテスト

ワークフローの複雑さテストでは、ワークフローの複雑さに基づいてワーキング セットの違いを測定します。 前のセクションで使用した複合ワークフローの他に、1 つのアクティビティ ワークフローと 1,000 のアクティビティがあるシーケンスという 2 つの基本ケースを取り入れるための新しいバリエーションを追加します。 これらのテストでは、ワークフローを初期化し、1 分間の単一シリアル ループ内で完了するまで実行します。 各テスト バリエーションを 3 回実行し、その平均データを記録しました。

新しい 2 つの基本テストには次のようなワークフローがあります。

Complex workflow for both WF3 and WF4

前の WF3 のワークフローでは、空の CodeActivity アクティビティを使用しています。 前の WF4 ワークフローでは Comment アクティビティを使用しています。 Comment アクティビティについては前の「コンポーネント レベルのパフォーマンス比較」で説明しました。

Column chart showing complex workflow memory usage for WF3 and WF4 workflows

この図で注目すべき明白な傾向の 1 つは、WF3 および WF4 のいずれの場合も入れ子がメモリ使用量に与える影響が比較的小さいことです。 メモリに最も重大な影響をもたらす要因はワークフロー内のアクティビティの数です。 シーケンス 1,000、複合深さ 5 シーケンス 5、複合深さ 7 シーケンス 1 (バリエーションあり) の各データから、アクティビティの数が千単位になるとメモリ使用量の増加が顕著になることが明らかです。 最大 29 K のアクティビティが存在する極端なケース (深さ 7 シーケンス 1) では、WF4 のメモリ使用量は WF3 より約 79% 少なくなっています。

複数のワークフロー定義のテスト

ワークフロー定義ごとのメモリの測定は、WF3 と WF4 でワークフローのホスティングに使用できるオプションが原因で 2 つの異なるテストに分かれています。 このテストでは、ワークフローの複雑さテストと異なり、指定されたワークフローのインスタンス化と実行を定義ごとに 1 回だけ行います。 これは、ワークフロー定義とそのホストは AppDomain の有効期間中はメモリ内に保持されるためです。 指定されたワークフロー インスタンスの実行によって使用されたメモリは、ガベージ コレクション中にクリーンアップする必要があります。 WF4 の移行ガイドラインに、ホスト オプションの詳細が記載されています。 詳細については、「WF 移行のクックブック: ワークフロー ホスティング」を参照してください。

ワークフロー定義テスト用に多数のワークフロー定義を作成するには、いくつかの方法があります。 たとえば、コード生成を使用して、名前以外は同一の 1,000 のワークフローを作成し、各ワークフローを個別のファイルに保存します。 この方法はコンソール ホストのテストで使用しました。 WF3 では、ワークフロー定義の実行に WorkflowRuntime クラスを使用します。 WF4 は WorkflowApplication を使用して単一ワークフローのインスタンスを作成するか、直接 WorkflowInvoker を使用してアクティビティがメソッドに呼び出された場合のように実行できます。 WorkflowApplication は単一ワークフローのインスタンスのホストで、WorkflowRuntime に類似した機能を持つため、このテストで使用しました。

IIS でワークフローをホストする場合、VirtualPathProvider を使用することで、すべての XAMLX または XOML ファイルを生成するのではなく、新しい WorkflowServiceHost を作成することができます。 VirtualPathProvider は受信要求を処理し、データベースから読み込み可能な、またはこの場合は実行時に生成可能な "仮想ファイル" を使用して応答します。 したがって、1,000 個の物理ファイルを作成する必要はありません。

コンソール テストで使用したワークフロー定義はアクティビティが 1 つの単純なシーケンシャル ワークフローです。 1 つのアクティビティは、WF3 の場合は空の CodeActivity、WF4 の場合は Comment アクティビティでした。 IIS でホストするケースでは、メッセージの受信で開始し、応答の送信で終了するワークフローを使用しました。

次の画像は、ReceiveActivity を使用した WF3 ワークフローと、要求/応答パターンを使用した WF4 ワークフローを示しています。

Workflow Services in WF3 and WF4

次の表に、1 つのワークフロー定義と 1,001 の定義との間のワーキング セットのデルタを示します。

ホスト オプション WF3 のワーキング セット デルタ WF4 のワーキング セット デルタ
コンソール アプリケーションでホストされるワークフロー 18 MB 9 MB
IIS でホストされるワークフロー サービス 446 MB 364 MB

IIS のホスト ワークフロー定義の方がメモリ使用量がはるかに多いのは、WorkflowServiceHost、詳細な WCF サービス成果物、およびホストに関連付けられているメッセージ処理ロジックが原因です。

WF3 のコンソール ホストでは、ワークフローは XOML ではなくコードで実装されています。 WF4 では既定で XAML を使用します。 XAML は埋め込みリソースとしてアセンブリに格納され、実行時にコンパイルされてワークフローの実装を提供します。 このプロセスにはある程度のオーバーヘッドが伴います。 WF3 と WF4 を公正に比較するため、XAML ではなくコード化されたワークフローを使用しました。 WF4 ワークフローの例を次に示します。

public class Workflow1 : Activity
{
    protected override Func<Activity> Implementation
    {
        get
        {
            return new Func<Activity>(() =>
            {
                return new Sequence
                {
                    Activities = {
                        new Comment()
                    }
                };
            });
        }
        set
        {
            base.Implementation = value;
        }
    }
}

その他にもメモリ消費量に影響を与えるさまざまな要因があります。 すべてのマネージド プログラムに対しても同じアドバイスが当てはまります。 IIS ホスト環境では、ワークフロー定義に対して作成された WorkflowServiceHost オブジェクトはアプリケーション プールがリサイクルされるまでメモリに保持されます。 拡張を書き込むときは、この点に注意してください。 また、グローバル変数 (ワークフロー全体をスコープとする変数) の使用を避け、変数のスコープをできるだけ制限することをお勧めします。

ワークフロー ランタイムのサービス

永続化

WF3 と WF4 には SQL 永続化プロバイダーが付属しています。 WF3 の SQL 永続化プロバイダーはワークフロー インスタンスをシリアル化して BLOB に格納する単純な実装です。 そのため、このプロバイダーのパフォーマンスはワークフロー インスタンスのサイズに大きく依存します。 WF3 では、前述したさまざまな理由からインスタンスのサイズは大きくなる可能性があります。 シリアル化されたインスタンスをデータベースに格納するとワークフローの状態を視認できないため、多くの場合に顧客は既定の SQL 永続化プロバイダーを使用しません。 ワークフロー ID が不明な場合、特定のワークフローを探すには、永続化された各インスタンスを逆シリアル化して内容を確認する必要があります。 多くの開発者は、この不都合を解消するために独自の永続化プロバイダーを作成することを選択します。

WF4 の SQL 永続化プロバイダーでは、この問題の一部に対処することを試みました。 永続化テーブルは、アクティブなブックマークや昇格可能なプロパティなどの一定の情報を公開します。 WF4 の新しいコンテンツ ベースの相関関係機能は WF3 の SQL 永続化手法では適切に機能せず、永続化されたワークフロー インスタンスの構成に変更が生じます。 これにより永続化プロバイダーの処理が複雑化し、データベースに余分な負荷がかかります。

環境のセットアップ

Environment setup for workflow performance test

テストの設定

機能セットを向上させ、コンカレンシー処理の設定を改善した場合でも、WF4 の SQL 永続化プロバイダーは WF3 のプロバイダーより高速です。 このことを示すため、WF3 と WF4 で同じ操作をする 2 つのワークフローの比較を次に示します。

Persistence workflow in WF3 on left and WF4 on right

2 つのワークフローは、いずれも受信したメッセージによって作成されます。 最初に応答を送信すると、ワークフローが永続化されます。 WF3 の場合は、空の TransactionScopeActivity を使用して永続化を開始します。 WF3 ではアクティビティを "クローズ時に保持" としてマークすることによって、同じ操作を行うこともできます。2 番目の、関連付けられたメッセージによってワークフローを完了します。 ワークフローは永続化されますが、アンロードされません。

テスト結果

Column chart showing throughput persistence

クライアントと中間層とのトランスポートが HTTP の場合、WF4 の永続化ではパフォーマンスが 2.6 倍向上し、 TCP トランスポートの場合では 3.0 倍向上しています。 いずれの場合も、中間層の CPU 使用率は 98% 以上です。 WF4 の方がスループットが高い理由はワークフロー ランタイムが高速であるためです。 シリアル化されたインスタンスのサイズはどちらの場合も小さく、ここでは大きな要因になりません。

このテストでは、WF3 と WF4 の両方のワークフローに、永続化するときを明示的に示すアクティビティを使用します。 この方法にはワークフローをアンロードせずに永続化できるという利点があります。 WF3 では TimeToUnload 機能を使用して永続化することもできますが、この機能はワークフロー インスタンスをメモリからアンロードします。 WF3 では、特定の時点でワークフローが永続化されていることを確認する場合、ワークフロー定義を変更するか、ワークフロー インスタンスをアンロードして再読み込みする必要があります。 WF4 の新機能 TimeToPersist を使用すると、アンロードせずに永続化することができます。 この機能ではワークフロー インスタンスをアイドル状態で永続化することが可能で、TimeToUnload のしきい値に達するか、実行が再開されるまでワークフロー インスタンスがメモリに保持されます。

WF4 の SQL 永続化プロバイダーはデータベース層でより多くの作業を実行することに注意してください。 SQL データベースはボトルネックになる可能性があるため、CPU およびディスクの使用量を監視することが重要です。 ワークフロー アプリケーションのパフォーマンス テストを行う場合、必ず SQL データベースから次のパフォーマンス カウンターを含めてください。

  • PhysicalDisk\% Disk Read Time

  • PhysicalDisk\% Disk Time

  • PhysicalDisk\% Disk Write Time

  • PhysicalDisk\% Avg. Disk Queue Length

  • PhysicalDisk\Avg.Disk Read Queue Length

  • PhysicalDisk\Avg.Disk Write Queue Length

  • PhysicalDisk\Current Disk Queue Length

  • Processor Information\% Processor Time

  • SQLServer:Latches\Average Latch Wait Time (ms)

  • SQLServer:Latches\Latch Waits/sec

追跡

ワークフロー追跡機能を使用して、ワークフローの進行状況を管理できます。 追跡イベントに含まれる情報は追跡プロファイルによって決まります。 追跡プロファイルが複雑になるほど、追跡の負荷が大きくなります。

WF3 には SQL ベースの追跡サービスが付属しています。 このサービスはバッチ モードと非バッチ モードで動作します。 非バッチ モードでは、イベントの追跡はデータベースに直接書き込まれます。 バッチ モードでは、追跡イベントは同じバッチ内にワークフロー インスタンスの状態として収集されます。 バッチ モードは多様なワークフロー デザインを高パフォーマンスで処理します。 ただし、ワークフローが永続化せずに多数のアクティビティを実行し、そのアクティビティを追跡する場合、バッチ処理によってパフォーマンスが低下する可能性があります。 このような状態は一般にループで発生するので、大規模なループに永続化ポイントを含めることが最善の回避策になります。 ループに永続化ポイントを導入することによってもパフォーマンスが低下する可能性があるので、両者のコストを測定してバランスを考慮することが重要です。

WF4 には SQL 追跡サービスは付属していません。 SQL データベースへの追跡情報の記録は .NET Framework に組み込むよりもアプリケーション サーバーから処理する方が適しているため、 SQL 追跡は AppFabric によって処理するようになりました。 WF4 の標準追跡プロバイダーは Event Tracing for Windows (ETW) に基づいています。

ETW は Windows に組み込まれた、カーネル レベルの待機時間が短いイベント システムです。 このシステムは、実際に使用したときにのみイベント追跡のコストが発生するようにすることが可能なプロバイダー/コンシューマー モデルを使用しています。 プロセッサ、ディスク、メモリ、ネットワーク使用量などのカーネル イベント以外にも、多くのアプリケーションが ETW を利用しています。 ETW イベントは、イベントをアプリケーションに応じてカスタマイズできるという点でパフォーマンス カウンターより高機能です。 イベントにワークフロー ID や情報メッセージなどのテキストを含めることができます。 また、すべてのイベントをキャプチャするより一定のイベントのサブセットを使用した方がパフォーマンスへの影響が少ないため、イベントはビットマスクを使用して分類されています。

追跡に SQL ではなく ETW を使用する方法には次の利点があります。

  • 追跡イベントの収集を別のプロセスに分離できます。 これによりイベントの記録方法の柔軟性が高まります。

  • ETW の追跡イベントは WCF ETW イベントまたは SQL Server やカーネル プロバイダーなどのその他の ETW プロバイダーと容易に組み合わせることができます。

  • ワークフローの作成者は、WF3 の SQL 追跡サービスのバッチ モードなど、特定の追跡の実装を改良するためにワークフローを変更する必要がありません。

  • 管理者はホスト プロセスをリサイクルすることなく追跡をオンまたはオフにできます。

ETW による追跡にはパフォーマンス上の利点と同時に欠点もあります。 システムが極度のリソース圧迫にさらされると、ETW イベントは失われる可能性があります。 イベントの処理は通常のプログラムの実行をブロックすることを目的としていないため、すべての ETW イベントがサブスクライバーにブロードキャストされることは保証されません。 そのため、ETW 追跡は状態監視に適していますが、監査には適しません。

WF4 には SQL 追跡プロバイダーはありませんが、AppFabric に SQL 追跡プロバイダーがあります。 AppFabric の SQL 追跡では、クイック挿入用にイベントをバッチ処理して SQL テーブルに書き込む Windows サービスを使用して、ETW イベントに定期受信します。 別のジョブでこのテーブルからデータを排出し、形式を変更して AppFabric ダッシュボードに表示可能なレポート テーブルに格納します。 追跡イベントのバッチは元になるワークフローとは独立して処理されるため、永続化ポイントを待機せずに記録されます。

ETW イベントの記録には logman や xperf などのツールを使用できます。 小規模な ETL ファイルは xperfview などのツールで表示できます。または、tracerpt を使用して XML などのわかりやすい形式に変換することもできます。 WF3 では、SQL データベースなしで追跡イベントを取得する唯一の方法は、カスタム追跡サービスを作成することです。 ETW の詳細については、「WCF サービスと Event Tracing for Windows」およびイベント トレース - Windows アプリケーションに関するページを参照してください。

ワークフロー追跡を有効にした場合のパフォーマンスへの影響の程度はさまざまです。 次のベンチマークでは、logman ツールを使用し、ETW 追跡イベントを使用してそのイベントを ETL ファイルに記録します。 AppFabric の SQL 追跡のコストについてはこの記事の対象外です。 このベンチマークには AppFabric でも使用される基本的な追跡プロファイルが示されています。 状態監視イベントのみを追跡した場合のコストも含まれています。 これらのイベントは問題をトラブルシューティングし、システムの平均スループットを確認するために役立ちます。

環境のセットアップ

Environment setup for workflow performance test

テスト結果

Column chart showing workflow tracking costs

状態監視はスループットに約 3% の影響をもたらしています。 基本プロファイルのコストは約 8% です。

Interop

WF4 では WF をほぼ全面的に変更しているため、WF3 のワークフローおよびアクティビティは WF4 とは直接互換性がありません。 Windows Workflow Foundation を早期に採用した多くの顧客は、WF3 用の自社製またはサードパーティ製のワークフロー定義とカスタム アクティビティを有しています。 WF4 への移行を簡素化するには、Interop アクティビティを使用する方法があります。この方法により WF4 ワークフロー内から WF3 アクティビティを実行できます。 Interop アクティビティは必要な場合にのみ使用することをお勧めします。 WF4 への移行の詳細については、WF4 への移行ガイダンスを参照してください。

環境のセットアップ

Environment setup for workflow performance test

テスト結果

次の表に、シーケンスに 5 つのアクティビティを含むワークフローのさまざまな構成での実行結果を示します。

Test スループット (ワークフロー/秒)
WF3 ランタイムの WF3 シーケンス 1,576
Interop を使用した WF4 ランタイムの WF3 シーケンス 2,745
WF4 シーケンス 153,582

Interop を使用した場合の方が直接 WF3 を使用した場合よりパフォーマンスが顕著に高くなっていますが、 WF4 アクティビティと比較した場合、パフォーマンスの向上はごくわずかです。

まとめ

WF4 のパフォーマンスに対する多大な投資は多くの重要な面で成果をもたらしました。 個々のワークフロー コンポーネントのパフォーマンスは、WF ランタイムが効率的になることにより、WF4 が WF3 と比較して数百倍高速になる場合があります。 待機時間の数値も大幅に向上しています。 WF を使用することによる利点を考慮すると、手動コーディングの WCF オーケストレーション サービスと比べて、WF を使用した場合のパフォーマンスの低下はごく些細です。 永続化のパフォーマンスは 2.5 ~ 3.0 倍向上しています。 ワークフロー追跡による状態監視のオーバーヘッドもごくわずかになりました。 WF3 から WF4 への移行を検討する場合の包括的な移行ガイドも提供されています。 これらのあらゆる要素を考慮すると、WF4 は複雑なアプリケーションの作成において魅力的な選択肢です。