11

Tôi có chức năng Haskell, điều này gây ra hơn 50% tất cả phân bổ chương trình của tôi, làm cho 60% thời gian chạy của tôi . Tôi chạy với một ngăn xếp nhỏ (-K10K) do đó không có tràn ngăn xếp, nhưng tôi có thể làm cho chức năng này nhanh hơn, với phân bổ ít hơn?Tối ưu hóa chức năng danh sách tạo ra quá nhiều rác (không tràn ngăn xếp)

Mục tiêu ở đây là tính sản phẩm của ma trận bằng vectơ. Ví dụ: tôi không thể sử dụng hmatrix vì đây là một phần của chức năng lớn hơn bằng gói ad Automatic Differentiation, vì vậy tôi cần sử dụng danh sách Num. Khi chạy, tôi giả sử việc sử dụng mô-đun Numeric.AD có nghĩa là các loại của tôi phải là Scalar Double.

listMProd :: (Num a) => [a] -> [a] -> [a] 
listMProd mdt vdt = go mdt vdt 0 
    where 
    go [] _ s = [s] 
    go ls [] s = s : go ls vdt 0 
    go (y:ys) (x:xs) ix = go ys xs (y*x+ix) 

Về cơ bản chúng ta lặp qua ma trận, nhân và thêm một ắc cho đến khi chúng ta đạt được kết thúc của vector, lưu trữ kết quả, sau đó tiếp tục khởi động lại vector một lần nữa. Tôi có một thử nghiệm quickcheck xác minh rằng tôi nhận được kết quả tương tự so với sản phẩm ma trận/vector trong hmatrix.

Tôi đã thử với foldl, foldr, v.v. Không có gì tôi đã thử làm cho chức năng nhanh hơn (và một số thứ như foldr gây rò rỉ không gian).

Chạy với hồ sơ cho tôi biết, trên thực tế chức năng này là nơi hầu hết thời gian và phân bổ được chi tiêu, có vô số Cells đang được tạo, Cells là loại dữ liệu từ gói ad.

Một xét nghiệm đơn giản để chạy:

import Numeric.AD 

main = do 
    let m :: [Double] = replicate 400 0.2 
     v :: [Double] = replicate 4 0.1 
     mycost v m = sum $ listMProd m v 
     mygrads = gradientDescent (mycost (map auto v)) (map auto m) 
    print $ mygrads !! 1000 

này trên máy tính của tôi nói với tôi GC đang bận 47% thời gian.

Bất kỳ ý tưởng nào?

+1

Thông tin thêm! Bạn đang chạy chương trình này như thế nào? Kiểm tra khai thác của bạn ở đâu? Bạn đang sử dụng loại bê tông nào? Cờ và phiên bản trên trình biên dịch Haskell của bạn là gì? –

+0

Đã thêm một số thông tin. Hàm đó được gọi thông qua hàm ad grad sử dụng các kiểu riêng của nó (các thể hiện của Num). Tiểu sử hiển thị phân bổ của "Ô". –

+0

Một số đề xuất được thông báo một nửa: bạn có cân nhắc sử dụng trạng thái có thể thay đổi với 'ST' không? Và [stream-fusion] (https://hackage.haskell.org/package/stream-fusion-0.1.2.5/docs/Data-List-Stream.html)/[ống dẫn] (https: //hackage.haskell. org/gói/ống dẫn)/[ống] (https://hackage.haskell.org/package/pipes)? Có thể (?) Thậm chí có thể đáng giá để chuyển đổi danh sách vectơ của bạn thành một thứ khác, ví dụ: một [vectơ không được đóng hộp] (http://lambda.jstolarek.com/2013/04/haskell-as-fast-as-c-a-case-study/)? Tôi không có kinh nghiệm với bất kỳ kỹ thuật nào trong số này nhưng có thể các liên kết có thể giúp bạn thêm. –

Trả lời

7

Một tối ưu hóa rất đơn giản là làm cho go chức năng nghiêm ngặt bởi tham số ắc nó, bởi vì nó nhỏ, có thể là không có hộp bọc nếu a là nguyên thủy và luôn luôn cần phải được đánh giá đầy đủ:

{-# LANGUAGE BangPatterns #-} 
listMProd :: (Num a) => [a] -> [a] -> [a] 
listMProd mdt vdt = go mdt vdt 0 
    where 
    go [] _ !s = [s] 
    go ls [] !s = s : go ls vdt 0 
    go (y:ys) (x:xs) !ix = go ys xs (y*x+ix) 

Trên máy tính của tôi, nó cho tốc độ 3-4x (được biên dịch với -O2).

Mặt khác, danh sách trung gian không được nghiêm ngặt để chúng có thể hợp nhất.

+0

Mhh, ý tưởng hay, nhưng nó không giúp gì cả trong trường hợp sử dụng của tôi (không cải thiện tốc độ hoặc sử dụng GC). Tôi nghĩ rằng thực tế là chức năng được gọi thông qua thư viện quảng cáo là những gì ảnh hưởng đến hiệu suất (tôi thấy một loại dữ liệu Cells với một trường Int nghiêm ngặt). –

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