2010-03-19 3 views
8

은 내가 비동기 I/O를 활용에서 엄청난 성능 향상을위한 기회가 있다는 것을 시사 돈 사임의 블로그Async.Parallel 또는 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)

에서 읽을 패턴을 구현하기 위해 노력하고있어. 나는 현재 Array.Parallel.Map을 사용하여 한 방향으로 "작동"하는 코드 조각을 가져 와서 Async.Parallel을 사용하여 동일한 결과를 얻을 수 있는지를 확인하려고하지만 실제로 Async.Parallel을 이해하지 못한다. 그리고 일을 할 수는 없습니다.

하나의 코드에 대한 데이터 배열을 성공적으로 검색하는 코드 조각 (요점을 설명하기 위해 단순화 한 코드)이 있습니다. (예를 들어 가격 시리즈)

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

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

그래서이 방법은 (3000 많은 수) 각 주식의 내 데이터 공급 업체에 인터넷을 통해 연결함으로써, 배열의 배열을 contructs 날을 반환 배열 배열 (주식 당 1 개, 각 배열에 대한 가격 시리즈). 필자는 Array.Parallel.map 아래에서 무슨 일이 일어나는지 이해하지 못하고 있지만, 이것이 후드 아래에서 리소스가 낭비되는 시나리오인지 궁금해합니다. 실제로 비동기 I/O를 사용하면 더 빠를 수 있습니다. 그래서 이것을 테스트하기 위해 비동기를 사용하여이 함수를 만들려고했습니다. 아래 함수가 Don Syme 기사의 URL을 사용하여 패턴을 따르지만 "let!"으로 컴파일되지 않을 것이라고 생각합니다.

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

내가 오류는 다음과 같습니다! "하자" 이 표현은 유형 비동기 < '을 것으로 예상되었다>하지만 여기에 입력 한 OBJ

그것은, 대신 "하자"로 잘 컴파일하지만, 스레드를 차단하지 않고 명령을 실행하기 위해서는 느낌표가 필요하다는 것이 전부 였다고 생각했습니다.

위의 구문, getStockDataAsync 및 그 상위 레벨에서 잘못된 점은 누구나 비동기 I/O에 대한 추가 통찰력을 제공하고 내가 제시 한 시나리오에서 이점을 얻을 수 있는지 여부입니다. Array.Parallel.map보다 훨씬 빠르고 훨씬 빠릅니다. 정말 고마워.

답변

18

F # 비동기 워크 플로를 사용하면 비동기 계산을 구현할 수 있지만 F #은 일반적인 계산과 비동기 계산을 구별합니다. 이 차이는 형식 시스템에 의해 추적됩니다. 예를 들어, 웹 페이지를 다운로드하고 동기식 인 메소드는 string -> string (URL 가져 오기 및 HTML 반환) 유형을가집니다. 그러나 동일한 작업을 비동기 적으로 수행하는 메소드의 유형은 string -> Async<string>입니다. async 블록에서 let!을 사용하여 비동기 작업을 호출 할 수 있지만 let을 사용하여 다른 모든 (표준 동기식) 메서드를 호출해야합니다. 자, 예제의 문제점은 GetData 연산이 일반적인 동기식 메서드이므로 let!을 사용하여 호출 할 수 없다는 것입니다.

일반적인 F # 시나리오에서 GetData 멤버를 비동기로 만들려면 비동기 워크 플로를 사용해야하므로 async 블록으로 묶어야합니다. 어떤 시점에서 비동기 적으로 원시 작업을 실행해야하는 위치 (예 : 웹 사이트에서 데이터 다운로드)로 이동합니다. F #은 AsyncGetResponse (GetResponse 메서드의 비동기 버전)과 같이 let!을 사용하여 async 블록에서 호출 할 수있는 몇 가지 기본 비동기 작업을 제공합니다.그래서, 당신의 GetData 방법, 당신은 예를 들면 다음처럼 작성할 수 있습니다 :

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) } 

요약하면 (예 : 웹 서버 또는 파일 시스템 대기 등) 일부 기본 비동기 작업을 식별 할 필요가있다 그 시점에서 원시 비동기 작업을 사용하고 async 블록에서 이러한 작업을 사용하는 모든 코드를 래핑하십시오. 비동기 적으로 실행할 수있는 기본 연산이 없으면 코드가 CPU에 바인딩되어 있으므로 Parallel.map 만 사용할 수 있습니다.

F # 비동기 워크 플로가 어떻게 작동하는지 이해하는 데 도움이되기를 바랍니다. 자세한 내용은 Don Syme's blog post, 시리즈 번호 asynchronous programming by Robert Pickering 또는 F# web cast을 참조하십시오.

+0

주셔서 감사합니다. 그것은 매우 도움이되었습니다. – user297400

5

@Tomas는 이미 훌륭한 해답을 가지고 있습니다. 나는 단지 몇 비트 더 말할 것이다.

F # asyncs의 관용구는 "Async"접두어 (이 아니라 AsyncFoo이 아닌, 다른 .NET 기술에서 이미 사용 된 관용구)를 사용하여 메서드 이름을 지정하는 것입니다. 따라서 귀하의 기능은 getStockDataasyncGetStockData이어야합니다. 비동기 워크 플로우 내부

, 당신은 let! 대신 let 또는 do! 대신 do을 사용할 때마다, 오른쪽에있는 것은 유형 Async<T> 대신 T이 있어야합니다. 기본적으로 워크 플로우의이 시점에서 '비동기로 전환'하기 위해 기존 비동기 계산이 필요합니다. 각 Async<T>은 그 자체로 다른 워크 플로이거나 다른 비동기 "프리미티브"입니다. 이 프리미티브는 F # 라이브러리에 정의되거나 사용자 코드로 Async.FromBeginEnd 또는 Async.FromContinuations을 통해 생성되며, 계산 시작, I/O 콜백 등록, 스레드 해제 및 콜백시 계산 시작과 관련된 세부 정보를 정의 할 수 있습니다 . 따라서 비동기 I/O의 모든 이점을 얻으려면 비동기 I/O 프리미티브로 비동기 적으로 '비동기 적으로'연결해야합니다.

+0

이것도 도움이됩니다 ... 대단히 감사합니다! – user297400

관련 문제