jvm-batching (empty) → 0.1
raw patch · 16 files changed
+1652/−0 lines, 16 filesdep +basedep +bytestringdep +criterionbuild-type:Customsetup-changed
Dependencies added: base, bytestring, criterion, deepseq, distributed-closure, hspec, inline-java, jni, jvm, jvm-batching, singletons, split, text, vector
Files
- LICENSE +30/−0
- README.md +38/−0
- Setup.hs +4/−0
- benchmarks/Main.hs +57/−0
- build.gradle +5/−0
- jvm-batching.cabal +85/−0
- src/cabal/haskell/Language/Java/Batching/Jars.hs +7/−0
- src/main/haskell/Language/Java/Batching.hs +628/−0
- src/main/java/io/tweag/jvm/batching/BatchReader.java +16/−0
- src/main/java/io/tweag/jvm/batching/BatchReaders.java +304/−0
- src/main/java/io/tweag/jvm/batching/BatchWriter.java +14/−0
- src/main/java/io/tweag/jvm/batching/BatchWriters.java +409/−0
- src/main/java/io/tweag/jvm/batching/Tuple2.java +10/−0
- src/test/haskell/Language/Java/BatchingSpec.hs +34/−0
- src/test/haskell/Main.hs +10/−0
- src/test/haskell/Spec.hs +1/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2016-2017 LeapYear Technologies, Inc.++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Author name here nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,38 @@+# jvm-batching++Provides batched marshalling of values between Java and Haskell.++## Using it as a dependency++Add `jvm-batching` to the list of dependencies in your .cabal file.+Then edit the `Setup.hs` file to add the `jvm-batching.jar` to the+classpath.++```Haskell+import Distribution.Simple+import Language.Java.Inline.Cabal+import qualified Language.Java.Batching.Jars++main = do+ jars <- Language.Java.Batching.Jars.getJars+ defaultMainWithHooks (addJarsToClasspath jars simpleUserHooks)+```++Add a `custom-setup` stanza to your .cabal file.++```+custom-setup+ setup-depends:+ base,+ Cabal,+ inline-java,+ jvm-batching+```++## Layout of source directories++This is a multi-language package. We use+Maven's [standard directory layout][maven-sdl] to organize source code+in multiple languages side-by-side.++[maven-sdl]: https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html
+ Setup.hs view
@@ -0,0 +1,4 @@+import Distribution.Simple+import Language.Java.Inline.Cabal++main = defaultMainWithHooks (gradleHooks simpleUserHooks)
+ benchmarks/Main.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Main where++import Criterion.Main as Criterion+import Control.DeepSeq+import Data.Int+import qualified Data.Vector as V+import Data.List.Split (chunksOf)+import qualified Data.Vector.Storable as VS+import Language.Java+import Language.Java.Batching ()++roundtrip :: forall a. (Reify a, Reflect a) => a -> IO ()+roundtrip a = pushWithSizeHint 30 $ (reflect a >>= reify :: IO a) >> pop++intValues :: [Int32]+intValues = [1..1000]++vValues :: [VS.Vector Int32]+vValues = [ VS.fromList [i, i] | i <- [1..500] ]++benchBatching :: Benchmark+benchBatching = deepseq intValues $+ bgroup "roundtrips"+ [ bgroup "ints"+ [ bgroup "no batching"+ [ bench "list" $ nfIO $ roundtrip intValues+ , bench "storable array" $ nfIO $ roundtrip $ VS.fromList intValues+ ]+ , bgroup "batching"+ [ bench "bare" $ nfIO $ roundtrip $ V.fromList intValues+ , bench "unboxed vectors" $ nfIO $ roundtrip $+ V.fromList $ map VS.fromList $+ chunksOf (length intValues `div` 7) intValues+ , bench "boxed vectors" $ nfIO $ roundtrip $+ V.fromList $ map V.fromList $+ chunksOf (length intValues `div` 7) intValues+ ]+ ]+ , bgroup "int vectors"+ [ bgroup "no batching"+ [ bench "list" $ nfIO $ roundtrip vValues+ ]+ , bgroup "batching"+ [ bench "bare" $ nfIO $ roundtrip $ V.fromList vValues+ , bench "vectors of vectors" $ nfIO $ roundtrip $+ V.fromList $ map V.fromList $+ chunksOf (length vValues `div` 7) vValues+ ]+ ]+ ]++main :: IO ()+main = withJVM ["-Djava.class.path=build/libs/jvm-batching.jar"] $+ Criterion.defaultMain [benchBatching]
+ build.gradle view
@@ -0,0 +1,5 @@+apply plugin: 'java'++dependencies {+ compile files(buildscript.sourceFile.getParent() + "/build/libs/jvm-batching.jar")+}
+ jvm-batching.cabal view
@@ -0,0 +1,85 @@+name: jvm-batching+version: 0.1+synopsis: Provides batched marshalling of values between Java and Haskell.+description: Please see README.md.+homepage: http://github.com/tweag/inline-java/tree/master/jvm-batching#readme+license: BSD3+license-file: LICENSE+author: Tweag I/O+maintainer: facundo.dominguez@tweag.io+copyright: 2017 LeapYear Technologies+category: FFI, JVM, Java+build-type: Custom+cabal-version: >=1.10+extra-source-files:+ src/main/java/io/tweag/jvm/batching/Tuple2.java+ src/main/java/io/tweag/jvm/batching/BatchWriter.java+ src/main/java/io/tweag/jvm/batching/BatchWriters.java+ src/main/java/io/tweag/jvm/batching/BatchReader.java+ src/main/java/io/tweag/jvm/batching/BatchReaders.java+ README.md+ build.gradle++source-repository head+ type: git+ location: https://github.com/tweag/inline-java+ subdir: jvm-batching++custom-setup+ setup-depends:+ base,+ Cabal >= 1.24,+ inline-java >= 0.7++library+ hs-source-dirs: src/main/haskell src/cabal/haskell+ default-language: Haskell2010+ exposed-modules:+ Language.Java.Batching+ Language.Java.Batching.Jars+ other-modules:+ Paths_jvm_batching+ build-depends:+ base >= 4.7 && < 5,+ bytestring,+ distributed-closure >= 0.3.5,+ jni >= 0.6.0 && <0.7,+ jvm >= 0.4.0 && <0.5,+ inline-java >= 0.7 && <0.9,+ singletons >= 2.2,+ text,+ vector++test-suite spec+ type:+ exitcode-stdio-1.0+ hs-source-dirs: src/test/haskell+ main-is: Main.hs+ other-modules:+ Language.Java.BatchingSpec+ Spec+ build-depends:+ base,+ bytestring,+ hspec,+ inline-java,+ jvm,+ jvm-batching,+ text,+ vector+ default-language: Haskell2010++benchmark micro-benchmarks+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs: benchmarks+ build-depends:+ base >=4.8 && <5,+ criterion,+ deepseq,+ jvm,+ jvm-batching,+ split,+ vector+ default-language: Haskell2010+ ghc-options: -threaded
+ src/cabal/haskell/Language/Java/Batching/Jars.hs view
@@ -0,0 +1,7 @@+-- | Provides the paths to the jars needed to build with jvm-batching.+module Language.Java.Batching.Jars where++import Paths_jvm_batching (getDataFileName)++getJars :: IO [String]+getJars = (:[]) <$> getDataFileName "build/libs/jvm-batching.jar"
+ src/main/haskell/Language/Java/Batching.hs view
@@ -0,0 +1,628 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StaticPointers #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# OPTIONS_GHC -fplugin=Language.Java.Inline.Plugin #-}++-- | This module provides composable batched marshalling.+--+-- === Batching+--+-- Calls to Java methods via JNI are slow in general. Marshalling an array of+-- primitive values can be as slow as marshalling a single value.+--+-- Because of this, reifying an iterator or a container is best done by+-- accumulates multiple elements on the java side before passing them to the+-- Haskell side. And conversely, when reflecting an iterator or container,+-- multiple Haskell values are put together before marshalling to the Java+-- side.+--+-- Some Haskell values can be batched trivially into arrays of primitive values.+-- 'Int32' can be batched in a java @int[]@, 'Double' can be batched in a java+-- @double[]@, etc. However, other types like @Tuple2 Int32 Double@ would+-- require more primitive arrays. Values of type @Tuple2 Int32 Double@ are+-- batched in a pair of java arrays of type @int[]@ and @double[]@.+--+-- > data Tuple2 a b = Tuple2 a b+--+-- More generally, the design aims to provide composable batchers. If one knows+-- how to batch types @a@ and @b@, one can also batch @Tuple2 a b@, @[a]@,+-- @Vector a@, etc.+--+-- A reference to a batch of values in Java has the type @J (Batch a)@, where+-- @a@ is the Haskell type of the elements in the batch. e.g.+--+-- > type instance Batch Int32 = 'Array ('Prim "int")+-- > type instance Batch Double = 'Array ('Prim "double")+-- > type instance Batch (Tuple2 a b) =+-- > 'Class "scala.Tuple2" <> '[Batch a, Batch b]+--+-- When defining batching for a new type, one needs to tell how batches are+-- represented in Java by adding a type instance to the type family @Batch@.+-- In addition, procedures for adding and extracting values from the batch+-- need to be specified on both the Haskell and the Java side.+--+-- On the Java side, batches are built using the interface+-- @io.tweag.jvm.batching.BatchWriter@. On the Haskell side, these+-- batches are read using @reifyBatch@.+--+-- > class ( ... ) => BatchReify a where+-- > newBatchWriter+-- > :: proxy a+-- > -> IO (J ('Iface "io.tweag.jvm.batching.BatchWriter"+-- > <> [Interp a, Batch a]+-- > )+-- > )+-- > reifyBatch :: J (Batch a) -> Int32 -> IO (V.Vector a)+--+-- @newReifyBatched@ produces a java object implementing the @BatchWriter@+-- interface, and @reifyBatch@ allows to read a batch created in this fashion.+--+-- Conversely, batches can be read on the Java side using the interface+-- @io.tweag.jvm.batching.BatchReader@. And on the Haskell side, these+-- batches can be created with @reflectBatch@.+--+-- > class ( ... ) => BatchReflect a where+-- > newBatchReader+-- > :: proxy a+-- > -> IO (J ('Iface "io.tweag.jvm.batching.BatchReader"+-- > <> [Batch a, Interp a]+-- > )+-- > )+-- > reflectBatch :: V.Vector a -> IO (J (Batch a))+--+-- @newBatchReader@ produces a java object implementing the @BatchReader@+-- interface, and @reflectBatch@ allows to create these batches from vectors of+-- Haskell values.+--+-- The methods of @BatchReify@ and @BatchReflect@ offer default+-- implementations which marshal elements in the batch one at a time. Taking+-- advantage of batching requires defining the methods explicitly. The default+-- implementations are useful for cases where speed is not important, for+-- instance when the iterators to reflect or reify contain a single element or+-- just a very few.+--+-- 'Vector's and 'ByteString's are batched with the follow scheme.+--+-- > type instance Batch BS.ByteString+-- > = 'Class "io.tweag.jvm.batching.Tuple2" <>+-- > '[ 'Array ('Prim "byte")+-- > , 'Array ('Prim "int")+-- > ]+--+-- We use two arrays. One of the arrays contains the result of appending all of+-- the 'ByteString's in the batch. The other array contains the offset of each+-- vector in the resulting array. See 'ArrayBatch'.+--+module Language.Java.Batching+ ( Batchable(..)+ , BatchReify(..)+ , BatchReflect(..)+ -- * Array batching+ , ArrayBatch+ ) where++import Control.Distributed.Closure.TH+import Control.Exception (bracket)+import Control.Monad (forM_, foldM)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Unsafe as BS+import Data.Int+import Data.Singletons (SingI, Proxy(..))+import qualified Data.Text as Text+import qualified Data.Text.Foreign as Text+import qualified Data.Vector as V+import qualified Data.Vector.Mutable as VM+import qualified Data.Vector.Storable as VS+import Data.Word+import Foreign.C.Types (CChar)+import Foreign.ForeignPtr (newForeignPtr_, withForeignPtr)+import Foreign.JNI+import Foreign.Ptr+import Foreign.Storable+import Language.Java+import Language.Java.Inline++imports "io.tweag.jvm.batching.*"++-- | A class of types whose values can be marshaled in batches.+class (Interpretation a, SingI (Batch a)) => Batchable (a :: k) where+ -- | The type of java batches for reifying and reflecting values of type @a@.+ type family Batch a :: JType++-- | A class for batching reification of values.+--+-- It has a method to create a batcher that creates batches in Java, and+-- another method that refies a batch into a vector of haskell values.+--+-- The type of the batch used to appear as a class parameter but we run into+-- https://ghc.haskell.org/trac/ghc/ticket/13582+--+class Batchable a => BatchReify a where+ -- | Produces a batcher that aggregates elements of type @ty@ (such as @int@)+ -- and produces collections of type @Batch a@ (such as @int[]@).+ newBatchWriter+ :: proxy a+ -> IO (J ('Iface "io.tweag.jvm.batching.BatchWriter"+ <> [Interp a, Batch a]+ )+ )++ -- The default implementation makes calls to the JVM for each element in the+ -- batch.+ default newBatchWriter+ :: (Batch a ~ 'Array (Interp a))+ => proxy a+ -> IO (J ('Iface "io.tweag.jvm.batching.BatchWriter"+ <> [Interp a, Batch a]+ )+ )+ newBatchWriter _ = generic <$> [java| new BatchWriters.ObjectBatchWriter() |]++ -- | Reifies the values in a batch of type @Batch a@.+ -- Gets the batch and the amount of elements it contains.+ reifyBatch :: J (Batch a) -> Int32 -> IO (V.Vector a)++ -- The default implementation makes calls to the JVM for each element in the+ -- batch.+ default reifyBatch+ :: (Reify a, Batch a ~ 'Array (Interp a))+ => J (Batch a) -> Int32 -> IO (V.Vector a)+ reifyBatch jxs size =+ V.generateM (fromIntegral size) $ \i ->+ getObjectArrayElement jxs (fromIntegral i) >>= reify . unsafeCast++-- | Helper for reifying batches of primitive types+reifyPrimitiveBatch+ :: Storable a+ => (J ('Array ty) -> IO (Ptr a))+ -> (J ('Array ty) -> Ptr a -> IO ())+ -> J ('Array ty) -> Int32 -> IO (V.Vector a)+reifyPrimitiveBatch getArrayElements releaseArrayElements jxs size = do+ bracket (getArrayElements jxs) (releaseArrayElements jxs)+ $ V.generateM (fromIntegral size) . peekElemOff++-- | Batches of arrays of variable length+--+-- The first component is an array or batch containing the actual elements,+-- and the second component is an @int[]@ where the ith position has the+-- offset of the first position after the ith array.+type ArrayBatch ty =+ 'Class "io.tweag.jvm.batching.Tuple2" <>+ '[ ty+ , 'Array ('Prim "int")+ ]++-- | Helper for reifying batches of vectors+--+-- Arrays are batched with two arrays. One of the arrays contains the result+-- of appending all of the vectors in the batch. The other array contains the+-- offset of each vector in the resulting array.+--+reifyArrayBatch+ :: forall a b ty.+ (Int32 -> J ty -> IO a) -- ^ reify the array/batch of values+ -- (takes the amount of elements in the array)+ -> (Int -> Int -> a -> IO b) -- ^ slice at a given offset of given length of some value+ -> J (ArrayBatch ty)+ -> Int32+ -> IO (V.Vector b)+reifyArrayBatch reifyB slice batch0 batchSize = do+ result <- VM.new (fromIntegral batchSize)+ let batch = unsafeUngeneric batch0+ vecEnds <- [java| (int[])$batch._2 |] >>= reify+ let count = if VS.null vecEnds then 0 else VS.last vecEnds+ vecValues <- [java| $batch._1 |] >>= reifyB count . fromObject+ _ <- foldM (writeVector result vecEnds vecValues) 0 [0 .. batchSize - 1]+ V.unsafeFreeze result+ where+ fromObject :: JObject -> J x+ fromObject = unsafeCast++ writeVector :: VM.IOVector b -> VS.Vector Int32 -> a -> Int32 -> Int32 -> IO Int32+ writeVector mv ends values offset i32 = do+ let i = fromIntegral i32+ slice (fromIntegral offset)+ (fromIntegral $ ends VS.! i - offset) values+ >>= VM.unsafeWrite mv i+ return $ ends VS.! i++-- | A class for batching reflection of values.+--+-- It has a method to create a batch reader that reads batches in Java, and+-- another method that reflects a vector of haskell values into a batch.+--+-- We considered having the type of the batch appear as a class parameter but+-- we run into https://ghc.haskell.org/trac/ghc/ticket/13582+--+class Batchable a => BatchReflect a where+ -- | Produces a batch reader that receives collections of type @ty1@+ -- (such as @int[]@) and produces values of type @ty2@ (such as @int@).+ newBatchReader+ :: proxy a+ -> IO (J ('Iface "io.tweag.jvm.batching.BatchReader"+ <> [Batch a, Interp a]+ )+ )++ -- The default implementation makes calls to the JVM for each element in the+ -- batch.+ default newBatchReader+ :: (Batch a ~ 'Array (Interp a))+ => proxy a+ -> IO (J ('Iface "io.tweag.jvm.batching.BatchReader"+ <> [Batch a, Interp a]+ )+ )+ newBatchReader _ =+ generic <$> [java| new BatchReaders.ObjectBatchReader() |]++ -- | Reflects the values in a vector to a batch of type @ty@.+ reflectBatch :: V.Vector a -> IO (J (Batch a))+ -- The default implementation makes calls to the JVM for each element in the+ -- batch.+ default reflectBatch+ :: (Reflect a, Batch a ~ 'Array (Interp a))+ => V.Vector a -> IO (J (Batch a))+ reflectBatch v = do+ jxs <- newArray $ fromIntegral (V.length v)+ forM_ [0 .. V.length v - 1] $ \i ->+ withLocalRef (reflect (v V.! i))+ (setObjectArrayElement jxs (fromIntegral i))+ return jxs++-- | Helper for reflecting batches of primitive types+reflectPrimitiveBatch+ :: forall a ty. (Storable a, IsPrimitiveType ty)+ => (J ('Array ty) -> Int32 -> Int32 -> Ptr a -> IO ())+ -> V.Vector a -> IO (J ('Array ty))+reflectPrimitiveBatch setArrayRegion v = do+ let (fptr, offset, len) = VS.unsafeToForeignPtr (V.convert v)+ withForeignPtr fptr $ \ptr -> do+ jxs <- newArray (fromIntegral len)+ let aOffset = offset * sizeOf (undefined :: a)+ setArrayRegion jxs 0 (fromIntegral len) (plusPtr ptr aOffset)+ return jxs++-- | Helper for reflecting batches of vectors+--+-- The vector type is a, and vectors are manipulated exclusively with the+-- polymorphic functions given as arguments.+--+-- Vectors are batched with two arrays. One of the arrays contains the result+-- of appending all of the vectors in the batch. The other array contains the+-- offset of each vector in the resulting array.+--+reflectArrayBatch+ :: forall a b ty.+ (b -> IO (J ty))+ -> (a -> Int) -- ^ get length+ -> ([a] -> IO b) -- ^ concat+ -> V.Vector a+ -> IO (J (ArrayBatch ty))+reflectArrayBatch reflectB getLength concatenate vecs = do+ let ends = V.convert $ V.postscanl' (+) 0 $+ V.map (fromIntegral . getLength) vecs+ :: VS.Vector Int32+ bigvec <- concatenate $ V.toList vecs+ jvec <- reflectB bigvec+ jends <- reflect ends+ generic <$> Language.Java.new [ coerce (upcast jvec)+ , coerce (upcast jends)+ ]++withStatic [d|+ instance Batchable Bool where+ type Batch Bool = 'Array ('Prim "boolean")++ instance BatchReify Bool where+ newBatchWriter _ = [java| new BatchWriters.BooleanBatchWriter() |]+ reifyBatch jxs size = do+ let toBool w = if w == 0 then False else True+ bracket (getBooleanArrayElements jxs)+ (releaseBooleanArrayElements jxs) $+ \arr -> V.generateM (fromIntegral size)+ ((toBool <$>) . peekElemOff arr)++ instance Batchable CChar where+ type Batch CChar = 'Array ('Prim "byte")++ instance BatchReify CChar where+ newBatchWriter _ = [java| new BatchWriters.ByteBatchWriter() |]+ reifyBatch =+ reifyPrimitiveBatch getByteArrayElements releaseByteArrayElements++ instance Batchable Word16 where+ type Batch Word16 = 'Array ('Prim "char")++ instance BatchReify Word16 where+ newBatchWriter _ = [java| new BatchWriters.CharacterBatchWriter() |]+ reifyBatch =+ reifyPrimitiveBatch getCharArrayElements releaseCharArrayElements++ instance Batchable Int16 where+ type Batch Int16 = 'Array ('Prim "short")++ instance BatchReify Int16 where+ newBatchWriter _ = [java| new BatchWriters.ShortBatchWriter() |]+ reifyBatch =+ reifyPrimitiveBatch getShortArrayElements releaseShortArrayElements++ instance Batchable Int32 where+ type Batch Int32 = 'Array ('Prim "int")++ instance BatchReify Int32 where+ newBatchWriter _ = [java| new BatchWriters.IntegerBatchWriter() |]+ reifyBatch =+ reifyPrimitiveBatch getIntArrayElements releaseIntArrayElements++ instance Batchable Int64 where+ type Batch Int64 = 'Array ('Prim "long")++ instance BatchReify Int64 where+ newBatchWriter _ = [java| new BatchWriters.LongBatchWriter() |]+ reifyBatch =+ reifyPrimitiveBatch getLongArrayElements releaseLongArrayElements++ instance Batchable Float where+ type Batch Float = 'Array ('Prim "float")++ instance BatchReify Float where+ newBatchWriter _ = [java| new BatchWriters.FloatBatchWriter() |]+ reifyBatch =+ reifyPrimitiveBatch getFloatArrayElements releaseFloatArrayElements++ instance Batchable Double where+ type Batch Double = 'Array ('Prim "double")++ instance BatchReify Double where+ newBatchWriter _ = [java| new BatchWriters.DoubleBatchWriter() |]+ reifyBatch =+ reifyPrimitiveBatch getDoubleArrayElements releaseDoubleArrayElements++ instance BatchReflect Bool where+ newBatchReader _ = [java| new BatchReaders.BooleanBatchReader() |]+ reflectBatch = reflectPrimitiveBatch setBooleanArrayRegion+ . V.map (\w -> if w then 1 else 0)++ instance BatchReflect CChar where+ newBatchReader _ = [java| new BatchReaders.ByteBatchReader() |]+ reflectBatch = reflectPrimitiveBatch setByteArrayRegion++ instance BatchReflect Word16 where+ newBatchReader _ = [java| new BatchReaders.CharacterBatchReader() |]+ reflectBatch = reflectPrimitiveBatch setCharArrayRegion++ instance BatchReflect Int16 where+ newBatchReader _ = [java| new BatchReaders.ShortBatchReader() |]+ reflectBatch = reflectPrimitiveBatch setShortArrayRegion++ instance BatchReflect Int32 where+ newBatchReader _ = [java| new BatchReaders.IntegerBatchReader() |]+ reflectBatch = reflectPrimitiveBatch setIntArrayRegion++ instance BatchReflect Int64 where+ newBatchReader _ = [java| new BatchReaders.LongBatchReader() |]+ reflectBatch = reflectPrimitiveBatch setLongArrayRegion++ instance BatchReflect Float where+ newBatchReader _ = [java| new BatchReaders.FloatBatchReader() |]+ reflectBatch = reflectPrimitiveBatch setFloatArrayRegion++ instance BatchReflect Double where+ newBatchReader _ = [java| new BatchReaders.DoubleBatchReader() |]+ reflectBatch = reflectPrimitiveBatch setDoubleArrayRegion++#if ! (__GLASGOW_HASKELL__ == 800 && __GLASGOW_HASKELL_PATCHLEVEL1__ == 1)+ instance Batchable BS.ByteString where+ type Batch BS.ByteString+ = 'Class "io.tweag.jvm.batching.Tuple2" <>+ '[ 'Array ('Prim "byte")+ , 'Array ('Prim "int")+ ]++ instance Batchable (VS.Vector Word16) where+ type Batch (VS.Vector Word16)+ = 'Class "io.tweag.jvm.batching.Tuple2" <>+ '[ 'Array ('Prim "char")+ , 'Array ('Prim "int")+ ]++ instance Batchable (VS.Vector Int16) where+ type Batch (VS.Vector Int16)+ = 'Class "io.tweag.jvm.batching.Tuple2" <>+ '[ 'Array ('Prim "short")+ , 'Array ('Prim "int")+ ]++ instance Batchable (VS.Vector Int32) where+ type Batch (VS.Vector Int32)+ = 'Class "io.tweag.jvm.batching.Tuple2" <>+ '[ 'Array ('Prim "int")+ , 'Array ('Prim "int")+ ]++ instance Batchable (VS.Vector Int64) where+ type Batch (VS.Vector Int64)+ = 'Class "io.tweag.jvm.batching.Tuple2" <>+ '[ 'Array ('Prim "long")+ , 'Array ('Prim "int")+ ]++ instance Batchable (VS.Vector Float) where+ type Batch (VS.Vector Float)+ = 'Class "io.tweag.jvm.batching.Tuple2" <>+ '[ 'Array ('Prim "float")+ , 'Array ('Prim "int")+ ]++ instance Batchable (VS.Vector Double) where+ type Batch (VS.Vector Double)+ = 'Class "io.tweag.jvm.batching.Tuple2" <>+ '[ 'Array ('Prim "double")+ , 'Array ('Prim "int")+ ]++ instance Batchable Text.Text where+ type Batch Text.Text+ = 'Class "io.tweag.jvm.batching.Tuple2" <>+ '[ 'Array ('Prim "char")+ , 'Array ('Prim "int")+ ]++ instance BatchReify BS.ByteString where+ newBatchWriter _ = [java| new BatchWriters.ByteArrayBatchWriter() |]+ reifyBatch = reifyArrayBatch (const reify) bsUnsafeSlice+ where+ bsUnsafeSlice :: Int -> Int -> BS.ByteString -> IO BS.ByteString+ bsUnsafeSlice offset sz = return . BS.unsafeTake sz . BS.unsafeDrop offset++ instance BatchReify (VS.Vector Word16) where+ newBatchWriter _ = [java| new BatchWriters.CharArrayBatchWriter() |]+ reifyBatch =+ reifyArrayBatch (const reify) (fmap (fmap return) . VS.unsafeSlice)++ instance BatchReify (VS.Vector Int16) where+ newBatchWriter _ = [java| new BatchWriters.ShortArrayBatchWriter() |]+ reifyBatch =+ reifyArrayBatch (const reify) (fmap (fmap return) . VS.unsafeSlice)++ instance BatchReify (VS.Vector Int32) where+ newBatchWriter _ = [java| new BatchWriters.IntArrayBatchWriter() |]+ reifyBatch =+ reifyArrayBatch (const reify) (fmap (fmap return) . VS.unsafeSlice)++ instance BatchReify (VS.Vector Int64) where+ newBatchWriter _ = [java| new BatchWriters.LongArrayBatchWriter() |]+ reifyBatch =+ reifyArrayBatch (const reify) (fmap (fmap return) . VS.unsafeSlice)++ instance BatchReify (VS.Vector Float) where+ newBatchWriter _ = [java| new BatchWriters.FloatArrayBatchWriter() |]+ reifyBatch =+ reifyArrayBatch (const reify) (fmap (fmap return) . VS.unsafeSlice)++ instance BatchReify (VS.Vector Double) where+ newBatchWriter _ = [java| new BatchWriters.DoubleArrayBatchWriter() |]+ reifyBatch =+ reifyArrayBatch (const reify) (fmap (fmap return) . VS.unsafeSlice)++ instance BatchReify Text.Text where+ newBatchWriter _ = [java| new BatchWriters.StringArrayBatchWriter() |]+ reifyBatch = reifyArrayBatch (const reify) $ \o n vs ->+ (VS.unsafeWith (VS.unsafeSlice o n vs) $ \ptr ->+ Text.fromPtr ptr (fromIntegral n)+ )++ instance BatchReflect BS.ByteString where+ newBatchReader _ = [java| new BatchReaders.ByteArrayBatchReader() |]+ reflectBatch = reflectArrayBatch reflect BS.length (return . BS.concat)++ instance BatchReflect (VS.Vector Word16) where+ newBatchReader _ = [java| new BatchReaders.CharArrayBatchReader() |]+ reflectBatch = reflectArrayBatch reflect VS.length (return . VS.concat)++ instance BatchReflect (VS.Vector Int16) where+ newBatchReader _ = [java| new BatchReaders.ShortArrayBatchReader() |]+ reflectBatch = reflectArrayBatch reflect VS.length (return . VS.concat)++ instance BatchReflect (VS.Vector Int32) where+ newBatchReader _ = [java| new BatchReaders.IntArrayBatchReader() |]+ reflectBatch = reflectArrayBatch reflect VS.length (return . VS.concat)++ instance BatchReflect (VS.Vector Int64) where+ newBatchReader _ = [java| new BatchReaders.LongArrayBatchReader() |]+ reflectBatch = reflectArrayBatch reflect VS.length (return . VS.concat)++ instance BatchReflect (VS.Vector Float) where+ newBatchReader _ = [java| new BatchReaders.FloatArrayBatchReader() |]+ reflectBatch = reflectArrayBatch reflect VS.length (return . VS.concat)++ instance BatchReflect (VS.Vector Double) where+ newBatchReader _ = [java| new BatchReaders.DoubleArrayBatchReader() |]+ reflectBatch = reflectArrayBatch reflect VS.length (return . VS.concat)++ instance BatchReflect Text.Text where+ newBatchReader _ = [java| new BatchReaders.StringArrayBatchReader() |]+ reflectBatch = reflectArrayBatch reflect Text.length $ \ts ->+ Text.useAsPtr (Text.concat ts) $ \ptr len ->+ (`VS.unsafeFromForeignPtr0` fromIntegral len)+ <$> newForeignPtr_ ptr+#endif++ instance Interpretation a => Interpretation (V.Vector a) where+ type Interp (V.Vector a) = 'Array (Interp a)++ -- TODO: Fix GHC so it doesn't complain that variables used exclusively in+ -- quasiquotes are unused. Thus we can stop prepending variable names with+ -- '_'.++ instance (Interpretation a, BatchReify a)+ => Reify (V.Vector a) where+ reify jv = do+ _batcher <- unsafeUngeneric <$> newBatchWriter (Proxy :: Proxy a)+ n <- getArrayLength jv+ let _jvo = arrayUpcast jv+ batch <- [java| {+ $_batcher.start($n);+ for(int i=0;i<$_jvo.length;i++)+ $_batcher.set(i, $_jvo[i]);+ return $_batcher.getBatch();+ } |]+ reifyBatch (fromObject batch) n+ where+ fromObject :: JObject -> J x+ fromObject = unsafeCast++ instance (Interpretation a, BatchReflect a)+ => Reflect (V.Vector a) where+ reflect v = do+ _batch <- upcast <$> reflectBatch v+ _batchReader <-+ unsafeUngeneric <$> newBatchReader (Proxy :: Proxy a)+ jv <- [java| {+ $_batchReader.setBatch($_batch);+ return $_batchReader.getSize();+ } |] >>= newArray :: IO (J ('Array (Interp a)))+ let _jvo = arrayUpcast jv+ () <- [java| {+ for(int i=0;i<$_jvo.length;i++)+ $_jvo[i] = $_batchReader.get(i);+ } |]+ return jv++ instance Batchable a => Batchable (V.Vector a) where+ type Batch (V.Vector a) = ArrayBatch (Batch a)++ instance (SingI (Interp a), SingI (Batch a), BatchReify a)+ => BatchReify (V.Vector a) where+ newBatchWriter _ = do+ _b <- unsafeUngeneric <$> newBatchWriter (Proxy :: Proxy a)+ generic <$> [java| new BatchWriters.ObjectArrayBatchWriter($_b) |]+ reifyBatch =+ reifyArrayBatch (flip reifyBatch) (fmap (fmap return) . V.unsafeSlice)++ instance (SingI (Interp a), SingI (Batch a), BatchReflect a)+ => BatchReflect (V.Vector a) where+ newBatchReader _ = do+ _b <- unsafeUngeneric <$> newBatchReader (Proxy :: Proxy a)+ generic <$> [java| new BatchReaders.ObjectArrayBatchReader($_b) |]+ reflectBatch =+ reflectArrayBatch reflectBatch V.length (return . V.concat)+ |]
+ src/main/java/io/tweag/jvm/batching/BatchReader.java view
@@ -0,0 +1,16 @@+package io.tweag.jvm.batching;++/**+ * A BatchReader<A, B> provides access to the elements of type B in a+ * batch of type A.+ */+public interface BatchReader<A, B> {+ /// Yields the batch size.+ public int getSize();+ /// Yields the object at position i of the batch.+ public B get(int i);+ /// Sets the batch read by get.+ public void setBatch(A o);+ /// Yields the class of the batched objects.+ public Class<B> getElemClass();+}
+ src/main/java/io/tweag/jvm/batching/BatchReaders.java view
@@ -0,0 +1,304 @@+package io.tweag.jvm.batching;++/**+ * Various BatchReaders+ * */+public class BatchReaders {++ public static final class BooleanBatchReader implements BatchReader<boolean[], Boolean> {+ private boolean[] arr = new boolean[0];+ public int getSize() { return arr.length; };+ public Boolean get(int i) { return arr[i]; };+ public void setBatch(boolean[] b) { arr = b; };+ public Class<Boolean> getElemClass() { return Boolean.class; };+ }++ public static final class ByteBatchReader implements BatchReader<byte[], Byte> {+ private byte[] arr = new byte[0];+ public int getSize() { return arr.length; };+ public Byte get(int i) { return arr[i]; };+ public void setBatch(byte[] b) { arr = b; };+ public Class<Byte> getElemClass() { return Byte.class; };+ }++ public static final class CharacterBatchReader implements BatchReader<char[], Character> {+ private char[] arr = new char[0];+ public int getSize() { return arr.length; };+ public Character get(int i) { return arr[i]; };+ public void setBatch(char[] b) { arr = b; };+ public Class<Character> getElemClass() { return Character.class; };+ }++ public static final class ShortBatchReader implements BatchReader<short[], Short> {+ private short[] arr = new short[0];+ public int getSize() { return arr.length; };+ public Short get(int i) { return arr[i]; };+ public void setBatch(short[] b) { arr = b; };+ public Class<Short> getElemClass() { return Short.class; };+ }++ public static final class IntegerBatchReader implements BatchReader<int[], Integer> {+ private int[] arr = new int[0];+ public int getSize() { return arr.length; };+ public Integer get(int i) { return arr[i]; };+ public void setBatch(int[] b) { arr = b; };+ public Class<Integer> getElemClass() { return Integer.class; };+ }++ public static final class LongBatchReader implements BatchReader<long[], Long> {+ private long[] arr = new long[0];+ public int getSize() { return arr.length; };+ public Long get(int i) { return arr[i]; };+ public void setBatch(long[] b) { arr = b; };+ public Class<Long> getElemClass() { return Long.class; };+ }++ public static final class FloatBatchReader implements BatchReader<float[], Float> {+ private float[] arr = new float[0];+ public int getSize() { return arr.length; };+ public Float get(int i) { return arr[i]; };+ public void setBatch(float[] b) { arr = b; };+ public Class<Float> getElemClass() { return Float.class; };+ }++ public static final class DoubleBatchReader implements BatchReader<double[], Double> {+ private double[] arr = new double[0];+ public int getSize() { return arr.length; };+ public Double get(int i) { return arr[i]; };+ public void setBatch(double[] b) { arr = b; };+ public Class<Double> getElemClass() { return Double.class; };+ }++ public static final class ObjectBatchReader implements BatchReader<Object[], Object> {+ private Object[] arr = new Object[0];+ public int getSize() { return arr.length; };+ public Object get(int i) { return arr[i]; };+ public void setBatch(Object[] b) { arr = b; };+ public Class<Object> getElemClass() { return Object.class; };+ }++ public static final class ByteArrayBatchReader+ implements BatchReader<Tuple2<byte[], int[]>, byte[]> {+ private byte[] arr;+ // end[i] tells the first position after the i-th array.+ private int[] end = new int[0];+ private int start;+ public void setBatch(Tuple2<byte[], int[]> t) {+ arr = t._1;+ end = t._2;+ start = 0;+ }+ public int getSize() { return end.length; }+ public byte[] get(int i) {+ final int len = end[i] - start;+ byte[] res = new byte[len];+ System.arraycopy(arr, start, res, 0, len);+ start = end[i];+ return res;+ }+ public Class<byte[]> getElemClass() { return byte[].class; };+ }++ public static final class BooleanArrayBatchReader+ implements BatchReader<Tuple2<boolean[], int[]>, boolean[]> {+ private boolean[] arr;+ // end[i] tells the first position after the i-th array.+ private int[] end = new int[0];+ private int start;+ public void setBatch(Tuple2<boolean[], int[]> t) {+ arr = t._1;+ end = t._2;+ start = 0;+ }+ public int getSize() { return end.length; }+ public boolean[] get(int i) {+ final int len = end[i] - start;+ boolean[] res = new boolean[len];+ System.arraycopy(arr, start, res, 0, len);+ start = end[i];+ return res;+ }+ public Class<boolean[]> getElemClass() { return boolean[].class; };+ }++ public static final class CharArrayBatchReader+ implements BatchReader<Tuple2<char[], int[]>, char[]> {+ private char[] arr;+ // end[i] tells the first position after the i-th array.+ private int[] end = new int[0];+ private int start;+ public void setBatch(Tuple2<char[], int[]> t) {+ arr = t._1;+ end = t._2;+ start = 0;+ }+ public int getSize() { return end.length; }+ public char[] get(int i) {+ final int len = end[i] - start;+ char[] res = new char[len];+ System.arraycopy(arr, start, res, 0, len);+ start = end[i];+ return res;+ }+ public Class<char[]> getElemClass() { return char[].class; };+ }++ public static final class ShortArrayBatchReader+ implements BatchReader<Tuple2<short[], int[]>, short[]> {+ private short[] arr;+ // end[i] tells the first position after the i-th array.+ private int[] end = new int[0];+ private int start;+ public void setBatch(Tuple2<short[], int[]> t) {+ arr = t._1;+ end = t._2;+ start = 0;+ }+ public int getSize() { return end.length; }+ public short[] get(int i) {+ final int len = end[i] - start;+ short[] res = new short[len];+ System.arraycopy(arr, start, res, 0, len);+ start = end[i];+ return res;+ }+ public Class<short[]> getElemClass() { return short[].class; };+ }++ public static final class IntArrayBatchReader+ implements BatchReader<Tuple2<int[], int[]>, int[]> {+ private int[] arr;+ // end[i] tells the first position after the i-th array.+ private int[] end = new int[0];+ private int start;+ public void setBatch(Tuple2<int[], int[]> t) {+ arr = t._1;+ end = t._2;+ start = 0;+ }+ public int getSize() { return end.length; }+ public int[] get(int i) {+ final int len = end[i] - start;+ int[] res = new int[len];+ System.arraycopy(arr, start, res, 0, len);+ start = end[i];+ return res;+ }+ public Class<int[]> getElemClass() { return int[].class; };+ }++ public static final class LongArrayBatchReader+ implements BatchReader<Tuple2<long[], int[]>, long[]> {+ private long[] arr;+ // end[i] tells the first position after the i-th array.+ private int[] end = new int[0];+ private int start;+ public void setBatch(Tuple2<long[], int[]> t) {+ arr = t._1;+ end = t._2;+ start = 0;+ }+ public int getSize() { return end.length; }+ public long[] get(int i) {+ final int len = end[i] - start;+ long[] res = new long[len];+ System.arraycopy(arr, start, res, 0, len);+ start = end[i];+ return res;+ }+ public Class<long[]> getElemClass() { return long[].class; };+ }++ public static final class FloatArrayBatchReader+ implements BatchReader<Tuple2<float[], int[]>, float[]> {+ private float[] arr;+ // end[i] tells the first position after the i-th array.+ private int[] end = new int[0];+ private int start;+ public void setBatch(Tuple2<float[], int[]> t) {+ arr = t._1;+ end = t._2;+ start = 0;+ }+ public int getSize() { return end.length; }+ public float[] get(int i) {+ final int len = end[i] - start;+ float[] res = new float[len];+ System.arraycopy(arr, start, res, 0, len);+ start = end[i];+ return res;+ }+ public Class<float[]> getElemClass() { return float[].class; };+ }++ public static final class DoubleArrayBatchReader+ implements BatchReader<Tuple2<double[], int[]>, double[]> {+ private double[] arr;+ // end[i] tells the first position after the i-th array.+ private int[] end = new int[0];+ private int start;+ public void setBatch(Tuple2<double[], int[]> t) {+ arr = t._1;+ end = t._2;+ start = 0;+ }+ public int getSize() { return end.length; }+ public double[] get(int i) {+ final int len = end[i] - start;+ double[] res = new double[len];+ System.arraycopy(arr, start, res, 0, len);+ start = end[i];+ return res;+ }+ public Class<double[]> getElemClass() { return double[].class; };+ }++ public static final class ObjectArrayBatchReader<A, T>+ implements BatchReader<Tuple2<A, int[]>, T[]> {+ // end[i] tells the first position after the i-th array.+ private int[] end = new int[0];+ private int start;+ private final BatchReader<A, T> br;+ public ObjectArrayBatchReader(BatchReader<A, T> br) {+ this.br = br;+ }+ public void setBatch(Tuple2<A, int[]> t) {+ br.setBatch(t._1);+ end = t._2;+ start = 0;+ }+ public int getSize() { return end.length; }+ public T[] get(int i) {+ final int len = end[i] - start;+ T[] res = (T[]) java.lang.reflect.Array.newInstance(br.getElemClass(), len);+ for(int j=0;j<len;j++)+ res[j] = br.get(start + j);+ start = end[i];+ return res;+ }+ public Class<T[]> getElemClass() {+ return (Class<T[]>) java.lang.reflect.Array.newInstance(br.getElemClass(), 0).getClass();+ }+ }++ public static final class StringArrayBatchReader+ implements BatchReader<Tuple2<char[], int[]>, String> {+ private char[] arr;+ // end[i] tells the first position after the i-th array.+ private int[] end = new int[0];+ private int start;+ public void setBatch(Tuple2<char[], int[]> t) {+ arr = t._1;+ end = t._2;+ start = 0;+ }+ public int getSize() { return end.length; }+ public String get(int i) {+ final int len = end[i] - start;+ String res = new String(arr, start, len);+ start = end[i];+ return res;+ }+ public Class<String> getElemClass() { return String.class; };+ }+}
+ src/main/java/io/tweag/jvm/batching/BatchWriter.java view
@@ -0,0 +1,14 @@+package io.tweag.jvm.batching;++/**+ * A BatchWriter<A,B> is an object that takes elements of type A one at+ * a time and yields a batch of type B containing all of them.+ */+public interface BatchWriter<A, B> {+ /// Starts a new batch of the given size+ public void start(int batchSize);+ /// Sets the object at position i of the batch.+ public void set(int i, A o);+ /// Yields the batch.+ public B getBatch();+}
+ src/main/java/io/tweag/jvm/batching/BatchWriters.java view
@@ -0,0 +1,409 @@+package io.tweag.jvm.batching;++/**+ * Various batchers+ *+ * */+public final class BatchWriters {++ public static final class BooleanBatchWriter implements BatchWriter<Boolean, boolean[]> {+ private boolean[] arr;+ public void start(int batchSize) { arr = new boolean[batchSize]; };+ public void set(int i, Boolean o) { arr[i] = o; };+ public boolean[] getBatch() { return arr; };+ }++ public static final class CharacterBatchWriter implements BatchWriter<Character, char[]> {+ private char[] arr;+ public void start(int batchSize) { arr = new char[batchSize]; };+ public void set(int i, Character o) { arr[i] = o; };+ public char[] getBatch() { return arr; };+ }++ public static final class ByteBatchWriter implements BatchWriter<Byte, byte[]> {+ private byte[] arr;+ public void start(int batchSize) { arr = new byte[batchSize]; };+ public void set(int i, Byte o) { arr[i] = o; };+ public byte[] getBatch() { return arr; };+ }++ public static final class ShortBatchWriter implements BatchWriter<Short, short[]> {+ private short[] arr;+ public void start(int batchSize) { arr = new short[batchSize]; };+ public void set(int i, Short o) { arr[i] = o; };+ public short[] getBatch() { return arr; };+ }++ public static final class IntegerBatchWriter implements BatchWriter<Integer, int[]> {+ private int[] arr;+ public void start(int batchSize) { arr = new int[batchSize]; };+ public void set(int i, Integer o) { arr[i] = o; };+ public int[] getBatch() { return arr; };+ }++ public static final class LongBatchWriter implements BatchWriter<Long, long[]> {+ private long[] arr;+ public void start(int batchSize) { arr = new long[batchSize]; };+ public void set(int i, Long o) { arr[i] = o; };+ public long[] getBatch() { return arr; };+ }++ public static final class FloatBatchWriter implements BatchWriter<Float, float[]> {+ private float[] arr;+ public void start(int batchSize) { arr = new float[batchSize]; };+ public void set(int i, Float o) { arr[i] = o; };+ public float[] getBatch() { return arr; };+ }++ public static final class DoubleBatchWriter implements BatchWriter<Double, double[]> {+ private double[] arr;+ public void start(int batchSize) { arr = new double[batchSize]; };+ public void set(int i, Double o) { arr[i] = o; };+ public double[] getBatch() { return arr; };+ }++ public static final class ObjectBatchWriter implements BatchWriter<Object, Object[]> {+ private Object[] arr;+ public void start(int batchSize) { arr = new Object[batchSize]; };+ public void set(int i, Object o) { arr[i] = o; };+ public Object[] getBatch() { return arr; };+ }++ public static final class ByteArrayBatchWriter+ implements BatchWriter<byte[], Tuple2<byte[], int[]> > {+ private byte[][] arrays;+ // end[i] tells the first position after the i-th array.+ private int[] end;+ // top tells the first position not occupied by arrays in the batch.+ private int top;+ public void start(int size) {+ end = new int[size];+ arrays = new byte[size][];+ top = 0;+ }+ public void set(int i, byte[] vec) {+ if (vec == null)+ throw new RuntimeException("null vectors are unsupported when reifying");+ arrays[i] = vec;+ top += vec.length;+ end[i] = top;+ }+ public Tuple2<byte[], int[]> getBatch() {+ byte[] batch = new byte[top];+ int pos = 0;+ for(int i=0;i<end.length && arrays[i]!=null;i++) {+ System.arraycopy(arrays[i], 0, batch, pos, arrays[i].length);+ pos = end[i];+ // Release the reference to the array so it can be reclaimed+ // before the loop is over.+ arrays[i] = null;+ }+ return new Tuple2<byte[], int[]>(batch, end);+ }+ }++ public static final class BooleanArrayBatchWriter+ implements BatchWriter<boolean[], Tuple2<boolean[], int[]> > {+ private boolean[][] arrays;+ // end[i] tells the first position after the i-th array.+ private int[] end;+ // top tells the first position not occupied by arrays in the batch.+ private int top;+ public void start(int size) {+ end = new int[size];+ arrays = new boolean[size][];+ top = 0;+ }+ public void set(int i, boolean[] vec) {+ if (vec == null)+ throw new RuntimeException("null vectors are unsupported when reifying");+ arrays[i] = vec;+ top += vec.length;+ end[i] = top;+ }+ public Tuple2<boolean[], int[]> getBatch() {+ boolean[] batch = new boolean[top];+ int pos = 0;+ for(int i=0;i<end.length && arrays[i]!=null;i++) {+ System.arraycopy(arrays[i], 0, batch, pos, arrays[i].length);+ pos = end[i];+ // Release the reference to the array so it can be reclaimed+ // before the loop is over.+ arrays[i] = null;+ }+ return new Tuple2<boolean[], int[]>(batch, end);+ }+ }++ public static final class CharArrayBatchWriter+ implements BatchWriter<char[], Tuple2<char[], int[]> > {+ private char[][] arrays;+ // end[i] tells the first position after the i-th array.+ private int[] end;+ // top tells the first position not occupied by arrays in the batch.+ private int top;+ public void start(int size) {+ end = new int[size];+ arrays = new char[size][];+ top = 0;+ }+ public void set(int i, char[] vec) {+ if (vec == null)+ throw new RuntimeException("null vectors are unsupported when reifying");+ arrays[i] = vec;+ top += vec.length;+ end[i] = top;+ }+ public Tuple2<char[], int[]> getBatch() {+ char[] batch = new char[top];+ int pos = 0;+ for(int i=0;i<end.length && arrays[i]!=null;i++) {+ System.arraycopy(arrays[i], 0, batch, pos, arrays[i].length);+ pos = end[i];+ // Release the reference to the array so it can be reclaimed+ // before the loop is over.+ arrays[i] = null;+ }+ return new Tuple2<char[], int[]>(batch, end);+ }+ }++ public static final class ShortArrayBatchWriter+ implements BatchWriter<short[], Tuple2<short[], int[]> > {+ private short[][] arrays;+ // end[i] tells the first position after the i-th array.+ private int[] end;+ // top tells the first position not occupied by arrays in the batch.+ private int top;+ public void start(int size) {+ end = new int[size];+ arrays = new short[size][];+ top = 0;+ }+ public void set(int i, short[] vec) {+ if (vec == null)+ throw new RuntimeException("null vectors are unsupported when reifying");+ arrays[i] = vec;+ top += vec.length;+ end[i] = top;+ }+ public Tuple2<short[], int[]> getBatch() {+ short[] batch = new short[top];+ int pos = 0;+ for(int i=0;i<end.length && arrays[i]!=null;i++) {+ System.arraycopy(arrays[i], 0, batch, pos, arrays[i].length);+ pos = end[i];+ // Release the reference to the array so it can be reclaimed+ // before the loop is over.+ arrays[i] = null;+ }+ return new Tuple2<short[], int[]>(batch, end);+ }+ }++ public static final class IntArrayBatchWriter+ implements BatchWriter<int[], Tuple2<int[], int[]> > {+ private int[][] arrays;+ // end[i] tells the first position after the i-th array.+ private int[] end;+ // top tells the first position not occupied by arrays in the batch.+ private int top;+ public void start(int size) {+ end = new int[size];+ arrays = new int[size][];+ top = 0;+ }+ public void set(int i, int[] vec) {+ if (vec == null)+ throw new RuntimeException("null vectors are unsupported when reifying");+ arrays[i] = vec;+ top += vec.length;+ end[i] = top;+ }+ public Tuple2<int[], int[]> getBatch() {+ int[] batch = new int[top];+ int pos = 0;+ for(int i=0;i<end.length && arrays[i]!=null;i++) {+ System.arraycopy(arrays[i], 0, batch, pos, arrays[i].length);+ pos = end[i];+ // Release the reference to the array so it can be reclaimed+ // before the loop is over.+ arrays[i] = null;+ }+ return new Tuple2<int[], int[]>(batch, end);+ }+ }++ public static final class LongArrayBatchWriter+ implements BatchWriter<long[], Tuple2<long[], int[]> > {+ private long[][] arrays;+ // end[i] tells the first position after the i-th array.+ private int[] end;+ // top tells the first position not occupied by arrays in the batch.+ private int top;+ public void start(int size) {+ end = new int[size];+ arrays = new long[size][];+ top = 0;+ }+ public void set(int i, long[] vec) {+ if (vec == null)+ throw new RuntimeException("null vectors are unsupported when reifying");+ arrays[i] = vec;+ top += vec.length;+ end[i] = top;+ }+ public Tuple2<long[], int[]> getBatch() {+ long[] batch = new long[top];+ int pos = 0;+ for(int i=0;i<end.length && arrays[i]!=null;i++) {+ System.arraycopy(arrays[i], 0, batch, pos, arrays[i].length);+ pos = end[i];+ // Release the reference to the array so it can be reclaimed+ // before the loop is over.+ arrays[i] = null;+ }+ return new Tuple2<long[], int[]>(batch, end);+ }+ }++ public static final class FloatArrayBatchWriter+ implements BatchWriter<float[], Tuple2<float[], int[]> > {+ private float[][] arrays;+ // end[i] tells the first position after the i-th array.+ private int[] end;+ // top tells the first position not occupied by arrays in the batch.+ private int top;+ public void start(int size) {+ end = new int[size];+ arrays = new float[size][];+ top = 0;+ }+ public void set(int i, float[] vec) {+ if (vec == null)+ throw new RuntimeException("null vectors are unsupported when reifying");+ arrays[i] = vec;+ top += vec.length;+ end[i] = top;+ }+ public Tuple2<float[], int[]> getBatch() {+ float[] batch = new float[top];+ int pos = 0;+ for(int i=0;i<end.length && arrays[i]!=null;i++) {+ System.arraycopy(arrays[i], 0, batch, pos, arrays[i].length);+ pos = end[i];+ // Release the reference to the array so it can be reclaimed+ // before the loop is over.+ arrays[i] = null;+ }+ return new Tuple2<float[], int[]>(batch, end);+ }+ }++ public static final class DoubleArrayBatchWriter+ implements BatchWriter<double[], Tuple2<double[], int[]> > {+ private double[][] arrays;+ // end[i] tells the first position after the i-th array.+ private int[] end;+ // top tells the first position not occupied by arrays in the batch.+ private int top;+ public void start(int size) {+ end = new int[size];+ arrays = new double[size][];+ top = 0;+ }+ public void set(int i, double[] vec) {+ if (vec == null)+ throw new RuntimeException("null vectors are unsupported when reifying");+ arrays[i] = vec;+ top += vec.length;+ end[i] = top;+ }+ public Tuple2<double[], int[]> getBatch() {+ double[] batch = new double[top];+ int pos = 0;+ for(int i=0;i<end.length && arrays[i]!=null;i++) {+ System.arraycopy(arrays[i], 0, batch, pos, arrays[i].length);+ pos = end[i];+ // Release the reference to the array so it can be reclaimed+ // before the loop is over.+ arrays[i] = null;+ }+ return new Tuple2<double[], int[]>(batch, end);+ }+ }++ public static final class ObjectArrayBatchWriter<T, B>+ implements BatchWriter<T[], Tuple2<B, int[]> > {+ private final BatchWriter<T, B> ob;+ private T[][] arrays;+ // end[i] tells the first position after the i-th array.+ private int[] end;+ // top tells the first position not occupied by arrays in the batch.+ private int top;++ public ObjectArrayBatchWriter(BatchWriter<T, B> ob) {+ this.ob = ob;+ }++ public void start(int size) {+ end = new int[size];+ arrays = (T[][]) new Object[size][];+ top = 0;+ }+ public void set(int i, T[] vec) {+ if (vec == null)+ throw new RuntimeException("null vectors are unsupported when reifying");+ arrays[i] = vec;+ top += vec.length;+ end[i] = top;+ }+ public Tuple2<B, int[]> getBatch() {+ ob.start(top);+ int k = 0;+ for(int i=0;i<end.length && arrays[i]!=null;i++) {+ for(int j=0;j<arrays[i].length;j++) {+ ob.set(k, arrays[i][j]);+ k++;+ }+ // Release the reference to the array so it can be reclaimed+ // before the loop is over.+ arrays[i] = null;+ }+ return new Tuple2<B, int[]>(ob.getBatch(), end);+ }+ }++ public static final class StringArrayBatchWriter+ implements BatchWriter<String, Tuple2<char[], int[]> > {+ private String[] arrays;+ // end[i] tells the first position after the i-th array.+ private int[] end;+ // top tells the first position not occupied by arrays in the batch.+ private int top;+ public void start(int size) {+ end = new int[size];+ arrays = new String[size];+ top = 0;+ }+ public void set(int i, String s) {+ if (s == null)+ throw new RuntimeException("null vectors are unsupported when reifying");+ arrays[i] = s;+ top += s.length();+ end[i] = top;+ }+ public Tuple2<char[], int[]> getBatch() {+ char[] batch = new char[top];+ int pos = 0;+ for(int i=0;i<end.length && arrays[i]!=null;i++) {+ arrays[i].getChars(0, arrays[i].length(), batch, pos);+ pos = end[i];+ // Release the reference to the array so it can be reclaimed+ // before the loop is over.+ arrays[i] = null;+ }+ return new Tuple2<char[], int[]>(batch, end);+ }+ }+}
+ src/main/java/io/tweag/jvm/batching/Tuple2.java view
@@ -0,0 +1,10 @@+package io.tweag.jvm.batching;++public class Tuple2<A, B> {+ public A _1;+ public B _2;+ public Tuple2(A _1, B _2) {+ this._1 = _1;+ this._2 = _2;+ }+}
+ src/test/haskell/Language/Java/BatchingSpec.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# OPTIONS_GHC -fplugin=Language.Java.Inline.Plugin #-}++module Language.Java.BatchingSpec where++import qualified Data.ByteString as BS+import Data.Int+import Data.Text (Text)+import qualified Data.Text as Text+import qualified Data.Vector as V+import qualified Data.Vector.Storable as VS+import Language.Java+import Language.Java.Batching ()+import Test.Hspec++spec :: Spec+spec = do+ let testrr :: (Eq a, Show a, Reify a, Reflect a) => a -> IO ()+ testrr x = (reflect x >>= reify) `shouldReturn` x+ describe "batching" $ do+ it "succeeds on empty vectors" $ do+ testrr (V.fromList [] :: V.Vector Text)+ it "succeeds on non-empty vectors" $ do+ testrr (V.fromList [1..10] :: V.Vector Int32)+ it "succeeds on vectors of vectors" $ do+ let xs :: V.Vector (V.Vector Int32)+ xs = V.fromList (map V.fromList [ [i .. i + 4] | i <- [1,6..26] ])+ testrr xs+ testrr (V.empty `asTypeOf` xs)+ testrr $ V.map (V.map (Text.pack . show)) xs+ testrr $ V.map (V.map (BS.singleton . fromIntegral)) xs+ testrr (V.map V.convert xs :: V.Vector (VS.Vector Int32))
+ src/test/haskell/Main.hs view
@@ -0,0 +1,10 @@+{-# LANGUAGE OverloadedStrings #-}+module Main where++import Language.Java (withJVM)+import qualified Spec+import Test.Hspec++main :: IO ()+main = withJVM ["-Djava.class.path=build/libs/jvm-batching.jar"] $+ hspec Spec.spec
+ src/test/haskell/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=Spec #-}