2012-09-21 31 views
15

Tôi có một chút công bằng về sự hiểu biết của haskell nhưng tôi luôn luôn không chắc chắn về loại pragmas và tối ưu hóa tôi nên sử dụng và ở đâu. Giống nhưKhi nào sử dụng các ngôn ngữ và tối ưu hóa khác nhau?

  • như khi sử dụng SPECIALIZE pragma và những gì diễn đạt nó có.
  • Nơi sử dụng RULES. Tôi nghe mọi người nói về một quy tắc cụ thể không bắn? Làm thế nào để chúng ta kiểm tra điều đó?
  • Khi nào cần thực hiện các đối số của hàm một cách nghiêm ngặt và khi nào điều đó sẽ hữu ích? Tôi hiểu rằng làm cho đối số chặt chẽ sẽ làm cho các đối số được đánh giá theo dạng bình thường, vậy tại sao tôi không nên thêm độ nghiêm ngặt vào tất cả các đối số hàm? Làm thế nào để tôi quyết định?
  • Làm cách nào để xem và kiểm tra xem tôi có rò rỉ không gian trong chương trình của mình không? Các mẫu chung tạo thành rò rỉ không gian là gì?
  • Làm cách nào để biết liệu có vấn đề với sự lười biếng quá nhiều? Tôi luôn có thể kiểm tra heap hồ sơ nhưng tôi muốn biết nguyên nhân chung, ví dụ và mô hình nơi lười biếng đau là gì?

Có nguồn nào nói về tối ưu hóa nâng cao (cả ở mức cao hơn và rất thấp) đặc biệt là đặc biệt cho haskell?

+0

RHW ch 25? http://book.realworldhaskell.org/read/profiling-and-optimization.html –

+0

@DonStewart Cảm ơn .. Tôi đã đọc RWH .. Vấn đề là tôi biết họ làm gì nhưng tôi không có trực giác khi và nơi sử dụng của họ là quan trọng và không quan trọng. Tôi đã hỏi câu hỏi này để tìm hiểu các nguồn khác để nâng cao hơn nữa sự hiểu biết của tôi. – Satvik

Trả lời

18

Giống như khi sử dụng SPECIALIZE pragma và lợi ích hiệu suất của nó.

Bạn để trình biên dịch chuyên chức năng nếu bạn có hàm đa hình (loại lớp) và mong đợi nó được gọi thường xuyên tại một hoặc một vài trường hợp của lớp.

Chuyên môn loại bỏ tra cứu từ điển mà nó được sử dụng, và thường cho phép tối ưu hóa thêm, các chức năng thành viên của lớp thường có thể được gạch chân, và chúng phải chịu sự phân tích nghiêm ngặt. Nếu tối ưu hóa duy nhất có thể là loại bỏ tra cứu dicitonary, lợi ích sẽ không nói chung là rất lớn.

Tính đến GHC-7, nó có thể hữu ích hơn để cung cấp cho các chức năng một {-# INLINABLE #-} pragma, mà làm (gần như không đổi, một số bình thường và desugaring được thực hiện) của nguồn có sẵn trong file giao diện, do đó chức năng có thể được chuyên và thậm chí có thể được trình bày tại trang cuộc gọi.

Nơi sử dụng RULES. Tôi nghe mọi người nói về một quy tắc cụ thể không bắn? Làm thế nào để chúng ta kiểm tra điều đó?

Bạn có thể kiểm tra quy tắc nào đã kích hoạt bằng cách sử dụng tùy chọn dòng lệnh -ddump-rule-firings. Điều đó thường đổ một số lượng lớn các quy tắc bị sa thải, vì vậy bạn phải tìm kiếm một chút cho các quy tắc của riêng bạn.

Bạn sử dụng quy tắc

  • khi bạn có một phiên bản hiệu quả hơn của một hàm với nhiều loại đặc biệt, ví dụ

    {-# RULES 
    "realToFrac/Float->Double" realToFrac = float2Double 
        #-} 
    
  • khi một số chức năng có thể được thay thế bằng phiên bản hiệu quả hơn cho các đối số đặc biệt, ví dụ:

    {-# RULES 
    "^2/Int"  forall x. x^(2 :: Int) = let u = x in u*u 
    "^3/Int"  forall x. x^(3 :: Int) = let u = x in u*u*u 
    "^4/Int"  forall x. x^(4 :: Int) = let u = x in u*u*u*u 
    "^5/Int"  forall x. x^(5 :: Int) = let u = x in u*u*u*u*u 
    "^2/Integer" forall x. x^(2 :: Integer) = let u = x in u*u 
    "^3/Integer" forall x. x^(3 :: Integer) = let u = x in u*u*u 
    "^4/Integer" forall x. x^(4 :: Integer) = let u = x in u*u*u*u 
    "^5/Integer" forall x. x^(5 :: Integer) = let u = x in u*u*u*u*u 
        #-} 
    
  • khi viết lại biểu thức theo luật chung có thể tạo mã tốt hơn để tối ưu hóa, ví dụ:

    {-# RULES 
    "map/map" forall f g. (map f) . (map g) = map (f . g) 
        #-} 
    

Sử dụng rộng rãi của RULES theo phong cách thứ hai được thực hiện trong khuôn khổ hợp, ví dụ như trong thư viện text, và cho các chức năng danh sách trong base, một loại khác nhau của phản ứng tổng hợp (foldr/build fusion) được thực hiện sử dụng các quy tắc.

Khi nào cần thực hiện đối số của hàm một cách nghiêm ngặt và khi nào điều đó sẽ hữu ích? Tôi hiểu rằng làm cho đối số chặt chẽ sẽ làm cho các đối số được đánh giá theo dạng bình thường, vậy tại sao tôi không nên thêm độ nghiêm ngặt vào tất cả các đối số hàm? Làm thế nào để tôi quyết định?

Đặt đối số một cách nghiêm ngặt sẽ đảm bảo rằng đối số được đánh giá là biểu mẫu chuẩn yếu đầu bình thường, không ở dạng bình thường.

Bạn không thực hiện tất cả các đối số một cách nghiêm ngặt vì một số hàm phải không nghiêm ngặt trong một số đối số của chúng để hoạt động và một số ít hiệu quả hơn nếu có trong tất cả các đối số.

Đối examplepartition phải là không nghiêm ngặt trong số thứ hai của mình để làm việc ở tất cả vào danh sách vô hạn, tổng quát hơn mỗi chức năng được sử dụng trong foldr phải là không nghiêm ngặt trong đối số thứ hai để làm việc trên danh sách vô hạn. Trên các danh sách hữu hạn, có hàm không nghiêm ngặt trong đối số thứ hai có thể làm cho nó hiệu quả hơn đáng kể (foldr (&&) True (False:replicate (10^9) True)).

Bạn thực hiện một đối số nghiêm ngặt, nếu bạn biết rằng đối số phải được đánh giá trước khi bất kỳ công việc đáng giá nào có thể được thực hiện. Trong nhiều trường hợp, máy phân tích nghiêm ngặt của GHC có thể tự làm điều đó, nhưng tất nhiên là không phải tất cả.

Một trường hợp rất điển hình là ắc quy trong các vòng lặp hoặc đuôi, nơi thêm độ nghiêm ngặt ngăn cản việc xây dựng các khối lớn trên đường.

Tôi không biết quy tắc cứng nhắc và nhanh chóng về nơi cần thêm độ nghiêm ngặt, đối với tôi đó là vấn đề về trải nghiệm, sau một thời gian bạn tìm hiểu những địa điểm bổ sung tính nghiêm ngặt có thể giúp đỡ và nơi gây hại.

Theo quy tắc chung, việc lưu giữ dữ liệu nhỏ (như Int) có ý nghĩa, nhưng có ngoại lệ.

Làm cách nào để xem và kiểm tra xem tôi có rò rỉ không gian trong chương trình của mình không? Các mẫu chung tạo thành rò rỉ không gian là gì?

Bước đầu tiên là sử dụng tùy chọn +RTS -s (nếu chương trình được liên kết với rtsopts được bật). Điều đó cho bạn thấy có bao nhiêu bộ nhớ đã được sử dụng tổng thể, và bạn thường có thể đánh giá bằng cách đó cho dù bạn có một rò rỉ. Một đầu ra thông tin có thể thu được từ chạy chương trình với tùy chọn +RTS -hT, tạo ra một hồ sơ heap có thể giúp xác định vị trí rò rỉ không gian (đồng thời, chương trình cần được liên kết với các rtsopts đã bật).

Nếu cần phân tích thêm, chương trình cần được biên dịch có bật hồ sơ (-rtsops -prof -fprof-auto, trong GHC cũ, tùy chọn -fprof-auto không có sẵn, tùy chọn -prof-auto-all là thư gần nhất ở đó).

Sau đó, bạn chạy nó với các tùy chọn lược tả khác nhau và xem xét hồ sơ heap được tạo.

Hai nguyên nhân phổ biến nhất đối với rò rỉ không gian là

  • quá nhiều sự lười biếng
  • quá nhiều tính nghiêm minh

vị trí thứ ba có lẽ lấy bằng cách chia sẻ mong muốn, GHC không ít loại bỏ subexpression chung , nhưng đôi khi nó chia sẻ danh sách dài ngay cả khi không muốn.

Để tìm nguyên nhân rò rỉ, tôi biết lại không có quy tắc cứng và nhanh, và đôi khi, có thể khắc phục sự cố bằng cách thêm độ nghiêm ngặt ở một nơi hoặc bằng cách thêm sự lười biếng vào một nơi khác.

Làm cách nào để biết liệu có vấn đề gì với quá nhiều sự lười biếng? Tôi luôn có thể kiểm tra heap hồ sơ nhưng tôi muốn biết nguyên nhân chung, ví dụ và mô hình nơi lười biếng đau là gì?

Nói chung, sự lười biếng được mong muốn khi kết quả có thể được tăng lên và không mong muốn khi kết thúc quá trình xử lý hoàn tất, chẳng hạn như ở các nếp gấp bên trái hoặc thường có chức năng đệ quy đuôi.

+0

Cảm ơn bạn đã có câu trả lời rất hay. – Satvik

3

Tôi khuyên bạn nên đọc tài liệu GHC trên PragmasRewrite Rules vì chúng giải quyết được nhiều câu hỏi của bạn về SPECIALIZE và RULES.

Để giải quyết ngắn gọn câu hỏi của bạn:

  • chuyên được sử dụng để buộc các trình biên dịch để xây dựng một phiên bản đặc biệt của một hàm đa hình cho một loại cụ thể. Ưu điểm là việc áp dụng hàm trong trường hợp đó sẽ không còn yêu cầu từ điển nữa. Điểm bất lợi là nó sẽ tăng kích thước của chương trình của bạn. Chuyên môn đặc biệt có giá trị đối với các hàm được gọi là "các vòng bên trong", và về cơ bản nó vô dụng đối với các hàm mức cao nhất không thường xuyên. Tham khảo GHC documentation để biết các tương tác với INLINE.

  • QUY TẮC cho phép bạn chỉ định quy tắc viết lại mà bạn biết là hợp lệ nhưng trình biên dịch không thể suy ra theo cách riêng của nó. Ví dụ phổ biến là {-# RULES "mapfusion" forall f g xs. map f (map g xs) = map (f.g) xs #-}, thông báo cho GHC cách hợp nhất map. Nó có thể là khó khăn để có được GHC sử dụng các quy tắc vì sự can thiệp với INLINE. 7.19.3 liên quan đến cách tránh xung đột và cũng làm thế nào để buộc GHC sử dụng quy tắc ngay cả khi nó thường tránh nó.

  • Đối số chính xác quan trọng nhất đối với một thứ như bộ tích lũy trong chức năng đệ quy đuôi. Bạn biết rằng giá trị cuối cùng sẽ được tính toán đầy đủ, và xây dựng một đống đóng cửa để trì hoãn tính toán hoàn toàn đánh bại mục đích. Độ chính xác cưỡng chế phải tự nhiên tránh được bất cứ lúc nào chức năng có thể được áp dụng cho một giá trị phải được xử lý một cách lười biếng, giống như một danh sách vô hạn. Nói chung, ý tưởng tốt nhất là ban đầu chỉ ép buộc mức độ nghiêm ngặt, nơi nó rõ ràng là hữu ích (như ắc quy), và sau đó thêm nhiều hơn sau đó chỉ như hiển thị hồ sơ nó là cần thiết.

  • Trải nghiệm của tôi là hầu hết các rò rỉ không gian hiển thị đến từ bộ tích lũy lười và giá trị lười không được đánh giá trong cấu trúc dữ liệu rất lớn, mặc dù tôi chắc chắn điều này là cụ thể cho các loại chương trình bạn đang viết. Sử dụng cấu trúc dữ liệu không có hộp thư bất cứ khi nào có thể khắc phục được rất nhiều vấn đề.

  • Bên ngoài trường hợp sự lười biếng gây rò rỉ không gian, tình huống chính cần tránh trong IO. Tài nguyên xử lý trôi chảy vốn đã làm tăng lượng thời gian đồng hồ treo tường mà tài nguyên là cần thiết. Điều này có thể xấu đối với hiệu suất bộ nhớ cache, và rõ ràng là xấu nếu một cái gì đó khác muốn độc quyền sử dụng cùng một tài nguyên.

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