2010-03-19 2 views
8

Je suis en train de mettre en œuvre un modèle que je lis sur le blog de Don SymeAsync.Parallel ou Array.Parallel.Map?

(http://blogs.msdn.com/dsyme/archive/2010/01/09/async-and-parallel-design-patterns-in-f-parallelizing-cpu-and-i-o-computations.aspx)

ce qui suggère qu'il existe des possibilités d'amélioration massives de performance de tirer parti d'E/S asynchrone. J'essaie actuellement de prendre un morceau de code qui "fonctionne" d'une manière, en utilisant Array.Parallel.Map, et de voir si je peux obtenir le même résultat en utilisant Async.Parallel, mais je ne comprends vraiment pas Async.Parallel, et ne peut rien faire pour travailler.

J'ai un morceau de code (simplifié ci-dessous pour illustrer le point) qui récupère avec succès un tableau de données pour un cusip. (Une série de prix, par exemple)

let getStockData cusip = 
    let D = DataProvider() 
    let arr = D.GetPriceSeries(cusip) 
    return arr 

let data = Array.Parallel.map (fun x -> getStockData x) stockCusips 

donc cette approche contructs un tableau de tableaux, en faisant une connexion sur Internet à mon fournisseur de données pour chaque stock (qui pourrait être autant que 3000) et me retourne un tableau de tableaux (1 par stock, avec une série de prix pour chacun). Je ne comprends pas ce qui se passe en dessous de Array.Parallel.map, mais je me demande si c'est un scénario où il y a des ressources gaspillées sous le capot, et cela pourrait être plus rapide en utilisant des E/S asynchrones? Donc pour tester cela, j'ai essayé d'utiliser cette fonction en utilisant asyncs, et je pense que la fonction ci-dessous suit le modèle dans l'article de Don Syme utilisant les URL, mais ne compilera pas avec "let!".

let getStockDataAsync cusip = 
    async { let D = DataProvider() 
      let! arr = D.GetData(cusip) 
      return arr 
      } 

L'erreur que je reçois est: "let" Cette expression devait avoir le type Async < « a> mais ici est de type obj

Il compile très bien avec "laisser" au lieu de, mais Je pensais que tout le point était que vous avez besoin du point d'exclamation pour que la commande s'exécute sans bloquer un fil. Donc la première question est: qu'est-ce qui ne va pas avec ma syntaxe ci-dessus, dans getStockDataAsync, puis à un niveau supérieur, quelqu'un peut-il donner un aperçu supplémentaire sur les E/S asynchrones et si le scénario que j'ai présenté en bénéficierait? , ce qui rend potentiellement beaucoup, beaucoup plus rapide que Array.Parallel.map? Merci beaucoup.

Répondre

18

Les flux de travail asynchrones F # vous permettent d'implémenter des calculs asynchrones, cependant, F # fait une distinction entre le calcul habituel et les calculs asynchrones . Cette différence est suivie par le système de types. Par exemple, une méthode qui télécharge une page Web et qui est synchrone a un type string -> string (en prenant l'URL et en renvoyant le code HTML), mais une méthode qui fait la même chose de manière asynchrone a un type string -> Async<string>. Dans le bloc async, vous pouvez utiliser let! pour appeler opérations asynchrones, mais toutes les autres méthodes (standard synchrones) doivent être appelées en utilisant let. Maintenant, le problème avec votre exemple est que l'opération GetData est une méthode synchrone ordinaire, donc vous ne pouvez pas l'appeler avec let!.

Dans le scénario typique F #, si vous voulez faire le asynchrone membre GetData, vous aurez besoin de mettre en œuvre à l'aide d'un flux de travail asynchrone, vous aurez également besoin de l'envelopper dans le bloc async. À un certain point, vous atteindrez un emplacement où vous devez vraiment exécuter une opération primitive de façon asynchrone (par exemple, en téléchargeant des données à partir d'un site Web). F # fournit plusieurs opérations asynchrones primitives que vous pouvez appeler depuis le bloc async en utilisant let! tel que AsyncGetResponse (qui est une version asynchrone de la méthode GetResponse).Donc, dans votre méthode GetData, vous par exemple écrire quelque chose comme ceci:

let GetData (url:string) = async { 
    let req = WebRequest.Create(url) 
    let! rsp = req.AsyncGetResponse() 
    use stream = rsp.GetResponseStream() 
    use reader = new System.IO.StreamReader(stream) 
    let html = reader.AsyncReadToEnd() 
    return CalculateResult(html) } 

Le résumé est que vous devez identifier certaines opérations asynchrones primitives (telles que l'attente pour le serveur Web ou pour le système de fichiers) , utilisez des opérations asynchrones primitives à ce stade et enveloppez tout le code qui utilise ces opérations dans async blocs. Si aucune opération de primitive ne peut être exécutée de manière asynchrone, votre code est lié au processeur et vous pouvez simplement utiliser Parallel.map. J'espère que cela vous aidera à comprendre comment fonctionnent les flux de travail asynchrones F #. Pour plus d'informations, vous pouvez par exemple jeter un oeil à Don Syme's blog post, série sur asynchronous programming by Robert Pickering, ou mon F# web cast.

+0

Merci pour l'explication détaillée. C'était très utile. – user297400

5

@Tomas a déjà une bonne réponse. Je vais juste dire quelques bits en plus.

L'idiome de F # asyncs est de nommer la méthode avec un préfixe "Async" (AsyncFoo, pas FooAsync, ce dernier étant un idiome déjà utilisé par une autre technologie .NET). Vos fonctions doivent donc être getStockData et asyncGetStockData.

l'intérieur d'un flux de travail async, chaque fois que vous utilisez let! au lieu de let ou do! au lieu de do, la chose à droite devrait avoir le type Async<T> au lieu de T. Fondamentalement, vous avez besoin d'un calcul asynchrone existant pour «aller async» à ce stade du workflow. Chaque Async<T> sera lui-même soit un autre flux de travail async{...}, soit une "primitive" asynchrone. Les primitives sont définies dans la bibliothèque F # ou créées en code utilisateur via Async.FromBeginEnd ou Async.FromContinuations qui permettent de définir les détails de bas niveau de démarrage d'un calcul, d'enregistrement d'un rappel d'E/S, de libération du thread, puis de redémarrage du calcul lors d'un rappel . Donc, vous devez "asynchroniser" async jusqu'à une véritable primitive d'E/S asynchrone afin d'obtenir tous les avantages des E/S asynchrones.

+0

cela aide aussi ... merci beaucoup! – user297400

Questions connexes