diff --git a/bench/Bench.hs b/bench/Bench.hs
new file mode 100644
--- /dev/null
+++ b/bench/Bench.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE StandaloneDeriving #-}
+
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+import Criterion.Main as Criterion
+
+import GHC.Generics (Generic)
+import Control.DeepSeq (NFData)
+import Control.Monad
+import Database.Sqlite.Easy
+
+
+deriving instance NFData  SQLData
+
+data BenchEnv = BenchEnv
+  { largeSampleRows :: [[SQLData]]
+  , smallSmapleRows :: [[SQLData]]
+  } deriving (Generic, NFData)
+
+main :: IO ()
+main = defaultMain
+  [ env setupEnv $ \ ~(BenchEnv{..}) ->
+    bgroup "sqlite-easy-insert" 
+        [ bench "1000 rows/individual"     $ insertBench False smallSmapleRows
+        , bench "1000 rows/bulk"           $ insertBench True  smallSmapleRows
+        , bench "100000 rows/individual"   $ insertBench False largeSampleRows
+        , bench "100000 rows/bulk"         $ insertBench True  largeSampleRows
+        ]
+  ]
+
+setupEnv :: IO BenchEnv
+setupEnv =
+  pure $ BenchEnv (take 100000 rows) (take 1000 rows)
+  where
+    rows = cycle
+      [ [SQLInteger 42, SQLFloat 3.14152,   SQLText "The quick brown fox jumps over the lazy dog."]
+      , [SQLInteger  0, SQLFloat 12.131516, SQLText "If you've got nothing to say, start a band."]
+      ]
+
+createTable, insertTable :: SQL
+createTable = "CREATE TABLE bench (b_int INTEGER, b_float REAL, b_text TEXT)"
+insertTable = "INSERT INTO bench VALUES (?,?,?)"
+
+insertBench :: Bool -> [[SQLData]] -> Benchmarkable
+insertBench bulkMode rows = nfIO $
+  withDb ":memory:" $ do
+    run createTable
+    void $ transaction $ if bulkMode
+      then insertTable `runWithMany` rows
+      else mapM (insertTable `runWith`) rows
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,9 @@
+
+### 1.1.1.0
+
+* Add `runWithMany`, which reuses a prepared statement for many parameter rows (e.g. to speed up bulk inserts) (by @michael207)
+* Add benchmarking suite (by @michael207)
+
 ### 1.1.0.1
 
 * Make migrations containing transactions work.
diff --git a/sqlite-easy.cabal b/sqlite-easy.cabal
--- a/sqlite-easy.cabal
+++ b/sqlite-easy.cabal
@@ -1,7 +1,7 @@
 cabal-version: 2.4
 
 name: sqlite-easy
-version: 1.1.0.1
+version: 1.1.1.0
 synopsis: A primitive yet easy to use sqlite library.
 description: A primitive yet easy to use sqlite library built using sqlite-direct, resource-pool and migrant.
 author: Gil Mizrahi
@@ -55,3 +55,17 @@
     -threaded -rtsopts -with-rtsopts=-N
   build-tool-depends:
     hspec-discover:hspec-discover
+
+benchmark sqlite-easy-bench
+  type: exitcode-stdio-1.0
+  hs-source-dirs: bench
+  main-is: Bench.hs
+  other-modules:
+  build-depends:
+      base
+    , criterion
+    , deepseq
+    , sqlite-easy
+  default-language: Haskell2010
+  ghc-options:
+    -threaded -rtsopts -with-rtsopts=-N
diff --git a/src/Database/Sqlite/Easy.hs b/src/Database/Sqlite/Easy.hs
--- a/src/Database/Sqlite/Easy.hs
+++ b/src/Database/Sqlite/Easy.hs
@@ -21,6 +21,7 @@
     -- $running
   , run
   , runWith
+  , runWithMany
   , SQLite
   , liftIO
   , fromString
diff --git a/src/Database/Sqlite/Easy/Internal.hs b/src/Database/Sqlite/Easy/Internal.hs
--- a/src/Database/Sqlite/Easy/Internal.hs
+++ b/src/Database/Sqlite/Easy/Internal.hs
@@ -96,16 +96,31 @@
 run :: SQL -> SQLite [[SQLData]]
 run (SQL stmt) = do
   db <- getDB
-  liftIO $ bracket (Direct.prepare db stmt) Direct.finalize fetchAll
+  liftIO $ Direct.withStatement db stmt fetchAll
 
 -- | Run a SQL statement with certain parameters on a database and fetch the results.
 runWith :: SQL -> [SQLData] -> SQLite [[SQLData]]
 runWith (SQL stmt) params = do
   db <- getDB
-  liftIO $ do
-    bracket (Direct.prepare db stmt) Direct.finalize $ \preparedStmt -> do
+  liftIO $ Direct.withStatement db stmt $ \preparedStmt -> do
       Direct.bind preparedStmt params
       fetchAll preparedStmt
+
+-- | Run a SQL statement binding it to all given parameter rows and fetch all of the data.
+--   This can significantly improve speed for cases like bulk INSERTs.
+--
+-- @since 1.1.1.0
+runWithMany :: SQL -> [[SQLData]] -> SQLite [[[SQLData]]]
+runWithMany (SQL stmt) paramsList = do
+  db <- getDB
+  liftIO $ Direct.withStatement db stmt $ \preparedStmt ->
+    let
+      runRow params = do
+        Direct.reset preparedStmt
+        Direct.clearBindings preparedStmt
+        Direct.bind preparedStmt params
+        fetchAll preparedStmt
+    in mapM runRow paramsList
 
 -- | Run a statement and fetch all of the data.
 fetchAll :: Statement -> IO [[SQLData]]
diff --git a/src/Database/Sqlite/Easy/Migrant.hs b/src/Database/Sqlite/Easy/Migrant.hs
--- a/src/Database/Sqlite/Easy/Migrant.hs
+++ b/src/Database/Sqlite/Easy/Migrant.hs
@@ -1,5 +1,7 @@
 {-# language OverloadedStrings #-}
 
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
 -- | Migrations support based on @migrant-core@.
 module Database.Sqlite.Easy.Migrant
   ( module Database.Migrant.Driver.Class
