diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,12 @@
 # Changelog for `faster-megaparsec`
 
-All notable changes to this project will be documented in this file.
+## 0.1.0.1
 
-The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
-and this project adheres to the
-[Haskell Package Versioning Policy](https://pvp.haskell.org/).
+Test suite comparing `SimpleParser` against `Parsec`.
 
-## Unreleased
+# 0.1.1
 
-## 0.1.0.0 - YYYY-MM-DD
+Changed the definition of `takeP_` to honour the spec
+(fail if the input stream is shorter than requested). 
+
+Re-factored the test suite to include a benchmark.
diff --git a/faster-megaparsec.cabal b/faster-megaparsec.cabal
--- a/faster-megaparsec.cabal
+++ b/faster-megaparsec.cabal
@@ -1,6 +1,6 @@
 cabal-version: 1.12
 name:           faster-megaparsec
-version:        0.1.0.1
+version:        0.1.1.0
 description:    see README.md
 homepage:       https://hub.darcs.net/olf/faster-megaparsec
 bug-reports:    https://hub.darcs.net/olf/faster-megaparsec/issues
@@ -39,6 +39,7 @@
   main-is: Spec.hs
   other-modules:
       Paths_faster_megaparsec
+      GenLanguage
   hs-source-dirs:
       test
   ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N
@@ -49,4 +50,26 @@
     , faster-megaparsec
     , text
     , QuickCheck
+  default-language: Haskell2010
+
+benchmark faster-megaparsec-benchmark
+  type: exitcode-stdio-1.0
+  main-is: Bench.hs
+  other-modules:
+      Paths_faster_megaparsec
+      GenLanguage
+  hs-source-dirs:
+      test
+  ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded
+  build-depends:
+      base >=4.7 && <5
+    , containers >= 0.5.11
+    , directory
+    , megaparsec
+    , faster-megaparsec
+    , text
+    , QuickCheck
+    , tasty-bench
+    , deepseq
+    , temporary
   default-language: Haskell2010
diff --git a/src/Text/Megaparsec/Simple.hs b/src/Text/Megaparsec/Simple.hs
--- a/src/Text/Megaparsec/Simple.hs
+++ b/src/Text/Megaparsec/Simple.hs
@@ -141,8 +141,18 @@
 -- Since 'Mega.takeP' requests this parser to succeed only if 
 -- the requested number of tokens can be returned, and we can never 
 -- return a negative number of tokens, this parser fails for negative inputs. 
+-- This behaviour changes in Megaparsec with version 9.3.0
+#if MIN_VERSION_megaparsec(9,3,0)
 countAny :: forall s. (Mega.Stream s) => Int -> StateT s Maybe (Mega.Tokens s)
-countAny n = if n < 0 then mzero else StateT (\s -> Mega.takeN_ n s)
+countAny n = StateT (\s -> 
+    Mega.takeN_ n s >>= 
+        (\x@(taken,_) -> if Mega.chunkLength (Proxy :: Proxy s) taken < n then Nothing else Just x))
+#else
+countAny :: forall s. (Mega.Stream s) => Int -> StateT s Maybe (Mega.Tokens s)
+countAny n = if n < 0 then mzero else StateT (\s -> 
+    Mega.takeN_ n s >>= 
+        (\x@(taken,_) -> if Mega.chunkLength (Proxy :: Proxy s) taken < n then Nothing else Just x))
+#endif
 {-# INLINE countAny #-}
 
 -- | (For the 'MonadParsec' instance) 'Parser' does not contain any state besides the input left to parse. 
diff --git a/test/Bench.hs b/test/Bench.hs
new file mode 100644
--- /dev/null
+++ b/test/Bench.hs
@@ -0,0 +1,125 @@
+{-- Benchmarks.
+We re-use the 'Language' generator of thr test suite 
+and compare how long 'SimpleParser' and 'Parsec' take 
+to parse all words of a language, 
+which is written to a file before parsing, 
+to ensure that both parsers start from an equally-evaluated input. 
+--}
+{-# LANGUAGE OverloadedStrings, RankNTypes, FlexibleContexts, TypeApplications #-}
+import GenLanguage
+import qualified Test.QuickCheck as Q
+import Data.Text (Text)
+import qualified Data.Text as T
+import qualified Data.Text.IO as TIO
+import Data.Set (Set)
+import qualified Data.Set as Set
+import Data.Void (Void)
+import Text.Megaparsec (MonadParsec(..),runParser,sepBy,single)
+import Text.Megaparsec.Simple 
+import Test.Tasty.Bench (Benchmark,bench,bgroup,bcompare,envWithCleanup,nfIO,defaultMain)
+import System.Directory (removeFile)
+import System.IO.Temp (emptySystemTempFile)
+import Control.DeepSeq (NFData(..))
+
+
+-- * hard-coded test benchmark parameters 
+
+-- | size parameters passed to the language generator 'randomLanguages' 
+useLangSizes :: [Int]
+useLangSizes = three `mtimes` (10:[100,120..200]) where
+    mtimes :: Monad m => m () -> m a -> m a
+    mtimes scalar m = m >>= (\a -> fmap (const a) scalar)
+    three = [(),(),()] -- generate three languages of each size
+
+-- * benchmark
+
+writeAllWords :: FilePath -> Language (Set Text) -> IO ()
+writeAllWords file = TIO.writeFile file . 
+    foldl1 (\txt w -> txt <> "\n" <> w) .
+    allWords
+
+-- | read the file written by 'writeAllWords' 
+-- and parse using the 'wordsParser', 
+-- printing the parsed words to stdout.
+-- 
+-- @
+-- parseAllWordsWith (runParser @Void)
+-- parseAllWordsWith simpleParse
+-- @
+parseAllWordsWith :: (MonadParsec Void Text p) => 
+    (forall a. p a -> String -> Text -> result a) -> 
+    Language x -> 
+    FilePath -> IO (result [Text])
+parseAllWordsWith runP lang testFile = fmap 
+    (runP (wordsParser lang) testFile) 
+    (TIO.readFile testFile)
+
+-- | we keep this paremetric so that 
+-- we don't have to mention the megaparsec error type 
+-- which changed through library versions. 
+ignoreParsecError :: Either err [Text] -> [Text]
+ignoreParsecError = either (const []) id
+
+ignoreNothing :: Maybe [Text] -> [Text]
+ignoreNothing = maybe [] id
+
+-- | generate a parser for words separated by newline characters
+wordsParser :: MonadParsec Void Text p => Language x -> p [Text]
+wordsParser lang = let langWord = genParser lang in (langWord `sepBy` single '\n') <* eof
+
+simpleParse :: SimpleParser Text a -> String -> Text -> Maybe a
+simpleParse p _ s = fmap  fst (runSimpleParser p s)
+
+data ParserBenchmark = ParserBenchmark {
+    parseFromFile :: FilePath,
+    runOrdinary :: IO [Text],
+    runSimple   :: IO [Text]
+    }
+instance NFData ParserBenchmark where
+    rnf = rnf . parseFromFile -- ^ 'envWithCleanup' requires its type parameter to be member of 'NFData' bot IO actions do not have a normal form. It is enough if the 'testFile' is written. 
+
+-- | 'writeAllWords' and return two actions that parse the same test file, 
+-- to be compared in the benchmark.
+genBenchmarkable :: Language (Set Text) -> IO ParserBenchmark
+genBenchmarkable lang = do
+    testFile <- emptySystemTempFile "parserBenchmark.txt"
+    writeAllWords testFile lang
+    return $ ParserBenchmark
+        testFile
+        (fmap ignoreParsecError (parseAllWordsWith (runParser @Void) lang testFile)) 
+        (fmap ignoreNothing     (parseAllWordsWith simpleParse       lang testFile))
+
+langDepth :: Language x -> Int
+langDepth (Word _) = 0
+langDepth (Choice _ x y) = 1 + max (langDepth x) (langDepth y)
+langDepth (Concat _ x y) = 1 + langDepth x + langDepth y
+
+benchName :: Language (Set Text) -> String
+benchName lang = let ws = allWords lang in 
+    "language of depth " <> show (langDepth lang) <> 
+        " and size " <> show (Set.size ws) <> 
+        " e.g. " <> T.unpack (Set.findMin ws)
+
+genBenchmark :: Language (Set Text) -> Benchmark
+genBenchmark lang = envWithCleanup setUp cleanUp genBench where
+    setUp = genBenchmarkable lang
+    cleanUp = removeFile . parseFromFile
+    genBench :: ParserBenchmark -> Benchmark
+    genBench pb = bgroup (benchName lang) [
+        bench    "Parsec"                       (nfIO (runOrdinary pb)),
+        bcompare "Parsec" (bench "SimpleParser" (nfIO (runSimple   pb)))
+        ]
+
+-- | Generate as many 'Language's as integers given,
+-- where the integer is the size parameter passed to QuickCheck's 
+-- 'Q.Gen'erator. 
+randomLanguages :: [Int] -> IO [Language (Set Text)]
+randomLanguages sizes = Q.generate (traverse (flip Q.resize Q.arbitrary) sizes)
+
+genBenchmarks :: IO Benchmark
+genBenchmarks = do
+    langs <- randomLanguages useLangSizes 
+    return (bgroup "parser comparison" (fmap genBenchmark langs))
+
+main :: IO ()
+main = genBenchmarks >>= (defaultMain.pure)
diff --git a/test/GenLanguage.hs b/test/GenLanguage.hs
new file mode 100644
--- /dev/null
+++ b/test/GenLanguage.hs
@@ -0,0 +1,142 @@
+{-# LANGUAGE FlexibleInstances,FlexibleContexts,DeriveGeneric,DeriveFunctor #-}
+module GenLanguage where
+import Data.String (IsString(..))
+import Data.Text (Text)
+import qualified Data.Text as T
+import Data.List (transpose)
+import Control.Applicative (liftA2)
+import GHC.Generics (Generic)
+import Test.QuickCheck (Arbitrary(..))
+import qualified Test.QuickCheck as Q
+import Data.Set (Set)
+import qualified Data.Set as Set
+import Data.Void (Void)
+import Text.Megaparsec
+
+-- * Generating random languages to parse
+
+-- ** Languages
+
+-- | The language is designed to test the basic 
+-- parser operations concatenation and choice. 
+data Language x = Word String
+    | Choice x (Language x) (Language x)
+    | Concat x (Language x) (Language x)
+    deriving (Show,Eq,Ord,Generic,Functor)
+
+instance IsString (Language x) where
+    fromString = Word
+instance Semigroup (Language ()) where
+    (<>) = Concat ()
+instance Semigroup (Language (Set Text)) where
+    (Word w1) <> (Word w2) = Word (w1<>w2)
+    x@(Word w) <> y@(Choice ws _ _) = let pfx = fromString w 
+        in Concat (Set.mapMonotonic (pfx<>) ws) x y
+    x@(Word w) <> y@(Concat ws _ _) = let pfx = fromString w
+        in Concat (Set.mapMonotonic (pfx<>) ws) x y
+    x <> y = let ws = Set.map (uncurry (<>)) (Set.cartesianProduct (allWords x) (allWords y))
+        in Concat ws x y
+
+class Monad m => NonDeterministic m where
+    nonDeterministically :: [m a] -> m a
+instance NonDeterministic [] where
+    nonDeterministically = concat . transpose -- can enumerate finite choice of infinite lists
+instance NonDeterministic Q.Gen where
+    nonDeterministically = Q.oneof
+
+class Conditionable m where
+    suchThat :: m a -> (a -> Bool) -> m a
+instance Conditionable [] where
+    suchThat = flip filter
+instance Conditionable Q.Gen where 
+    suchThat = Q.suchThat
+instance Conditionable Set where
+    suchThat = flip Set.filter
+
+
+infixl 3 <||>
+-- | The choice operator of 'Language's
+(<||>) :: Language (Set Text) -> Language (Set Text) -> Language (Set Text)
+x <||> y = Choice (choiceWords (allWords x) (allWords y)) x y
+
+-- |Even with backtracking, 
+-- a parser may fail to recognize a word of the language 
+-- if the choices are ordered in a way such that 
+-- an earlier choice contains a proper prefix of a later choice. 
+-- Consider for example the regular expression
+-- 
+-- @
+-- (a|ab)c
+-- @
+--
+-- and the word @abc@ which is not accepted by the parser 
+-- 
+-- @
+-- (try (chunk "a") <|> chunk "ab") <> chunk "c"
+-- @
+-- 
+-- but is accepted by the parser 
+-- 
+-- @
+-- (try (chunk "ab") <|> chunk "a") <> chunk "c"
+-- @
+choiceWords :: Set Text -> Set Text -> Set Text
+choiceWords left right = Set.union left (right `suchThat` notSuffixOf left) where
+    notSuffixOf earlier = \w -> not (any (\a -> a `T.isPrefixOf` w) earlier)
+
+-- | 'nonDeterministically' sample from 'allWords' of a 'Language'.
+genWords :: NonDeterministic gen => 
+    Language (Set Text) -> gen Text
+genWords = nonDeterministically . fmap pure . Set.toList . allWords
+
+-- | We record the set of words in the constructor.
+allWords :: Language (Set Text) -> Set Text
+allWords (Word w) = Set.singleton (fromString w)
+allWords (Choice ws _ _) = ws
+allWords (Concat ws _ _) = ws
+
+-- ** parsing a language
+
+-- | generate a 'MonadParsec' parser for words of the given 'Language'
+genParser :: MonadParsec Void Text p => Language x -> p Text
+genParser (Word txt) = chunk (fromString txt)
+genParser (Choice _ x y) = try (genParser x) <|> genParser y -- backtracking choice
+genParser (Concat _ x y) = liftA2 (<>) (genParser x) (genParser y)
+
+-- ** non-deterministic language generation
+
+-- non-deterministically generate a language
+-- 
+-- >>> mapM_ print $ fmap (const ()) $ genLanguage ["Foo","Bar"] 1
+-- >>> Q.sample' (arbitrary :: Q.Gen (Language (Set Text))) >>= (print.fmap (const ()).head) 
+genLanguage :: NonDeterministic gen => 
+    gen String -> 
+    Int -> 
+    gen (Language (Set Text))
+genLanguage genWord = let 
+    sizedLang = \size -> if size <= 0 then fmap Word genWord else let
+        lang' = sizedLang (size `div` 2)
+        in nonDeterministically [
+            fmap Word genWord,
+            liftA2 (<>)   lang' lang',
+            liftA2 (<||>) lang' lang'
+            ]
+    in sizedLang
+
+-- | We make single-letter alphanumeric words the basic building blocks of languages
+genAlphaChar :: NonDeterministic gen => gen Char
+genAlphaChar = nonDeterministically [return c | c <- ['a'..'z']]
+
+genWordQ :: Q.Gen String
+genWordQ = fmap pure genAlphaChar -- use single-letter words as building blocks
+-- genWordQ = let g = genAlphaChar in liftA2 (:) (fmap toUpper g) (Q.listOf g)
+
+instance Arbitrary (Language (Set Text)) where
+    arbitrary = Q.sized (genLanguage genWordQ)
+    shrink (Word _) = []
+    shrink (Concat _ lang1 lang2) = [lang1,lang2] ++ [x <>   y | (x,y) <- shrink (lang1,lang2)]
+    shrink (Choice _ lang1 lang2) = [lang1,lang2] ++ [x <||> y | (x,y) <- shrink (lang1,lang2)]
+
+-- maximum word length
+maxWord :: Language (Set Text) -> Int
+maxWord = Set.foldl' (\imum w -> max imum (T.length w)) 0 . allWords
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -22,11 +22,10 @@
     FlexibleInstances, 
     TypeFamilies, 
     OverloadedStrings, 
-    DeriveGeneric, 
-    DeriveFunctor, 
     RankNTypes,
     ScopedTypeVariables
 #-}
+import GenLanguage -- randomly generate languages to parse
 import Text.Megaparsec
 import Text.Megaparsec.Simple
 import Data.Void (Void)
@@ -34,13 +33,10 @@
 import Data.Text (Text)
 import qualified Data.Text as T
 import Data.Maybe (isJust)
-import Data.List (transpose)
 import Control.Applicative (liftA2)
-import GHC.Generics (Generic)
 import Test.QuickCheck (Arbitrary(..))
 import qualified Test.QuickCheck as Q
 import Data.Set (Set)
-import qualified Data.Set as Set
 
 main :: IO ()
 main = do
@@ -59,130 +55,11 @@
     putStrLn "testing whether takeP behaves identically"
     Q.quickCheck prop_takeP
 
+-- | Parser selector. 'Ordinary' selects 'Parsec', 
+-- 'Fast' selects 'SimpleParser'. 
 data WhichParser = Fast | Ordinary
 
--- * Parsing languages
-
--- | The language is designed to test the basic 
--- parser operations concatenation and choice. 
-data Language x = Word String
-    | Choice x (Language x) (Language x)
-    | Concat x (Language x) (Language x)
-    deriving (Show,Eq,Ord,Generic,Functor)
-
-instance IsString (Language x) where
-    fromString = Word
-instance Semigroup (Language ()) where
-    (<>) = Concat ()
-instance Semigroup (Language (Set Text)) where
-    (Word w1) <> (Word w2) = Word (w1<>w2)
-    x@(Word w) <> y@(Choice ws _ _) = let pfx = fromString w 
-        in Concat (Set.mapMonotonic (pfx<>) ws) x y
-    x@(Word w) <> y@(Concat ws _ _) = let pfx = fromString w
-        in Concat (Set.mapMonotonic (pfx<>) ws) x y
-    x <> y = let ws = Set.map (uncurry (<>)) (Set.cartesianProduct (allWords x) (allWords y))
-        in Concat ws x y
-
-class Monad m => NonDeterministic m where
-    nonDeterministically :: [m a] -> m a
-instance NonDeterministic [] where
-    nonDeterministically = concat . transpose -- can enumerate finite choice of infinite lists
-instance NonDeterministic Q.Gen where
-    nonDeterministically = Q.oneof
-
-class Conditionable m where
-    suchThat :: m a -> (a -> Bool) -> m a
-instance Conditionable [] where
-    suchThat = flip filter
-instance Conditionable Q.Gen where 
-    suchThat = Q.suchThat
-instance Conditionable Set where
-    suchThat = flip Set.filter
-
-
-infixl 3 <||>
--- | The choice operator of 'Language's
-(<||>) :: Language (Set Text) -> Language (Set Text) -> Language (Set Text)
-x <||> y = Choice (choiceWords (allWords x) (allWords y)) x y
-
--- |Even with backtracking, 
--- a parser may fail to recognize a word of the language 
--- if the choices are ordered in a way such that 
--- an earlier choice contains a proper prefix of a later choice. 
--- Consider for example the regular expression
--- 
--- @
--- (a|ab)c
--- @
---
--- and the word @abc@ which is not accepted by the parser 
--- 
--- @
--- (try (chunk "a") <|> chunk "ab") <> chunk "c"
--- @
--- 
--- but is accepted by the parser 
--- 
--- @
--- (try (chunk "ab") <|> chunk "a") <> chunk "c"
--- @
-choiceWords :: Set Text -> Set Text -> Set Text
-choiceWords left right = Set.union left (right `suchThat` notSuffixOf left) where
-    notSuffixOf earlier = \w -> not (any (\a -> a `T.isPrefixOf` w) earlier)
-
-genWords :: NonDeterministic gen => 
-    Language (Set Text) -> gen Text
-genWords = nonDeterministically . fmap pure . Set.toList . allWords
-
--- | We record the set of words in the constructor.
-allWords :: Language (Set Text) -> Set Text
-allWords (Word w) = Set.singleton (fromString w)
-allWords (Choice ws _ _) = ws
-allWords (Concat ws _ _) = ws
-
--- ** non-deterministic language generation
-
--- non-deterministically generate a language
--- 
--- >>> mapM_ print $ fmap (const ()) $ genLanguage ["Foo","Bar"] 1
--- >>> Q.sample' (arbitrary :: Q.Gen (Language (Set Text))) >>= (print.fmap (const ()).head) 
-genLanguage :: NonDeterministic gen => 
-    gen String -> 
-    Int -> 
-    gen (Language (Set Text))
-genLanguage genWord = let 
-    sizedLang = \size -> if size <= 0 then fmap Word genWord else let
-        lang' = sizedLang (size `div` 2)
-        in nonDeterministically [
-            fmap Word genWord,
-            liftA2 (<>)   lang' lang',
-            liftA2 (<||>) lang' lang'
-            ]
-    in sizedLang
-
-genAlphaChar :: NonDeterministic gen => gen Char
-genAlphaChar = nonDeterministically [return c | c <- ['a'..'z']]
-
-genWordQ :: Q.Gen String
-genWordQ = fmap pure genAlphaChar -- use single-letter words as building blocks
--- genWordQ = let g = genAlphaChar in liftA2 (:) (fmap toUpper g) (Q.listOf g)
-
-instance Arbitrary (Language (Set Text)) where
-    arbitrary = Q.sized (genLanguage genWordQ)
-    shrink (Word _) = []
-    shrink (Concat _ lang1 lang2) = [lang1,lang2] ++ [x <>   y | (x,y) <- shrink (lang1,lang2)]
-    shrink (Choice _ lang1 lang2) = [lang1,lang2] ++ [x <||> y | (x,y) <- shrink (lang1,lang2)]
-
--- maximum word length
-maxWord :: Language (Set Text) -> Int
-maxWord = Set.foldl' (\imum w -> max imum (T.length w)) 0 . allWords
-
--- ** checking the behaviour of parsers
-
-genParser :: MonadParsec Void Text p => Language x -> p Text
-genParser (Word txt) = chunk (fromString txt)
-genParser (Choice _ x y) = try (genParser x) <|> genParser y -- backtracking choice
-genParser (Concat _ x y) = liftA2 (<>) (genParser x) (genParser y)
+-- * checking the behaviour of parsers
 
 -- Tests whether 'genParser' accepts all words generated by 'genWords'.
 -- This can take loooong if the language is large.
