reactive-banana
reactive-banana copied to clipboard
fmap (fmap f) (stepper a b) performs differently to stepper (f a) (fmap f b)
We have the following code in our application:
currentFrame <-
let undistort' img = undistort img cameraMatrix distCoeffs
in fmap (fmap undistort')
(stepper (appImage initialAppData) newFrameAcquired)
undistort is a relatively expensive operation, taking around 0.3s. Therefore it's important that we only distort when we really need to - which is exactly when a new frame comes in (from a web camera). However, the above seems to result in undistort being evaluated whenever I need the value of the currentFrame Behavior. The following rewrite evaluates undistort only when necessary:
currentFrame <-
let undistort' img = undistort img cameraMatrix distCoeffs
in stepper (undistort' (appImage initialAppData)) (fmap undistort' newFrameAcquired)
Is this to be expected?
It does look undesirable to me that the two code snippets have different performance characteristics.
At the moment, the internal implementation of reactive-banana uses different mechanisms for memoizing Event compared to memoizing Behavior values. The latter is not quite as good as the former, hence the discrepancy. That said, I'm not entirely sure why it doesn't work in this case.
I was unable to reproduce this with the following code:
import Control.Monad
import Reactive.Banana
import Reactive.Banana.Frameworks
import System.IO.Unsafe
import Control.Concurrent
main :: IO ()
main = do
(ah, fire) <- newAddHandler
(ah2, fire2) <- newAddHandler
(actuate =<<) . compile $ do
eEnter <- fromAddHandler ah
eFrame <- fromAddHandler ah2
bNum <- (fmap.fmap) expensive (stepper 0 eFrame)
reactimate (putStrLn "frame" <$ eFrame)
reactimate (print <$> bNum <@ eEnter)
void . forkIO . forM_ [1..] $ \i -> do
threadDelay (3*1000*1000)
fire2 i
forever $ getLine >> fire ()
expensive :: Int -> Int
expensive n =
unsafePerformIO $ do
putStrLn "expensive calculation!"
threadDelay (1*1000*1000)
pure (n*2)
{-# NOINLINE expensive #-}
Running this, "frames" arrive every 3 seconds, and pressing enter prints the value of the behavior that is updated by an expensive pure function every frame.
If I run this and press enter a bunch, I only see the expensive operation computed once per new frame, and between frames, it's cached.
May be a duplicate of https://github.com/HeinrichApfelmus/reactive-banana/issues/251