My question is - is there a way that I can write the functional version so that it will show better performance?
When I run in release without the debugger attached, it seems like the functional version wins by a little.
When I run in debug with the debugger attached, the match statement seems to be a little bit slower than an equivalent if-else statement.
I copied your code into F# and did the exact same test, and for me the functional version took 1015ms and the imperative version took 1031ms. I ran each one 10 times and got different results each time, but on average they were almost identical
The first thing I encountered though was that the f in the imperative version is not the same as the f in the functional version. They take different numbers of arguments. Are you testing with the same function? If the function you're using for f in the functional version was passed in by binding the other argument beforehand with a partial function application then I suppose there should be no difference, but you might also try running each one multiple times and making sure the average is what's off, because it was more or less identical for me.
Thanks. Yes, I removed an argument before posting to simplify it. I meant to do this with both versions but forgot.
Anyway, my orignal timings were in a Visual Studio debug build, which is non-optimized I guess. I've now run it in a release build and I got the same results as you.
More generally, is there any reason to think that pure functional programming should be faster or slower than imperative, any particular situations where it excels? Without having much experience with it, it seems that the best it could do is be equal in speed to an equivalent imperative program, but perhaps there are optimizations that the compiler does that lead to some shortcuts.
Regarding whether or not functional languages can compete with imperative languages, I don't think F# is mature enough yet to be able to compete with functional (or imperative) languages that have been around for a really long time, but a good algorithm written in a functional language using a really good compiler can be light years faster than an "equivalent" imperative program, for lots of reasons (it can also be slower). Take a look at this page:
[link:shootout.alioth.debian.org]
Some of the benchmarks for Haskell GHC are right up there. In fact, the website originally had to redesign all of their tests because of Haskell GHC. It was beating even the best languages by orders of magnitude, like tenfold due to laziness. Laziness is one aspect that leads to higher performing code, another is parallelism. F# isn't purely functional, but in a purely functional language (where there are no side effects anywhere, no state variables) the compiler can reason very deeply about, and do complex data flow analysis on your code to prove certain things about it, leading to things like automatic parallelization of large portions of code, among other things. In an imperative language, when you're sharing data across multiple threads, you have to synchronize access to that data, which by definition means someone is going to be waiting idly to access the data when they could be doing something useful. If your program is completely stateless, there is no shared data, and nothing to synchronize. Things can automatically run on different cpus without the need to worry about it.
Also, one last example, consider the case in an imperative language where you have a linked list and you want to make a copy of it for someone else to use. So you have A = [3, 4, 5, 6, 7, 8, 9, 10]. someone makes a copy of it, and deletes the first item, and replaces it with its negative and calls this new list B. so now there's 2 lists, A = [3, 4, 5, 6, 7, 8, 9, 10], and B = [-3, 4, 5, 6, 7, 8, 9, 10]. More than likely you've allocated 16 different nodes in memory if you're doing this in an imperative language. In F#, as in most other functional languages, there are only 9 different nodes allocated. There's the list K = [4, 5, 6, 7, 8, 9, 10], and then two other lists, [3] -> K, and [-3] -> K. The tails use the same actual memory, and the reason this is safe is because they're immutable, it's provably safe because there's no chance someone will ever end up with unexepcted changes to the middle of the list, since it's not modifiable in the first place.
Oh, one more example. Suppose you're trying to find all right triangles with integer sides whose perimeter is less than or equal to 750. Who knows how you'd do this in C or C++, it wouldn't be super difficult or anything, but on the other hand it wouldn't be trivial. Here's the code in Haskell.
1
[ (x,y,z) | x>0, y>0, x^2+y^2==z^2, x+y+z<=750 ]
(untested).
But look at how much work the compiler is doing for you. The more work the compiler does for you, the more freedom it has to pick the best possible way to do it.
Some of the benchmarks for Haskell GHC are right up there. In fact, the website originally had to redesign all of their tests because of Haskell GHC. It was beating even the best languages by orders of magnitude, like tenfold due to laziness. Laziness is one aspect that leads to higher performing code, another is parallelism. F# isn't purely functional, but in a purely functional language (where there are no side effects anywhere, no state variables) the compiler can reason very deeply about, and do complex data flow analysis on your code to prove certain things about it, leading to things like automatic parallelization of large portions of code, among other things. In an imperative language, when you're sharing data across multiple threads, you have to synchronize access to that data, which by definition means someone is going to be waiting idly to access the data when they could be doing something useful. If your program is completely stateless, there is no shared data, and nothing to synchronize. Things can automatically run on different cpus without the need to worry about it.
That was a long list of serious misconceptions.
Firstly, Haskell looks artificially good on the shootout tests because they are flawed benchmarks. Specifically they are trivially reducible in ways that real software is not. Other benchmarks have already proven that Haskell cannot provide competitive performance. Moreover, you need someone with a PhD in writing optimizing Haskell compilers just to get close.
Secondly, Haskell looks artificially good on the shootout because Haskell's authors optimized the Haskell compiler and its libraries specifically for the shootout in order to make Haskell look artificially good.
Thirdly, laziness is extremely detrimental to performance because it makes time and space consumption wildly unpredictable. There are many practical examples of "laziness gone bad" in this context. A Burrows-Wheeler block sorting data compressor that was optimized for two weeks by expert members of the Haskell Cafe mailing list only to be left 10,000x slower than a C alternative. The Darcs version management software was the only software ever written in Haskell to garner a significant userbase but is now falling out of favor because it is unusably slow (even the developers of the Glasgow Haskell itself compiler dropped it because the alternative, written in Python, was an order of magnitude faster).
Fourthly, laziness seriously undermines parallelism because it introduces lazy thunks everywhere that can be mutated from unevaluated expressions to evaluated values simultaneously from multiple threads, i.e. it creates billions of race conditions around almost every subexpression evaluated in the entire lifetime of a program. To get an idea of how detrimental this is, just read the latest academic paper about GHC's new parallel garbage collector. Their technology is far behind .NET's concurrent GC and they are already reaching the limits of feasibility (they developed a concurrent GC but shelved it because it was unusably slow). Moreover, the hacks they had to use to get even today's poor performance completely undermine reliability. In particular, they document subtle bugs that introduce infinite memory leaks in the garbage collector (!).
Finally, automatic parallelism has been studied intensively and every single working implementation was a complete failure. The idea that purely functional programming magically makes parallelization trivial is just complete nonsense. Haskell provides combinators for informally guiding the compiler with regard to parallelism but it is a toy compared to the state-of-the-art wait-free work-stealing queues already shipping in Microsoft's Task Parallel Library.
Haskell does have some merits but performance and parallelism are certainly not among them.
[link:shootout.alioth.debian.org]
Some of the benchmarks for Haskell GHC are right up there. In fact, the website originally had to redesign all of their tests because of Haskell GHC. It was beating even the best languages by orders of magnitude, like tenfold due to laziness.
No, that is not correct.
Rather than repeat myself, please read these relevant posts.
[link:shootout.alioth.debian.org]
Some of the benchmarks for Haskell GHC are right up there. In fact, the website originally had to redesign all of their tests because of Haskell GHC. It was beating even the best languages by orders of magnitude, like tenfold due to laziness.
No, that is not correct.
Rather than repeat myself, please read these relevant posts.
Well that's interesting, for reference my source was here (which is mentioned in that thread anyway, but here it is for reference). But all I really said is that it was "beating" other languages. If the means by which it was beating them is by throwing away unnecessary work that the other languages weren't throwing away, that still exactly highlights one of the advantages of functional languages. Anyway apparently this issue is a fairly tense one, which I had no idea about so I'll just drop it :)
I was really just trying to illustrate the point that functional languaes are intelligent about dropping unnecessary work.
[link:shootout.alioth.debian.org]
Some of the benchmarks for Haskell GHC are right up there. In fact, the website originally had to redesign all of their tests because of Haskell GHC. It was beating even the best languages by orders of magnitude, like tenfold due to laziness.
No, that is not correct.
Rather than repeat myself, please read these relevant posts.
Well that's interesting, for reference my source was here (which is mentioned in that thread anyway, but here it is for reference). But all I really said is that it was "beating" other languages. ...
Didn't you say "In fact, the website originally had to redesign all of their tests because of Haskell GHC."? :-)
Didn't you say " It was beating even the best languages by orders of magnitude, like tenfold due to laziness." ? :-)
The measurements are still available - the lazy Haskell program was slower than a Java program that set an initial heap, the lazy Haskell program took 3.07s compared to 3.79s for a C program that actually followed the rules.
You'll notice there are half a dozen C and C++ programs also considered not to have followed the rules which only took 0.1s - 0.6s.
You'll notice there are half a dozen C and C++ programs also considered not to have followed the rules which only took 0.1s - 0.6s.
I make mistakes too.
I copied the wrong column - there are half a dozen C and C++ programs also considered not to have followed the rules which only took 0.26s - 1.60s compared to 3.07s
I think it is true that, in general, it is easy in lazy languages to write code that you think will spin the processor. But, it will simply be optimized away.
My guess is that this kind of cruft bogs down many large scale imperative programs. An easy (and small) example is a stack of C functions that takes the same pointer, and every function checks
1
if( p == NULL )
all the way through the stack.
Thanks for the insight. ...to bad it was on reddit ;)
Ahh, but in an imperative style if you didn't want a copy you can add a node to the front of linked list with just one node allocation. Similarily you can add to the end of the linked list with one allocation as well. Whereas in a functional style, to add a node to the end of the list, even if I want a copy, you have to reallocate the entire list. So it's a two-way street.
Definitely a two way street, if it were a one way street there'd be provably no reason to use imperative languages :) Yea you can always add a node to the front of a linked list in an imperative language, but the only point I was really trying to make is that in a functional setting the same memory is re-used by default, whereas in an imperative setting, you have to actively think about whether or not it makes sense use the same reference in multiple places, and often times it doesn't.
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 |
Hi,
I have two versions of the same function, one in functional style (as best I can) and one in imperative. The function finds the maximum of a given function by trying a discrete number of points.
When I time these functions with nx = 20000000, the functional version takes 1043 ms and the imperative version takes 859 ms.
My question is - is there a way that I can write the functional version so that it will show better performance?