2016-03-24 9 views
2

私はakka.netで稼働していることを証明しようとしています。私は間違って何かをしていると確信していますが、それが何であるか把握できません。私のAkka.Netデモは信じられないほど遅いです

私の俳優にノードのグラフを形成させたい。その後、これはビジネスobjektsの複雑なグラフになりますが、今のところ私はこのような単純な線形構造を試してみたい:

enter image description here

は私が9歩の距離である隣人のためにノードをお願いしたいと思います。私はこれを再帰的に実装しようとしています。私は9ステップ離れた隣人のためにノード#9を尋ねる、そして、8ステップ離れたところにある隣人のためにノード#8に尋ねる。最後に、これは答えとしてノード#0を返すべきです。

私のコードはうまく動作しますが、4秒以上が実行されます。何故ですか?

これは私の完全なコードリストである:

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using Akka; 
using Akka.Actor; 

namespace AkkaTest 
{ 
    class Program 
    { 
     public static Stopwatch stopwatch = new Stopwatch(); 
     static void Main(string[] args) 
     { 
      var system = ActorSystem.Create("MySystem"); 

      IActorRef[] current = new IActorRef[0]; 

      Console.WriteLine("Initializing actors..."); 

      for (int i = 0; i < 10; i++) 
      { 
       var current1 = current; 
       var props = Props.Create<Obj>(() => new Obj(current1, Guid.NewGuid())); 
       var actorRef = system.ActorOf(props, i.ToString()); 
       current = new[] { actorRef }; 
      } 
      Console.WriteLine("actors initialized."); 

      FindNeighboursRequest r = new FindNeighboursRequest(9); 

      stopwatch.Start(); 

      var response = current[0].Ask(r); 
      FindNeighboursResponse result = (FindNeighboursResponse)response.Result; 
      stopwatch.Stop(); 
      foreach (var d in result.FoundNeighbours) 
      { 
       Console.WriteLine(d); 
      } 

      Console.WriteLine("Search took " + stopwatch.ElapsedMilliseconds + "ms."); 
      Console.ReadLine(); 
     } 
    } 
    public class FindNeighboursRequest 
    { 
     public FindNeighboursRequest(int distance) 
     { 
      this.Distance = distance; 
     } 
     public int Distance { get; private set; } 
    } 

    public class FindNeighboursResponse 
    { 
     private IActorRef[] foundNeighbours; 

     public FindNeighboursResponse(IEnumerable<IActorRef> descendants) 
     { 
      this.foundNeighbours = descendants.ToArray(); 
     } 

     public IActorRef[] FoundNeighbours 
     { 
      get { return this.foundNeighbours; } 
     } 
    } 


    public class Obj : ReceiveActor 
    { 
     private Guid objGuid; 
     readonly List<IActorRef> neighbours = new List<IActorRef>(); 
     public Obj(IEnumerable<IActorRef> otherObjs, Guid objGuid) 
     { 
      this.neighbours.AddRange(otherObjs); 
      this.objGuid = objGuid; 
      Receive<FindNeighboursRequest>(r => handleFindNeighbourRequest(r)); 
     } 

     public Obj() 
     { 
     } 

     private async void handleFindNeighbourRequest (FindNeighboursRequest r) 
     { 
      if (r.Distance == 0) 
      { 
       FindNeighboursResponse response = new FindNeighboursResponse(new IActorRef[] { Self }); 
       Sender.Tell(response, Self); 
       return; 
      } 

      List<FindNeighboursResponse> responses = new List<FindNeighboursResponse>(); 

      foreach (var actorRef in neighbours) 
      { 
       FindNeighboursRequest req = new FindNeighboursRequest(r.Distance - 1); 
       var response2 = actorRef.Ask(req); 
       responses.Add((FindNeighboursResponse)response2.Result); 
      } 

      FindNeighboursResponse response3 = new FindNeighboursResponse(responses.SelectMany(rx => rx.FoundNeighbours)); 
      Sender.Tell(response3, Self); 
     } 
    } 
} 
+0

ActorSystemの作成後にストップウォッチを開始して再試行できますか? –

+0

それはまさにコードがすることです - 少なくとも私が意図したものです。 'for(int i = 0; i <10; i ++)'ループの後にストップウォッチを開始します。 – user1691896

+0

ああ私は間違っている。 Horusiathが言ったように、俳優たちはブロックしてはいけません。あなたはスレッドプールの飢えを経験しています。 –

答えて

4

、このような遅い行動の理由は、あなたが求める使う方法です(あなたがそれを使用することが、私はこの後に説明します)。あなたの例では、ループ内の各ネイバーに依頼していて、現在アクティブなアクター(およびスレッドが存在するスレッド)をアクティブにブロックしているresponse2.Resultを即座に実行しています。つまり、本質的にはブロッキングを伴う同期フローを作ります。

これを修正する最も簡単な方法は、返されたすべてのタスクをAskから収集し、ループ内でそれぞれを待つことなく、それらをすべて収集することです。Task.WhenAllこの例を取る:

public class Obj : ReceiveActor 
{ 
    private readonly IActorRef[] _neighbours; 
    private readonly Guid _id; 

    public Obj(IActorRef[] neighbours, Guid id) 
    { 
     _neighbours = neighbours; 
     _id = id; 
     Receive<FindNeighboursRequest>(async r => 
     { 
      if (r.Distance == 0) Sender.Tell(new FindNeighboursResponse(new[] {Self})); 
      else 
      { 
       var request = new FindNeighboursRequest(r.Distance - 1); 
       var replies = _neighbours.Select(neighbour => neighbour.Ask<FindNeighboursResponse>(request)); 
       var ready = await Task.WhenAll(replies); 
       var responses = ready.SelectMany(x => x.FoundNeighbours); 
       Sender.Tell(new FindNeighboursResponse(responses.ToArray())); 
      } 
     }); 
    } 
} 

これははるかに高速です。

:あなたが俳優の内部で掲載し使用することはできません一般的には:Askを使用して、一般的にように、各が、現在俳優の内部にリスナーを割り当てて尋ねる

  1. はとメッセージを渡すよりもLOT重いですTell
  2. アクター連鎖を通じてメッセージを送信する場合、尋ねるコストは、各アクターを通じてさらにメッセージを2回(要求用と応答用)転送します。一般的なパターンの1つは、A ⇒ B ⇒ C ⇒ Dからの要求を送信し、DからAに応答する場合、メッセージ全体をチェーンバックで渡す必要なく、直接D ⇒ Aに返信することができます。通常はForward/Tellの組み合わせが効果的です。
  3. 一般的に、受信バージョンの非同期バージョンは使用しないでください。現時点では、同期バージョンと比較してアクターの方が遅いです。
+0

偉大な答え、Horusiath。実装をA⇒B⇒C⇒Dのパターンに変更し、DからDへ直接戻してみます。このアプローチには1つの問題がありますが、Aさんはすべての返答を返したことをどのようにして知っていますか? – user1691896

+0

すべての必要なノードが確実にトラバースされるようにするには、ノードのセットとその深さを追跡し、応答した後でそれらをセットから削除する必要があります。他の要件(すなわち、Xノードのみを使用する)では、Forward/Tellバックアプローチがより実現可能であるが、追加の制限なしで、ここでAskを使用することは正当化されるようである。 – Horusiath

関連する問題