Open soupi opened 1 year ago
@soupi I wasn't able to reproduce a space leak using a modified version of your repro. I used the following, which just depends on Spock-core:
{-# LANGUAGE OverloadedStrings #-}
{-# language ScopedTypeVariables #-}
import Data.Aeson (Value)
import Data.String (fromString)
import Data.Text (Text)
import qualified Web.Spock.Core as Spock
import System.Mem
import Control.Concurrent
import GHC.Stats
import Control.Monad (forever, void)
main :: IO ()
main = do
void $ forkIO $ forever $ do
threadDelay $ 30*1000*1000
performMajorGC
getRTSStats >>= \stats -> do
print $ gcdetails_live_bytes $ gc stats
Spock.runSpock 3000 $
Spock.spockT id routes
routes :: Spock.SpockCtxT () IO ()
routes = do
Spock.get "/" $
Spock.text "hi"
Spock.get ("/id/" Spock.<//> Spock.var) $ \(id' :: Int) -> do
(name :: Text) <- Spock.param' "name"
Spock.setHeader "x-powered-by" "benchmark"
Spock.text (fromString (show id') <> " " <> name)
Spock.post "/json" $ do
(value :: Value) <- Spock.jsonBody'
Spock.json value
I just tested it using curl, and observing the reported live bytes that get logged every 30 seconds, e.g.:
for _ in {1..1000000}; do curl -X GET http://localhost:3000/id/3\?name\=bun &> /dev/null; done
I tested both w ghc 9.2.7 and 9.4.5
So wrt any space leaks, it might be:
Here’s my freeze file for 9.4
@jberryman oh nice catch. This seems to be a problem with the full Spock version and not Spock-core. I've ran your example (without the forkIO
) and indeed it runs quite fast while the full Spock version does not. This is the diff that will make it run fast:
{-# LANGUAGE OverloadedStrings #-}
{-# language ScopedTypeVariables #-}
import Data.Aeson (Value)
import Data.String (fromString)
import Data.Text (Text)
-import qualified Web.Spock as Spock
-import qualified Web.Spock.Config as Spock
+import qualified Web.Spock.Core as Spock
main :: IO ()
main = do
- config <- mkConfig
Spock.runSpock 3000 $
- Spock.spock config routes
+ Spock.spockT id routes
-mkConfig :: IO (Spock.SpockCfg () () ())
-mkConfig = Spock.defaultSpockCfg () Spock.PCNoDatabase ()
-routes :: Spock.SpockM () () () ()
+routes :: Spock.SpockCtxT () IO ()
routes = do
Spock.get "/" $
Spock.text "hi"
Spock.get ("/id/" Spock.<//> Spock.var) $ \(id' :: Int) -> do
(name :: Text) <- Spock.param' "name"
Spock.setHeader "x-powered-by" "benchmark"
Spock.text (fromString (show id') <> " " <> name)
Spock.post "/json" $ do
(value :: Value) <- Spock.jsonBody'
Spock.json value
And the profile looks fine as well:
I spotted what I believe is a memory leak trying to benchmark a very simple Spock app.
Reproduction steps in this repo.
You may need to use
ulimit -n 4096
before running hello-spock, which is another indication that there's a problem.Spock fairs rather poorly compared to twain and scotty. Here are some numbers:
When trying to figure out why that is I noticed very different behaviours between Spock and twain:
Here's a
``` > Spock is running on port 3000 149,218,795,624 bytes allocated in the heap 31,465,251,520 bytes copied during GC 505,829,376 bytes maximum residency (198 sample(s)) 67,873,264 bytes maximum slop 1557 MiB total memory in use (61 MiB lost due to fragmentation) Tot time (elapsed) Avg pause Max pause Gen 0 14970 colls, 14970 par 21.356s 6.710s 0.0004s 0.0044s Gen 1 198 colls, 197 par 39.219s 5.222s 0.0264s 0.0956s Parallel GC work balance: 87.24% (serial 0%, perfect 100%) TASKS: 123 (1 bound, 121 peak workers (122 total), using -N12) SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled) INIT time 0.003s ( 0.002s elapsed) MUT time 164.764s ( 23.123s elapsed) GC time 60.574s ( 11.932s elapsed) EXIT time 0.065s ( 0.003s elapsed) Total time 225.407s ( 35.060s elapsed) Alloc rate 905,650,467 bytes per MUT second Productivity 73.1% of total user, 66.0% of total elapsed ------ > Running twain app at http://localhost:3000 (ctrl-c to quit) 101,783,300,920 bytes allocated in the heap 2,880,638,200 bytes copied during GC 19,310,688 bytes maximum residency (23 sample(s)) 872,128 bytes maximum slop 104 MiB total memory in use (0 MiB lost due to fragmentation) Tot time (elapsed) Avg pause Max pause Gen 0 3931 colls, 3931 par 7.664s 2.089s 0.0005s 0.0017s Gen 1 23 colls, 22 par 0.128s 0.037s 0.0016s 0.0030s Parallel GC work balance: 72.41% (serial 0%, perfect 100%) TASKS: 26 (1 bound, 25 peak workers (25 total), using -N12) SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled) INIT time 0.003s ( 0.002s elapsed) MUT time 167.343s ( 32.921s elapsed) GC time 7.792s ( 2.126s elapsed) EXIT time 0.003s ( 0.001s elapsed) Total time 175.141s ( 35.051s elapsed) Alloc rate 608,230,568 bytes per MUT second Productivity 95.5% of total user, 93.9% of total elapsed ```-s
report of the twoAnd a quick look at the
hello-spock.prof
reveals thatCrypto.Random
is doing quite a bit of work: