FWIW Tôi nghĩ rằng các vấn đề chi tiết ở đây chỉ đi xuống trình biên dịch C# thông minh hơn, và tạo ra một máy trạng thái hiệu quả dựa mô hình để xử lý mã async, trong khi trình biên dịch F # tạo ra vô số các đối tượng và các cuộc gọi hàm thường chỉ kém hiệu quả.Hiệu suất của async trong F # vs C# (có cách nào tốt hơn để viết async {...})
Dù sao, nếu tôi có C# chức năng dưới đây:
public async static Task<IReadOnlyList<T>> CSharpAsyncRead<T>(
SqlCommand cmd,
Func<SqlDataReader, T> createDatum)
{
var result = new List<T>();
var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
var datum = createDatum(reader);
result.Add(datum);
}
return result.AsReadOnly();
}
Và sau đó chuyển đổi này để F # như sau:
let fsharpAsyncRead1 (cmd:SqlCommand) createDatum = async {
let! reader =
Async.AwaitTask (cmd.ExecuteReaderAsync())
let rec readRows (results:ResizeArray<_>) = async {
let! readAsyncResult = Async.AwaitTask (reader.ReadAsync())
if readAsyncResult then
let datum = createDatum reader
results.Add datum
return! readRows results
else
return results.AsReadOnly() :> IReadOnlyList<_>
}
return! readRows (ResizeArray())
}
Sau đó, tôi thấy rằng việc thực hiện cáC# mã f là chậm hơn đáng kể và nhiều CPU đói hơn phiên bản C#. Tôi đã tự hỏi nếu có tốt hơn là để soạn nó. Tôi đã cố gắng loại bỏ các hàm đệ quy như sau (trong đó xuất hiện một chút xấu xí với không gian và không thể thay đổi let s!):
let fsharpAsyncRead2 (cmd:SqlCommand) createDatum = async {
let result = ResizeArray()
let! reader =
Async.AwaitTask (cmd.ExecuteReaderAsync())
let! moreData = Async.AwaitTask (reader.ReadAsync())
let mutable isMoreData = moreData
while isMoreData do
let datum = createDatum reader
result.Add datum
let! moreData = Async.AwaitTask (reader.ReadAsync())
isMoreData <- moreData
return result.AsReadOnly() :> IReadOnlyList<_>
}
Nhưng việc thực hiện là cơ bản giống nhau.
Như một ví dụ về việc thực hiện, khi tôi đã tải một thanh dữ liệu thị trường như:
type OHLC = {
Time : DateTime
Open : float
High : float
Low : float
Close : float
}
Trên máy tính của tôi, F phiên bản # async mất ~ gấp đôi thời gian, và tiêu thụ ~ gấp đôi Tài nguyên CPU cho toàn bộ thời gian nó chạy - do đó lấy khoảng 4x tài nguyên nhiều (tức là nội bộ nó phải được kéo lên nhiều chủ đề hơn?).
(Có thể nó hơi mơ hồ khi đọc một cấu trúc tầm thường như vậy? Tôi thực sự chỉ chọc máy để xem nó hoạt động như thế nào. So với phiên bản không đồng bộ (nghĩa là chỉ đọc thẳng) C# một hoàn thành trong cùng một thời gian, nhưng tiêu thụ> gấp đôi CPU, tức là đọc thẳng() tiêu thụ < 1/8 của tài nguyên f #)
Vì vậy, câu hỏi của tôi là, khi tôi thực hiện F # async "đúng" cách (đây là lần đầu tiên tôi sử dụng)?
(... và nếu tôi, sau đó tôi chỉ cần đi và sửa đổi các trình biên dịch để thêm một máy nhà nước dựa trên thực hiện cho Asyncs biên soạn ... làm thế nào cứng có thể được :-))
Bạn có chắc chắn cả hai đều chạy với cùng một cài đặt liên quan đến bitness, tối ưu hóa và như vậy? –
Bạn đã xem 'hopac' (https://github.com/Hopac/Hopac) chưa? nó có chi phí CPU thấp hơn đáng kể so với 'Async' (và IIRC thấp hơn' Task'). FYI; Cá nhân tôi sẽ không xem xét sử dụng 'Async' hoặc 'TPL' và như vậy khi tôi cần song song. Nếu tôi bị ràng buộc CPU, tôi rất có thể không thể làm hỏng các trừu tượng. – FuleSnabel
@FuleSnabel BTW, tôi đã xem xét Hopac vì nó thực sự nhanh hơn 'async {}'. Nhưng interop với C# (thiếu) là một dealbreaker. 'task {}' biểu thức tính toán đưa ra một cách để sử dụng TPL (có lẽ là phần được tối ưu hóa nhất của .NET) theo cách thành ngữ F #. –