I think you are right in this case - I updated my example code above to use bitwise and it is running in the same amount of time. (100,000 iterations in about 6 milliseconds)
To be clear I <3 clojure, but I'm taking the bait just for fun:
1) The chunk of Java code shows a complete program - the others show function definitions
2) The Java is written purposely to be as verbose as possible
3) The Java sends it's output to stdout, unlike the others
4) The Java code is needlessly written using generics
Things the comparison ignores:
1) The Java code is more toolable than the others
2) The Java code will outperform most of the other examples once it has been subject to Hotspot
Here's a more fair comparison, at the function level to level the playing field (and only one import needed: java.util.*):
List trollBait() {
return new ArrayList(){{
for (int i : new int[]{1, 2, 3, 4}) {
if (i % 2 == 0) add(i);
}}};
}
Here's why I absolutely do heart Java:
I just ran this 100,000 times in 6 milliseconds on a Core i7 920 (2.67 GHz). And I scanned it with Findbugs, and PMD, and a security vulnerability tool. And I refactored it in my IDE with crazy IntelliJ foo-jitsu. And it's more strongly typed. And it's runtime memory model is safer.
now we're talking, this is a really good response! I almost agree with all of your points, except for the performance, and only in thoroughness: a benchmark will be nice.
And for clarity: the other ones aren't function definitions, they also build a new list, like your example. The examples are meant to be run in a REPL and that's why they don't send to stdout, although it will be just another function application in most cases (but your example works in beanshell, the stand-alone java quasi-REPL, so we're ok). I think all of them can be considered a full-program in those languages: as a stand-alone chunk of code that can be compiled/interpreted and will produce an executable output, that is. And, yes, the autoboxing was the only reason I chose generics, but I tried for it to be concise, given my limited knowledge of java; the max-verbose mode would have used a Runnable or something evil to simulate a higher order function.
Thanks... my experience with clojure on the JVM is it is in general slower than optimally tuned Java code. I really can't imagine that any of the other languages you listed would easily outperform the code I have here, but maybe since the use-case is a sweet spot for functional? The person who responded to me with the F# example reported a crazy low time, and I am wondering if they have a defect in the code. I think it is possible a true functional language could make some tweaks that Java can't (like realizing the function is idempotent and caching the response instead of recalculating it). Maybe that is why the F# example was so quick - either the way it times itself is defective, or perhaps the runtime memoizes the function.
My experience with all the languages in your sampler is that their runtime performance usually lags the Java on the JVM for general purpose programming. This is not an argument not to use them - I would prefer many of them in certain use cases. But if I want a managed runtime, and have some performance critical code that I am OK with being written in an imperative language, it is hard to beat the performance of simple Java on the JVM. There has been so much tuning to Hotspot over the last decade. That's been my experience anyways.
Interesting, and yes, I am a witness of how fast proper java is; and in extreme performance cases, I guess I'd turn to java before C++ ; maybe the f# also benefits from optimizations (like tail-call optimization) and the underlying VM? (though I don't know how performant the CLR is)
That's not bad, how about 0.014 milliseconds for 100,000 iterations? On a Core i5 @ 2.6 Ghz
// Learn more about F# at http://fsharp.net
open System
let consume x = ()
let start = System.Environment.TickCount
Seq.init 100000 id
|> Seq.iter (fun x -> [0..4] |> List.filter (fun y -> (y % 2) = 0) |> consume )
let endTime = System.Environment.TickCount
printfn "%f milliseconds" (TimeSpan.FromTicks(int64(endTime - start)).TotalMilliseconds)
Output:
0.014100 milliseconds
Press any key to continue . . .
I'm sure if I added some java line noise I could get it up to 6 milliseconds. Too bad I didn't need a calendar class to figure out how to get from ticks to milliseconds. It's also really unfortunate that System.Environment.TickCount compiles down to a single assembly instruction. I was really hoping to use a factory pattern in there somewhere. Maybe some IOC containers. What good is a language if you can't write half your code in XML.
That is crazy fast... funny, but based on your response I am guessing you don't know why. What could possibly explain such a big difference? Understanding this is what separates a seasoned pro from someone throwing out trollbait comments about XML and assembly instructions.
First off, the resolution of TickCount is not less than 500 milliseconds. Are you sure you are actually timing it right? Maybe you should have used DateTime.Ticks which has a higher resolution. (seriously - I think you need to check your code)
But in any case, let's say your test is actually valid. You mentioned "Java line noise" - not really a real explanation for the difference.
So let me guess for you - maybe your F# runtime knows the function is pure and idempotent, and after running it once it probably just cached the output. So your run doesn't actually execute the filter across the list for every iteration, whereas my Java code did.
I could rewrite my Java code to return the same array over and over after it has been computed once, but it sure won't look as clean as a functional language like F#. I grant you that, and that's one of the reasons I prefer functional languages to Java.
Next time maybe put a little more thought into the response if you actually really have no idea why your program said "0.014100 milliseconds".
TickCount has 1 millisecond accuray and it's actually milliseconds, which is what tipped me off to the low numbers. It actually takes 140 milliseconds to execute. I redid the code to not use Seqs and now it's down to 15 ms.
// Learn more about F# at http://fsharp.net
open System
let consume x = ()
let start = System.Environment.TickCount
let arr = [| 1..4 |]
for i = 1 to 100000 do
arr
|> Array.filter (fun y -> (y % 2) = 0)
|> consume
let endTime = System.Environment.TickCount
printf "%d milliseconds" (endTime - start)
You would still be able to run the Open JDK. There is a port for the Mac, but if I am not mistaken it uses the X Server and is not as well integrated with the graphics layer of OS X desktop. Example: http://landonf.bikemonkey.org/static/soylatte/
I had the same thought - also note that the software powering the Squeezebox server AND the devices is open source! In fact they just flipped the player software (written in LUA) over to BSD, here's the commit:
This sounds really interesting. Is there any chance that you'll give it closer to the middle of the country? I'm in Milwaukee, but Chicago's just a hop skip and a jump away.