Created
December 15, 2012 22:59
-
-
Save danielhopkins/4300150 to your computer and use it in GitHub Desktop.
Exploration of nio java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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