Skip to content

Instantly share code, notes, and snippets.

@danielhopkins
Created December 15, 2012 22:59
Show Gist options
  • Save danielhopkins/4300150 to your computer and use it in GitHub Desktop.
Save danielhopkins/4300150 to your computer and use it in GitHub Desktop.
Exploration of nio java
import java.io._
import java.nio._
import java.nio.channels._
/*
Initially I wanted to explore the nio api for file access and I ended up coming across a SO article
saying that nio isn't faster than just plain 'ol FileOutputStream. Curious about this I came up with
a benchmark suite and quickly found that is indeed true. After a couple of iterations I was able to
beat the default, but clearly it's not easy or obvious.
My conclusion is first that FileOutputStream is a solid library and should always be your default.
Second, the nio api is really complex (much too much so). It was tricky to get working and even
when I did there is such a big range in performance that it seems unlikely someone would use the
correct "spell" when developing and end up with suboptimal performance.
Generally I'm not a fan of the "we have sane defaults" library design (you end up losing too much
customization and no one ends up happy) but in this case I think there is room for a middle ground.
Numbers:
direct buffered nio timed in: 2117ms
wrapped nio timed in: 5636ms
mapped nio timed in: 58ms
file output stream timed in: 205ms
I don't really care about the absolute numbers, so there isn't any averaging going on, please run
this again if you're curious.
*/
object NioIsNeat {
val FILE_NAME = "somefile.txt"
def main(args: Array[String]) = {
println("Building huge array")
implicit val testData = randCharArray
println("Warming")
nioDirectBuffered(testData)
nioWrapped(testData)
nioMapped(testData)
fileOutputStream(testData)
println("Benchmark")
timed("direct buffered nio")(nioDirectBuffered)
timed("wrapped nio")(nioWrapped)
timed("mapped nio")(nioMapped)
timed("file output stream")(fileOutputStream)
}
def fileOutputStream(data: Array[Byte]) {
val file = new FileOutputStream(new File(FILE_NAME))
file.write(data)
file.close()
}
def nioWrapped(data: Array[Byte]) {
val channel = new FileOutputStream(new File(FILE_NAME)).getChannel
val bb = ByteBuffer.wrap(randCharArray)
channel.write(bb)
channel.close()
}
def nioMapped(data: Array[Byte]){
val length = 0x8FFFFFF // 128 Mb
val fc = new RandomAccessFile(FILE_NAME, "rw").getChannel()
val out = fc.map(FileChannel.MapMode.READ_WRITE, 0, length)
out.put(data)
fc.close()
}
def nioDirectBuffered(data: Array[Byte]) {
val channel = new FileOutputStream(new File(FILE_NAME)).getChannel
val bb = ByteBuffer.allocateDirect(1024)
data.grouped(1024).foreach { arr =>
bb.clear()
bb.put(arr)
bb.flip()
channel.write(bb)
}
channel.close()
}
def timed(desc: String)(f: Array[Byte] => Unit)(implicit data: Array[Byte]) {
val start = System.currentTimeMillis
f(data)
val time = System.currentTimeMillis - start
println(desc + " timed in: " + time)
}
def randCharArray():Array[Byte] = {
val r = new util.Random()
val str = new collection.mutable.ArrayBuffer[Byte]()
(1 to 1000000).foreach{ _ =>
(1 to 80).foreach(_ => str.append(r.nextPrintableChar.toByte))
str.append('\n')
}
str.toArray
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment