2012-05-06 36 views
8

Tôi đang học F # và một điều khiến tôi lo ngại về ngôn ngữ này là hiệu suất. Tôi đã viết một chuẩn mực nhỏ nơi tôi so sánh thành ngữ F # với mã kiểu mệnh lệnh được viết bằng cùng một ngôn ngữ - và nhiều điều khiến tôi ngạc nhiên, phiên bản chức năng xuất hiện nhanh hơn đáng kể.Seq.map nhanh hơn vòng lặp thông thường?

Điểm chuẩn bao gồm:

  1. Reading trong một file văn bản sử dụng File.ReadAllLines
  2. Đảo ngược thứ tự của ký tự trong mỗi dòng
  3. Viết lại kết quả với cùng tập tin sử dụng File.WriteAllLines .

Dưới đây là các mã:

open System 
open System.IO 
open System.Diagnostics 

let reverseString(str:string) = 
    new string(Array.rev(str.ToCharArray())) 

let CSharpStyle() = 
    let lines = File.ReadAllLines("text.txt") 
    for i in 0 .. lines.Length - 1 do 
     lines.[i] <- reverseString(lines.[i]) 

    File.WriteAllLines("text.txt", lines) 

let FSharpStyle() = 
    File.ReadAllLines("text.txt") 
    |> Seq.map reverseString 
    |> (fun lines -> File.WriteAllLines("text.txt", lines)) 

let benchmark func message = 
    // initial call for warm-up 
    func() 

    let sw = Stopwatch.StartNew() 
    for i in 0 .. 19 do 
     func() 

    printfn message sw.ElapsedMilliseconds 


[<EntryPoint>] 
let main args = 
    benchmark CSharpStyle "C# time: %d ms" 
    benchmark FSharpStyle "F# time: %d ms" 
    0 

Dù kích thước của tập tin, phiên bản "F # kiểu" hoàn thành trong khoảng 75% thời gian của phiên bản "C# kiểu". Câu hỏi của tôi là, tại sao vậy? Tôi thấy không có sự kém hiệu quả rõ ràng trong phiên bản bắt buộc.

+1

Kudos @Dr_Asik cho một câu hỏi được chuẩn bị kỹ lưỡng. –

Trả lời

10

Seq.map khác với Array.map. Bởi vì chuỗi (IEnumerable<T>) không được đánh giá cho đến khi chúng được liệt kê, trong mã kiểu F # không có tính toán thực sự xảy ra cho đến khi File.WriteAllLines lặp qua chuỗi (không phải mảng) được tạo bởi Seq.map.

Nói cách khác, phiên bản kiểu C của bạn đang đảo ngược tất cả các chuỗi và lưu trữ các chuỗi bị đảo ngược trong một mảng, sau đó lặp qua mảng để ghi ra tệp. Phiên bản kiểu F # đang đảo ngược tất cả các chuỗi và viết chúng trực tiếp nhiều hơn vào tệp. Điều đó có nghĩa là mã C#-style đang lặp qua toàn bộ tệp ba lần (đọc tới mảng, xây dựng mảng đảo ngược, viết mảng thành tệp), trong khi mã kiểu # # chỉ lặp lại toàn bộ tệp (đọc vào mảng, ghi các dòng được đảo ngược thành tệp).

Bạn muốn có được hiệu suất tốt nhất của tất cả nếu bạn sử dụng File.ReadLines thay vì File.ReadAllLines kết hợp với Seq.map - nhưng tập tin đầu ra của bạn sẽ phải khác so với tệp đầu vào của bạn, như bạn muốn được viết cho sản lượng trong khi vẫn đọc từ đầu vào.

+1

Ah, tôi nhìn thấy nó ngay bây giờ - phiên bản C# gọi File.WriteAllLines (chuỗi, chuỗi []) trong khi phiên bản F # gọi File.WriteAllLines (chuỗi, IEnumerable ). Vì vậy, có thực sự chỉ là 2 vòng thay vì 3. Nó đã không đến với tâm trí của tôi rằng có quá tải khác của phương pháp đó. Cảm ơn lời giải thích! – Asik

1

Biểu mẫu Seq.map có nhiều ưu điểm hơn vòng lặp thông thường. Nó có thể tính toán trước tham chiếu hàm chỉ một lần; nó có thể tránh được các bài tập biến; và nó có thể sử dụng độ dài chuỗi đầu vào để xác định mảng kết quả.

+1

Có vẻ như các điểm rất hợp lệ, nhưng tôi gặp khó khăn khi nhìn thấy ý của bạn. Bạn có thể mở rộng và minh họa từng điểm một chút không? Cảm ơn. – Asik

Các vấn đề liên quan