2014-06-10 15 views
9

Trong this câu hỏi ở đây trên SO sự khác biệt giữa hai nhà khai thác divquot được đề cập cũng như thực tế là các nhà điều hành quot là hiệu quả hơn so với các nhà điều hành div, trong khi div là tự nhiên hơn đối với chúng tôi con người sử dụng.khác biệt chính xác giữa div và quot

Câu hỏi của tôi là những gì triển khai chính xác của hai nhà khai thác và được liên kết với những gì sự khác biệt giữa triển khai là gì. Ngoài ra tôi muốn biết sự khác biệt về tốc độ giữa hai yếu tố này, như việc sử dụng Hoogle và duyệt các nguồn không giúp tôi trong việc tìm hiểu sự hiểu biết của mình.

Tôi muốn làm rõ rằng tôi hiểu sự khác biệt chung giữa hai nhà khai thác và chỉ quan tâm đến việc triển khai hoặc thay vì sự khác biệt.

+0

Tôi đã thêm một chút nhỏ bé của điểm chuẩn quá, FYI. –

+0

@ AndrásKovács Cảm ơn bạn đã trả lời tuyệt vời! : D – ThreeFx

+0

Khi @augustss đề cập đến câu trả lời cho câu hỏi mà bạn đã liên kết, lý do * cơ bản cho sự khác biệt tốc độ là 'quot' là những gì thường được thực hiện trực tiếp như một lệnh trong CPU hiện đại. Như vậy, như đã đề cập dưới đây, đó là những gì GHC chọn làm hoạt động nguyên thủy của nó. –

Trả lời

9

quot vòng về phía zero, div vòng về phía vô cực âm:

div (-3) 2 == (-2) 
quot (-3) 2 == (-1) 

Đối với các overhead của div, quot có một tương ứng primitive GHC operation, trong khi div không some extra work:

quotRemInt :: Int -> Int -> (Int, Int) 
(I# x) `quotRemInt` (I# y) = case x `quotRemInt#` y of 
          (# q, r #) -> 
           (I# q, I# r) 

divModInt# :: Int# -> Int# -> (# Int#, Int# #) 
x# `divModInt#` y# 
| (x# ># 0#) && (y# <# 0#) = case (x# -# 1#) `quotRemInt#` y# of 
           (# q, r #) -> (# q -# 1#, r +# y# +# 1# #) 
| (x# <# 0#) && (y# ># 0#) = case (x# +# 1#) `quotRemInt#` y# of 
           (# q, r #) -> (# q -# 1#, r +# y# -# 1# #) 
| otherwise    = x# `quotRemInt#` y# 

Trong cuối cùng của họ biểu mẫu, cả hai hàm đều có một số error handling checks on them:

a `quot` b 
| b == 0      = divZeroError 
| b == (-1) && a == minBound = overflowError -- Note [Order of tests] 
               -- in GHC.Int 
| otherwise     = a `quotInt` b 

a `div` b 
| b == 0      = divZeroError 
| b == (-1) && a == minBound = overflowError -- Note [Order of tests] 
               -- in GHC.Int 
| otherwise     = a `divInt` b 

Tôi cũng đã làm một chút rất nhỏ của microbenchmarking, nhưng nó nên được thực hiện với một lượng muối khổng lồ, bởi vì GHC và LLVM tối ưu hóa mã số chặt chẽ đi như không có ngày mai. Tôi đã cố ngăn chặn chúng và kết quả dường như thực tế: 14,67 ms cho div13,37 ms cho quot. Ngoài ra, nó là GHC 7.8.2 với -O2 và -fllvm. Dưới đây là các mã:

{-# LANGUAGE BangPatterns #-} 

import Criterion.Main 
import System.Random 

benchOp :: (Int -> Int) -> Int ->() 
benchOp f = go 0 0 where 
    go !i !acc !limit | i < limit = go (i + 1) (f i) limit 
         | otherwise =() 

main = do 
    limit1 <- randomRIO (1000000, 1000000 :: Int) 
    limit2 <- randomRIO (1000000, 1000000 :: Int) 
    n  <- randomRIO (100, 100 :: Int) 
    defaultMain [ 
     bench "div" $ whnf (benchOp (`div` n)) limit1, 
     bench "quot" $ whnf (benchOp (`quot` n)) limit2] 
+0

Tôi không biết đủ về điểm chuẩn để biết liệu điểm chuẩn của bạn có kiểm tra số âm hay không. Phải không? – dfeuer

+0

@dfeuer tất cả đều tích cực. Tôi chạy một số với số âm, và kết quả là tương tự, nhưng dù sao tôi nghĩ rằng đây không phải là một phần rất tốt của điểm chuẩn; một điều tốt hơn là nơi chúng tôi sử dụng NOINLINE-s và nhìn vào GHC Core để đảm bảo chúng tôi đang chuẩn hóa đúng thứ. –

+0

Lý do tôi đề cập đến các âm bản là nếu GHC/LLVM đủ thông minh để nhận ra chỉ có các mặt tích cực, thì nó sẽ gut logic 'divMod' sau khi nội tuyến. Có lẽ bạn cần phải chuẩn bị cẩn thận hơn, nhưng tôi chắc chắn * không phải là người thích hợp để làm điều đó. – dfeuer

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