on my machine, following changes makes good improvement of performance. almost 2 times faster
1 2 3 4 5 6 7 8 9 10
let stringToCharArray ( str : string ) = str.ToCharArray () let explode = stringToCharArray let implode ( cl : char [] ) = let buf = new System.Text.StringBuilder () let _ = buf.Append ( cl ) buf.ToString () let rev s = implode ( Array.rev ( explode ( s ) ) )
Hi deex,
I'll take your word for that, but you're now comparing
apples and oranges, because the SML code uses lists of
characters, not arrays. The signatures of explode and
implode in SML are:
string -> char list
char list -> string
whereas the signatures of your respective
implementations are:
string -> char []
char [] -> string.
If you also do array-based string reversal is SML,
of course you'll speed up that code too. Again,
this is not about how to reverse strings efficiently.
In fact you compare specialized version of `implode` & `explode` from SML with your own realization on F#. So for `implode` you have `String.ToCharArray >> Array.toList` dataflow and for `explode` you have `List.toArray >> StringBuilder.Append >> StringBuilder.ToString`. In total you have 3 temporary and unnecessary structures and big overhead memory and speed. Slow F# version is not big surprise in that case. Try to do array-based string reversal is SML and measure it. In that case it will be fair comparision.
Yes, another way to look at it is that although string reversal isn't the stated goal, if replacing one implementation with another that is three times faster makes the whole program run about twice as fast (as the OP as stated) then that means that about 2/3 of the program time is spent in string reversal, so indeed we have been basically profiling string reversal. If string reversal is just a notional palceholder for some other operation, the why not just define
1
let rev = id
and be done with it? (Unless the code actually uses of string reversal to get some read-world job done, of course.)
Hi anotheraccount,
You must have read my mind. I was just about to get rid
of this whole string reversal business because it was
really proving distracting. So I did go ahead and
defined rev to be the identity function, both in
the F# code and in the SML code. Since the initial
benchmark now completes much faster, I upped the
number of iterations from 100,000 to 500,000.
Respective results:
F#: 15.9 seconds
SML-NJ: 7.6 seconds
MLton: 2.3 seconds
Pretty much the same situation we had initially.
Btw, I was intrigued by your earlier suggestion
that records may incur an overhead due to .Net
class-related stuff. I might try making the
datatype constructors take simple tuples
as arguments, instead of records, just to see
if that makes a difference. [Of course, if
DUs themselves incur a significant overhead,
record arguments or not, then it's a lost cause.
I don't see how you can have efficient functional
programming without efficient DU's.]
Having F# be twice as slow (assuming you don't get better) isn't necessarily bad at all, if you balance this out with the fact the code is now part of the ".Net ecosystem". F# objects can be used from any other .Net language. If that doesn't bring you any advantages re: interoperability, customers, etc., and a stand-alone processor is fine for you, then maybe your code is good as-is - which is always nice.
If a real-world task takes 120ms instead of 60ms, then the user probably won't even notice. If it takes six hours instead of three - you leave work early either way. If it takes two minutes instead of one, that could be annoying - funny how that works :)
But F# is a very efficient language in most respects.
Not sure how the overhead of tuple creation and unpacking compares to your use of records.
However, it could be interesting to have an attribute to control the representation of records and DU's to use more efficient schemes if possible, such as structs and classes with mutable public fields for recs, and tag/union storage rather than classes for DU's.
I see what you're saying, but I'm not so sure this
applies in my case. As I said initially, this is
a benchmark I threw together quickly in an attempt
to replicate the problem in my own code. In my own
code, the F# version is about 4-5 times slower than
SML-NJ, at best (some parts slow down even more).
Also, keep in mind that anything written in SML-NJ
can be compiled by MLton for free; SML-NJ is used for
development and MLton cranks out the final executable
at the end. Given that the MLton is anywhere from 2-4
to 10+ times faster than SML-NJ (depending on the code),
this means that the F# version would be anywhere from
10 to 40-50+ times slower than what I would get with SML.
That's too big a hit for any serious interpreter to take.
I'd love to continue the F# project, but unless I have
some revelation soon about what's causing the poor
performance on AST manipulation, I'll have to either
switch to OCaml or stick with SML. [ocamlopt, btw, according
to my tests so far, is (very) marginally better than
SML-NJ, but pretty significantly worse than MLton.]
Which .NET version are you using?
If you are not yet using .NET 4, you might benefit from an upgrade. See [link:flyingfrogblog.blogspot.com]
The string reversal not being the thing notwidthstanding, I cannot reproduce your results the code I provided was slower than your original code. (It really can't be, given all it's doing.) The code below shows it to be about 3x faster and produces ~15% of the garbage. (And initalizing the stringBuffer with the size of the string speeds things up a little more to ~700ms. for one million reps.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
let stringToCharArray(str:string) = str.ToCharArray() let stringToCharList(str:string) = Array.toList(stringToCharArray(str)) let explode = stringToCharList let implode(cl) = let buf = new System.Text.StringBuilder() let _ = buf.Append(List.toArray(cl)) buf.ToString() let rev1(s) = implode(List.rev(explode(s))) let rev2 (str:string) = let sb = new System.Text.StringBuilder() for i in str.Length-1 .. -1 .. 0 do ignore <| sb.Append( str.[ i]) sb.ToString() let repeat n f = for i in 1 .. n do ignore (f()) let testStr = "A" + (new string('-',100)) + "Z" > repeat 1000000 (fun() -> rev1 testStr);; Real: 00:00:02.528, CPU: 00:00:02.464, GC gen0: 1001, gen1: 1, gen2: 0 > repeat 1000000 (fun() -> rev2 testStr);; Real: 00:00:00.851, CPU: 00:00:00.842, GC gen0: 152, gen1: 1, gen2: 0
I mention this because if you're seeing this make your code take "three times as long", then something else may be amiss in the timings.
Reply to anotheraccount:
Thanks for your latest email. I think there may be a bit
of a misunderstanding here. If you notice my previous reply
to your original post, you'll see that it only refers to
your suggested definitions of implode and explode, *not*
to your ground-up definition of rev in terms of StringBuilder.
So the triple times were in reference to an implementation
that used your implode and explode but kept the same
definition of string reversal:
implode(List.rev(explode(s))).
This was done in order to keep the comparison with SML
fair, since that's how reversal is done in SML, by
imploding the reverse of the string's explosion.
When I use your ground-up reversal implementation
in terms of StringBuilder, I do get a drastic
speed-up, about twice as fast, if I recall correctly
from last night. But of course that's not particularly
relevant to the situation: Given how reversal is done
in SML, the same reversal algorithm must also be
used in F#: implode - List.rev - explode. It's
fine for the purposes of this comparison to give
better F# definitions of implode and explode,
but not so for string reversal. So there's nothing
amiss in the timings.
I hope this clears things up a bit - thanks.
Got it, thanks.
If the goal is to get your code running to your satisfaction in F#, then ISTM you should use ideomatic/efficient code in F# where possible -- effecient constructs are always different between languages.
The style of clearest and fastest F# port may not be the best for your other languages either.
Because common operations like string reversal is done this way in language B, I'm sure they make sure these operations are very quick - but .Net provides a plethora of native string operations so one is expected to go that route.
If we remove issues like string reversal and perhaps tail-calls (by using higher-order fn's to traverse your AST instead of recursion if possible, which may also look clearer) we may be able to see a 1:1 comparison of the actual AST xform.
DU's and Records do come with overhead of full .Net classes with properties (getter and setter fn's) rather than direct field accessors, so there may be some perf issues compared to some other languages - but I think you should be able to get the perf to where you want it. But if you're wanting an auto-magic port of the code maybe with some search-and-replace to get things going, maybe not.
I can't speak to the rest at a glance, but this certainly is the scariest string reversal I've ever seen :) - though I get where it's coming from:
1 2 3 4 5 6 7 8
let stringToCharArray(str:string) = str.ToCharArray() let stringToCharList(str:string) = Array.toList(stringToCharArray(str)) let explode = stringToCharList let implode(cl) = let buf = new System.Text.StringBuilder() let _ = buf.Append(List.toArray(cl)) buf.ToString() let rev(s) = implode(List.rev(explode(s)))
More ideomatic versions of above could be:
1 2
let implode str = str |> List.fold (fun a b -> a + string b) "" let explode str = str |> Seq.toList
However, if you're going to break out StringBuffer anyway (and you're much better off than creating lists), try replacing all of the above with:
1 2 3 4 5 6
let rev (str:string) = let sb = new System.Text.StringBuilder() for i in str.Length-1 .. -1 .. 0 do ignore <| sb.Append( str.[ i]) sb.ToString()
That may or may not address the AST xform as a whole, but it couldn't hurt.
Thanks for the feedback.
Unfortunately, your suggested definitions of
implode and explode fare much worse - F#
takes 3 times as long with them (about 62
seconds on my laptop).
At any rate, the point is not to come up with
a better definition of string reversal. Obviously
there are more efficient implementations. The
point is to compare MLton and SML-NJ with F#
on how they handle recursing down the AST.
What they do on the AST nodes is completely
immaterial as long as it's the same in both
cases. And the SML code does use the same
definition of string reversal:
fun rev(s) = implode(List.rev(explode(s)))
Now, perhaps MLton and SML-NJ have more efficient
implementations of implode and explode than what
I've given here for F#, but I doubt that that's the gist
of the problem. For instance, here's a more efficient
implementation of explode in F#:
let explode(str:string) =
let rec makeList(i,res) =
if i < 0 then res else makeList(i-1,(str.[i])::res)
makeList(str.Length - 1,[])
Using this version makes little difference, shaving off only
about 1 second of running time: I got 19.2 seconds vs. the
original 20.5 seconds. Perhaps a more efficient version
of implode might shave off another second (or even two).
But that's still a very long way from the 10.4 of SML-NJ,
let alone the 3.7 of MLton. So there's something else
going on here.
32bit tail calls are much faster than 64bit ones. Are you running this on a 64bit machine?
64bit architectures have a different calling convention which requires complex stack manipulation.
On my machine a simple change to implode shaves about 15% off of the running time:
1
let implode(cl) = System.String(Array.ofList cl)
Off hand, I'm not sure what is causing the remainder of the difference in performance with SML-NJ (I don't think it's fair to compare F# against MLton, which is a whole program compiler). As always when characterizing performance, using a good profiler should give you an idea of where to start.
On another note, if you're using fsi, use the #time;;
directive rather than your timing function (and you should probably be using the System.Diagnostics.StopWatch class anyway rather than a DateTime value, although for operations which are this slow it won't make any difference).
Yes, as I said, the string reversal was not meant to be more than a micro-optimization; the fact that it tuned out not to be even that is vexing. I find it hard to believe that the imperative StringBuffer code is less efficient than consing and joining and converting the char list - I'll have to run that test myself... (It could be that while it looks bad, they are all natively implemented and fare better, though they'd still create more garbage.)
Please keep this thread up to date w/your findings...
Topic tags
- f# × 3705
- websharper × 1897
- compiler × 286
- functional × 201
- ui next × 139
- c# × 121
- classes × 97
- web × 97
- .net × 84
- book × 84
- async × 76
- ui.next × 67
- bug × 54
- core × 49
- website × 49
- server × 45
- parallel × 43
- ui × 43
- enhancement × 41
- parsing × 41
- testing × 41
- trywebsharper × 41
- typescript × 37
- html × 35
- javascript × 35
- owin × 35
- asynchronous × 30
- monad × 28
- ocaml × 28
- tutorial × 27
- warp × 27
- haskell × 26
- sitelet × 25
- linq × 22
- workflows × 22
- wpf × 20
- fpish × 19
- introduction × 19
- silverlight × 19
- sitelets × 19
- monodevelop × 17
- rpc × 17
- suave × 17
- piglets × 16
- collections × 15
- feature request × 15
- jquery × 15
- templates × 15
- getting started × 14
- pipeline × 14
- kendoui × 13
- reactive × 12
- 4.1.0.171 × 11
- monads × 11
- opinion × 10
- 4.0.190.100-rc × 9
- deployment × 9
- fixed × 9
- formlets × 9
- in × 9
- json × 9
- plugin × 9
- proposal × 9
- scheme × 9
- solid × 9
- basics × 8
- concurrent × 8
- highcharts × 8
- how-to × 8
- python × 8
- 4.1.1.175 × 7
- complexity × 7
- documentation × 7
- visual studio × 7
- 4.1.2.178 × 6
- lisp × 6
- real-world × 6
- released in 4.0.192.103-rc × 6
- remoting × 6
- resources × 6
- scala × 6
- websharper ui.next × 6
- workshop × 6
- xaml × 6
- 4.0.193.110 × 5
- 4.2.3.236 × 5
- aspnetmvc × 5
- authentication × 5
- azure × 5
- bootstrap × 5
- conference × 5
- dsl × 5
- formlet × 5
- java × 5
- list × 5
- metaprogramming × 5
- ml × 5
- released in Zafir.4.0.188.91-beta10 × 5
- sql × 5
- visualstudio × 5
- websharper.forms × 5
- zafir × 5
- 4.0.192.106 × 4
- 4.0.195.127 × 4
- 4.1.0.38 × 4
- 4.2.1.86 × 4
- 4.2.6.118 × 4
- css × 4
- example × 4
- extensions × 4
- fsi × 4
- fsx × 4
- html5 × 4
- jqueryui × 4
- lift × 4
- reflection × 4
- remote × 4
- rest × 4
- spa × 4
- teaching × 4
- template × 4
- websocket × 4
- wontfix × 4
- 4.0.196.147 × 3
- 4.1.0.34 × 3
- 4.1.6.207 × 3
- 4.2.1.223-beta × 3
- 4.2.11.258 × 3
- 4.2.4.114 × 3
- 4.2.4.247 × 3
- 4.2.5.115 × 3
- 4.2.6.253 × 3
- 4.2.9.256 × 3
- ajax × 3
- alt.net × 3
- aml × 3
- asp.net mvc × 3
- canvas × 3
- cloudsharper × 3
- compilation × 3
- database × 3
- erlang × 3
- events × 3
- extension × 3
- file upload × 3
- forums × 3
- inline × 3
- issue × 3
- kendo × 3
- macro × 3
- mono × 3
- msbuild × 3
- mvc × 3
- pattern × 3
- piglet × 3
- released in Zafir.4.0.187.90-beta10 × 3
- svg × 3
- type provider × 3
- view × 3
- 4.1.1.64 × 2
- 4.1.5.203 × 2
- 4.1.7.232 × 2
- 4.2.10.257 × 2
- 4.2.3.111 × 2
- 4.2.5.249 × 2
- android × 2
- asp.net × 2
- beginner × 2
- blog × 2
- chart × 2
- client × 2
- client server app × 2
- clojure × 2
- computation expressions × 2
- constructor × 2
- corporate × 2
- courses × 2
- cufp × 2
- d3 × 2
- debugging × 2
- direct × 2
- discriminated union × 2
- docs × 2
- elm × 2
- endpoint × 2
- endpoints × 2
- enterprise × 2
- entity framework × 2
- event × 2
- f# interactive × 2
- fable × 2
- flowlet × 2
- formdata × 2
- forms × 2
- fsc × 2
- google maps × 2
- hosting × 2
- http × 2
- https × 2
- iis 8.0 × 2
- install × 2
- interactive × 2
- interface × 2
- iphone × 2
- iteratee × 2
- jobs × 2
- jquery mobile × 2
- keynote × 2
- lens × 2
- lenses × 2
- linux × 2
- listmodel × 2
- mac × 2
- numeric × 2
- oauth × 2
- obfuscation × 2
- offline × 2
- oop × 2
- osx × 2
- packaging × 2
- pattern matching × 2
- performance × 2
- pipelines × 2
- q&a × 2
- quotation × 2
- reference × 2
- released in Zafir.4.0.185.88-beta10 × 2
- rx × 2
- script × 2
- security × 2
- self host × 2
- seq × 2
- sockets × 2
- stm × 2
- tcp × 2
- trie × 2
- tutorials × 2
- type × 2
- url × 2
- var × 2
- websharper.charting × 2
- websharper4 × 2
- websockets × 2
- wig × 2
- xna × 2
- zh × 2
- .net interop × 1
- 2012 × 1
- 4.0.194.126 × 1
- 4.1.3.184 × 1
- 4.1.4.189 × 1
- 4.2.0.214-beta × 1
- 4.2.12.259 × 1
- 4.2.2.231-beta × 1
- 4.2.8.255 × 1
- Canvas Sample Example × 1
- DynamicStyle Animated Style × 1
- Fixed in 4.0.190.100-rc × 1
- Released in Zafir.UI.Next.4.0.169.79-beta10 × 1
- SvgDynamicAttribute × 1
- WebComponent × 1
- abstract class × 1
- accumulator × 1
- active pattern × 1
- actor × 1
- addin × 1
- agents × 1
- aggregation × 1
- agile × 1
- alter session × 1
- animation × 1
- anonymous object × 1
- apache × 1
- api × 1
- appcelerator × 1
- architecture × 1
- array × 1
- arrays × 1
- asp.net 4.5 × 1
- asp.net core × 1
- asp.net integration × 1
- asp.net mvc 4 × 1
- asp.net web api × 1
- aspnet × 1
- ast × 1
- attributes × 1
- authorization × 1
- b-tree × 1
- back button × 1
- badimageformatexception × 1
- bash script × 1
- batching × 1
- binding-vars × 1
- bistro × 1
- body × 1
- bundle × 1
- camtasia studio × 1
- cas protocol × 1
- charts × 1
- clarity × 1
- class × 1
- cli × 1
- clipboard × 1
- clojurescript × 1
- closures × 1
- cloud × 1
- cms × 1
- coding diacritics × 1
- color highlighting × 1
- color zones × 1
- combinator × 1
- combinators × 1
- compile × 1
- compile code on server × 1
- config × 1
- confirm × 1
- content × 1
- context × 1
- context.usersession × 1
- continuation-passing style × 1
- coords × 1
- cordova × 1
- cors × 1
- coursera × 1
- cross-domain × 1
- csla × 1
- current_schema × 1
- custom content × 1
- data × 1
- data grid × 1
- datetime × 1
- debug × 1
- declarative × 1
- delete × 1
- devexpress × 1
- dhtmlx × 1
- dictionary × 1
- directattribute × 1
- disqus × 1
- distance × 1
- do binding × 1
- doc elt ui.next upgrade × 1
- docker × 1
- dojo × 1
- dol × 1
- dom × 1
- domain × 1
- du × 1
- duf-101 × 1
- dynamic × 1
- eastern language × 1
- eclipse × 1
- edsl × 1
- em algorithm × 1
- emacs × 1
- emotion × 1
- enums × 1
- error × 1
- etw × 1
- euclidean × 1
- eventhandlerlist × 1
- examples × 1
- ext js × 1
- extension methods × 1
- extra × 1
- facet pattern × 1
- failed to translate × 1
- fake × 1
- fantomas × 1
- fear × 1
- float × 1
- form × 1
- form-data × 1
- forum × 1
- fp × 1
- frank × 1
- fsdoc × 1
- fsharp × 1
- fsharp.core × 1
- fsharp.powerpack × 1
- fsharpx × 1
- fsunit × 1
- function × 1
- functional style × 1
- game × 1
- games × 1
- gc × 1
- generic × 1
- geometry × 1
- getlastwin32error × 1
- getting-started × 1
- google × 1
- google.maps × 1
- grid × 1
- group × 1
- guide × 1
- hash × 1
- headers × 1
- hello world example × 1
- heroku × 1
- highchart × 1
- history × 1
- how to × 1
- html-templating × 1
- http405 × 1
- httpcontext × 1
- hubfs × 1
- i18n × 1
- ie 8 × 1
- if-doc × 1
- iis × 1
- image × 1
- images × 1
- inheritance × 1
- initialize × 1
- input × 1
- install "visual studio" × 1
- installer × 1
- int64 × 1
- interfaces × 1
- internet explorer × 1
- interop × 1
- interpreter × 1
- io × 1
- iobservable × 1
- ios × 1
- iot × 1
- ipad × 1
- isomorphic × 1
- javascript optimization × 1
- javascript semanticui resources × 1
- jquery-plugin × 1
- jquery-ui × 1
- jquery-ui-datepicker × 1
- js × 1
- kendo datasource × 1
- kendochart × 1
- kendoui compiler × 1
- knockout × 1
- l10n × 1
- learning × 1
- library × 1
- libs × 1
- license × 1
- licensing × 1
- lineserieszonescfg × 1
- local setting × 1
- localization × 1
- logging × 1
- loop × 1
- macros × 1
- mailboxprocessor × 1
- mapping × 1
- maps × 1
- markerclusterer × 1
- markup × 1
- marshal × 1
- math × 1
- mathjax × 1
- message × 1
- message passing × 1
- message-passing × 1
- meta × 1
- metro style × 1
- micro orm × 1
- minimum-requirements × 1
- mix × 1
- mobile installation × 1
- mod_mono × 1
- modal × 1
- module × 1
- mouseevent × 1
- mouseposition × 1
- multidimensional × 1
- multiline × 1
- multithreading × 1
- mysql × 1
- mysqlclient × 1
- nancy × 1
- native × 1
- nested × 1
- nested loops × 1
- node × 1
- nunit × 1
- object relation mapper × 1
- object-oriented × 1
- om × 1
- onboarding × 1
- onclick × 1
- optimization × 1
- option × 1
- orm × 1
- os x × 1
- output-path × 1
- override × 1
- paper × 1
- parameter × 1
- persistence × 1
- persistent data structure × 1
- phonegap × 1
- pola × 1
- post × 1
- powerpack × 1
- prefix tree × 1
- principle of least authority × 1
- privacy × 1
- private × 1
- profile × 1
- programming × 1
- project × 1
- project euler × 1
- projekt_feladat × 1
- protected × 1
- provider × 1
- proxy × 1
- ptvs × 1
- public × 1
- pure f# × 1
- purescript × 1
- qna × 1
- quant × 1
- query sitelet × 1
- question × 1
- quotations × 1
- range × 1
- raphael × 1
- razor × 1
- rc × 1
- reactjs × 1
- real-time × 1
- ref × 1
- region × 1
- released in 4.0.190.100-rc × 1
- reporting × 1
- responsive design × 1
- rest api × 1
- rest sitelet × 1
- restful × 1
- round table × 1
- router × 1
- routing × 1
- rpc reverseproxy × 1
- runtime × 1
- sales × 1
- sample × 1
- sampleapp × 1
- scriptcs × 1
- scripting × 1
- search × 1
- self hosted × 1
- semanticui × 1
- sequence × 1
- serialisation × 1
- service × 1
- session-state × 1
- sharepoint × 1
- signals × 1
- sitelet website × 1
- sitelet.protect × 1
- sitlets × 1
- slickgrid × 1
- source code × 1
- sqlentityconnection × 1
- ssl × 1
- standards × 1
- static content × 1
- stickynotes × 1
- streamreader × 1
- stress × 1
- strong name × 1
- structures × 1
- submitbutton × 1
- subscribe × 1
- svg example html5 websharper.ui.next × 1
- sweetalert × 1
- system.datetime × 1
- system.reflection.targetinvocationexception × 1
- table storage × 1
- targets × 1
- tdd × 1
- templates ui.next × 1
- templating × 1
- text parsing × 1
- three.js × 1
- time travel × 1
- tls × 1
- tooltip × 1
- tracing × 1
- tsunamiide × 1
- turkish × 1
- twitter-bootstrap × 1
- type erasure × 1
- type inference × 1
- type providers × 1
- type-providers × 1
- typeprovider × 1
- ui next forms × 1
- ui-next × 1
- ui.next jqueryui × 1
- ui.next charting × 1
- ui.next formlets × 1
- ui.next forms × 1
- ui.next suave visualstudio × 1
- ui.next templating × 1
- unicode × 1
- unittest client × 1
- upload × 1
- usersession × 1
- validation × 1
- vb × 1
- vb.net × 1
- vector × 1
- view.map × 1
- visal studio × 1
- visual f# × 1
- visual studio 11 × 1
- visual studio 2012 × 1
- visual studio shell × 1
- vs2017 compiler zafir × 1
- vsix × 1
- web api × 1
- web-scraping × 1
- webapi × 1
- webcomponents × 1
- webforms × 1
- webgl × 1
- webrtc × 1
- webshaper × 1
- websharper async × 1
- websharper codemirror × 1
- websharper f# google × 1
- websharper forms × 1
- websharper reactive × 1
- websharper rpc × 1
- websharper sitelets routing × 1
- websharper warp × 1
- websharper-interface-generator × 1
- websharper.chartsjs × 1
- websharper.com × 1
- websharper.exe × 1
- websharper.owin × 1
- websharper.ui.next × 1
- websharper.ui.next jquery × 1
- websockets iis × 1
- why-websharper × 1
- windows 7 × 1
- windows 8 × 1
- windows-phone × 1
- winrt × 1
- www.grabbitmedia.com × 1
- xamarin × 1
- xml × 1
- yeoman × 1
- yield × 1
- zafir beta × 1
- zafir websharper4 × 1
- zarovizsga × 1
![]() |
Copyright (c) 2011-2012 IntelliFactory. All rights reserved. Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy |
Built with WebSharper |
I realized that in my previous message I did not include
any code. Unfortunately the code base I've been using
is quite large and I can't extract any meaningful
portions to show here. What I've done instead is this:
I've created a little independent benchmark that attempts
to replicate the issue. I've written the code both in F#
and in SML, for comparison purposes. The F# code can be
found here, in a file named pmtest.fs:
[link:uploadingit.com]
The SML code, in a file pmtest.sml, can be found here:
[link:uploadingit.com]
The code simply declares a couple of moderately sized
recursive datatypes (10 constructors each). Each constructor
holds some text data along with a numeric code, in addition
to the recursive children. I then defined some mutually
recursive functions for traversing a given AST and producing
a new AST obtained just by reversing the text data of each
node. So this is very typical AST-manipulating code, the
kind that is pervasive in compilers and theorem provers.
I then defined a sample AST, again of moderate size, and
ran the traversal function on it 100,000 (10^5) times.
The results were not as bad as the results that I got
on my own code, but they were disappointing nevertheless:
F#: 20.5 seconds
SML-NJ: 10.4 seconds
MLton: 3.7 seconds
Compiling the code with fsc.exe (with --optimization on)
barely makes a dent in the F# running time, shaving off
about 1 or 1.5 seconds at best.
My question is: What is it about this code that makes
MLton outperform F# by such a factor? Any suggestions
for improving the performance of the F# code?
Thanks again...