That's a great short read on the idiocy of over-optimization. I get a lot of similar questions when people learn that I write common lisp for a living - in most cases they go silent when I provide a sufficiently efficient solution to a problem that was just given to me in the meeting in which we were supposed to agree on a schedule for the implementation.... (which btw also happened a lot when I was mostly hacking in perl 15 years ago - if you haven't read "beating the averages"[1], read it now!).
I'm a big scheme fan, and really enjoy programming in it..
but the lack of a real ecosystem is really starting to hurt my progress.
What suggestions do you have for lisp-1 -> lisp-2? While I prefer lisp-1 + syntax-parse, I'd rather have.. quicklisp. quicklisp is life. the quicklisp must flow.
Leiningen serves a different purpose than quicklisp (Something more similar would be Clojars maybe). That said, Leiningen does way more (in a good sense) than quicklisp and any commonly used FLOSS tools in CL.
While I am actually not a fan of quicklisp for various reasons... just go for it? Emacs, slime & quicklisp and you are ready to go. As soon as you have created your first in-house libraries you'll want to look at asdf's config so you can override the quicklisp repo.
> He who controls the quicklisp controls the universe!
Too true, that's my main complaint about ql since day 1. Give me a simple way to define a ql-repo hierarchy and I'll be fine with it. I did invest a few hours maybe 3 years ago but realized that it's too much work (research and patching) to allow for that. Recent developments (author pitching for money in ql context) make me even more worried :(.
"...you don't need C. Haskell will give you the same performance, or better, and cut your development and maintenance costs by 75 to 90 percent as well."
I want both the compiler from the year 2030 and the drugs this guy has.
If you're not using C/C++ to take advantage of locality of reference, low memory footprint and the things that make C/C++ fast then why the hell are you using it?
Seems like this should just be filed under "right tool for the job".
While I agree with this, I look around the history of programming and see that too much code was written in C, and too much is being written in C++. And writing even more of it is my day job.
One thing that slows down change is that new languages come with parallel universes called "runtimes". The D language (https://dlang.org/) is an honourable exception (it has a runtime, but not a parellel universe).
But D is only an incremental improvement on C++, and somehow never took off. Maybe Rust will be the Saviour.
The advantage (one advantage?) of C is that if you write a library in C, then it can be used on any platform, and from any language. Basically every language has bindings into C.
Many times is a mixture of culture and people not knowing better.
Runtimes are a wonderful thing, they abstract the OS and make it almost irrelevant. Actually, I see POSIX as the unofficial C runtime that for political reasons wasn't made into ANSI C and turning into a separate standard instead.
I stop caring about POSIX, when I left the C++ world as full stack behind and started using it only to optimize specific code paths or OS integration.
Personally I think it is a side effect of the world going all VM/JIT, when the same languages could easily be AOT compiled to native code.
So many developers just kept using C and C++, because they thought they were the only ones giving them compiler toolchains to do AOT.
Lucky we now have Go, D, Swift, .NET Native, OCaml, Haskell, Ada, Rust to change that mentality.
The title is a bit click-baity, but the core point is sound: C is not fast, it's optimizable[1].
If you just write readable, friendly C code it won't be all that much faster than normal code in a high-level language like Haskell—it might even be slower. I know, I've done that myself. But you never see that in benchmarks, do you? That's not what benchmarks are about.
Here's another illustration: we can compile high-level languages like Haskell and Scheme to C. Does this make them "as fast as C"? Yes—they're literally running as C. But also no. Hand-optimized C code is going to beat those compilers any day. It's not even close.
C is not magical performance sauce you can sprinkle over your code to make it fast. It's just a language that doesn't have much mandatory overhead (no GC, minimal runtime... etc) and gives you access to certain low-level knobs that you can twiddle to optimize your code. I mean, that's important and useful, but unless you're going to apply that level of effort to your whole codebase, most of your C code won't be all that fast. Some applications need this level of optimization; most don't.
So, basically, write your code in Haskell, then, profile, and rewrite performance critical parts in C and hand-optimize them.
Which means ... C has become the new assembly language!
1) Often the "performance critical parts" are actually 60-80% of your code. This is the problem with the "don't optimize prematurely" philosophy. Think of it in advance instead. Do you need the highest possible performance? Are you sure? How fast should it be then? Per request? At setup? Per data point calculated? Choose your tools accordingly.
2) For numerical code I'd actually choose Modern Fortran over C. Much less to do until it's fast.
I find that much of what I do is highly generic code, so it gets reused in all kinds of different places. That's when this happens. So for me this is the case, but yes, it certainly depends. I just want to warn people that the thinking of no premature optimisation can backfire.
When talking about performance, C actually can have overhead though.
For example, people rely on how C lays things out in memory, so C compilers have to lay things out in memory in specific ways. Haskell doesn't have this constraint and so can do a bunch of tricks.
And then there are things where C cannot move a variable/optimise out a variable because it's unknown whether some unrelated bit of code my go poke at its memory.
Now one might say "well if you know what you're doing then you can get the compiler to do what you want." Everything's possible, since everything's turing complete. But I would consider "variables need to be somewhere in memory" to be mandatory overhead.
Variables don't necessarily need to be somewhere in memory. Compilers absolutely can and will notice if you never take the address of a variable to do this optimisation - it's very common for local variables in C to be materialised nowhere but in a register.
> For example, people rely on how C lays things out in memory, so C compilers have to lay things out in memory in specific ways.
One can rely on C compilers laying out things in memory in a specific way only within a struct and when compiler-specific alignment/packing attributes are used. All other assumptions about variables being adjacent in memory are wrong and lead to undefined behavior.
It is certainly possible to do structure layout optimizations (like converting from AOS to SOA) in C and its siblings. Even where the standard requires a specific layout, the compiler can optimize as long as it can prove that the code can't tell the difference (known as the as-if rule), although in the general case this requires whole program optimization.
This is not just a theoretical thing; at a certain point in the past, both LLVM and GCC had such an optimization, driven by PGO. I think it got removed because it just didn't trigger often enough and didn't give enough benefits to justify the complexity.
Scalar replacement of structs/arrays is of course routinely done by all major compilers and allows to completely optimize away non-escaping local objects.
Another thing about C is it's also debuggable and, consequently, profilable. You don't need to guess like the article's author at what is slow. You can know exactly what functions are slow and also why by examining hazards reported for every instruction. And then you can change your program in a way, which will eliminate the hazards if you wish so (and if it's possible, of course). With a smart high level compiler, even if you will manage to match the binary to source somehow, good luck predicting what modifications to the source are going to do to the binary.
Debuggability is also often overlooked. You can attach a debugger to a running image, and, provided you have symbol names, figure what's going on. Even if all you have is a core dump you can make a good guess of what has happened most of the time. Trying Haskell and doing some research on debugging it seems the only way other than debug prints is to instrument your program and even then you are not getting much.
Language-specific profiling and debugging tools are not the tools I had in mind. Being language-specific and system-agnostic they are naturally blind to the details of the execution which don't exist in the language. E.g. things like cache misses, TLB misses, jump miss-predictions, etc etc. I somehow doubt Haskell has tools for this.
Same with the debugger - you sure can set breakpoints in GHCi, but what do you do with a core dump? What if you have a bug, which happens on a single machine in Bangladesh and takes 18 hours to reproduce? With C I can just connect with the system level debugger and, at least, know what happens. Is there such a tool for Haskell?
PS. And, again, even if such tools existed they were just for informational purposes since you don't have much control over the generated code so you cannot sensibly modify source to change the binary in expected way. For example, in C, if I see a high number of cache misses in some loop I can: a) change the data layout (e.g. align structure fields or move simultaneously accessed fields together) b) add a prefetch. What am I to do in Haskell?
>What you mean by doing prefetchin C? There isn't any ANSI C feature for that.
Actually, there is, it's called inline assembly. But any decent compiler has an intrinsic anyways.
>You can also do layout changes in Haskell, for example use a ByteString instead of Text, or an Array instead of Lists and so on.
So, I have this type data Vector3 = Vector3 Float Float Float. Can I change its layout without having to change the rest of the code? The only way I know is compiler-specific unboxing and even that has little predictable effect, as we don't even know if this type is going to hold boxed values to begin with.
> Actually, there is, it's called inline assembly. But any decent compiler has an intrinsic anyways.
Which is a language extension and not a C feature.
Nothing prevents an Haskell compiler to provide similar extensions.
> So, I have this type data Vector3 = Vector3 Float Float Float. Can I change its layout without having to change the rest of the code? The only way I know is compiler-specific unboxing and even that has little predictable effect, as we don't even know if this type is going to hold boxed values to begin with.
Yeah, with my limited Haskell knowledge I would say you would need to make use of Data.Vector.Unboxed or use unboxed values as compiler extension like on JHC.
Still, there are ways to improve performance on Haskell applications, even if they require new ways of thinking.
Back in the day higher level languages (including C) were quite bad at generating code on home computers, and we had to use Assembly if we cared about performance.
So like C compiler quality and tooling has improved in these last decades due to high industry use, Haskell tools can also improve if its acceptance in the industry keeps increasing.
And then we can probably enjoy such tools for Haskell as well.
>Which is a language extension and not a C feature.
In line assembler has always been C feature. I also fail to see the point of mentioning intrinsics being extension. Do you have an example of a compiler, which does not support cache prefetch on a platform that supports them?
>I would say you would need to make use of Data.Vector.Unboxed
I believe you are missing my point. The run-time layout of this type is not known in the first place. Making it unboxed may or may not change its layout to another unknown one. If you need a specific change, and not a change for the sake of change - there is nothing you can do. In C, I might want this to be a 12 byte type if I access the values sequentially and a 16 byte if randomly. In Haskell you can either have it boxed (and you have an order of magnitude more cache misses) or have either 12 or 16 byte in unboxed, but you cannot tell which one and if you wanted another one - tough cookies.
>Haskell tools can also improve if its acceptance in the industry keeps increasing.
Try telling that thing about Haskell to actual enterprise developers. The lazy evaluation puts even experienced veterans off the track when it comes to profiling/speak-leaks/debugging. Haskell support is lousy for debugging and profiling except for toy programs, period. AFAIK, Standard Charter has asked the devs to develop an entirely different language which is not lazy at all.
"Interestingly, Mu eschews Haskell’s laziness and is a strict language instead." [1]
The Haskell community is silent on getting rid of lazy-ness from it or at least making it completely and entirely optional for people.
So let me repeat this even if Haskell is good to train your mind in the comfort of your home, it is miles far from being enterprise ready.
To be fair, John Harrop is notorious for having an axe to grind with Haskell. He's very knowledgable, but often makes gross mischaracterisations of Haskell. Which is a shame, because he knows better than that.
Pardon me, but why didn't they throw away that strict runtime (especially if laziness so great?) and use the already available Haskell instead, if its laziness has not got any problems?
The rationale behind their decision is simple: Laziness comes with its big problems, one of them is difficult to debug and predict runtime behavior.
Sorry, what do you know about Standard Chartered exactly? Have you worked there? Have you discussed this with the designers of their language (called "Mu")?
Mu is strict because it was designed to target an existing strict runtime for which plenty of code was already written (in a language called Lambda, the letter before Mu in the Greek alphabet) and which was already deployed to many users through the corporation. The decision to be strict and not lazy has nothing to do with any downsides of laziness.
I'm largely ignorant of Haskell, but isn't one the touted benefits of strictly typed languages (bondage and discipline typing I've heard it called) that SEGVs simply don't happen?
I've built fairly complex systems written in similar languages which were capable of crashing in the sense of an un-handleable OOM, but they didn't just trip over a bad pointer and die.
If this is more widely true of such languages, and not just an artefact of the implementation and problem domain I was working in, then perhaps the need to attach a debugger to running systems, or corefile-spelunk, is reduced. What do you think of that?
FWIW, I would still have liked a better debugger on the system I used. We had a small team and they were making hard choices about what was capable of being implemented in the time to hand. So I did a bunch of printf-debugging of my logic, and learned to right some log-file analysis tools. But it never crashed.
Strict typing eliminates some run-time bugs but not all of them. You can type "tail []" in any Haskell interpreter and see what's going to happen. Also there are logical bugs, which won't go away. For example you typed '-' instead of '+' somewhere. Type system won't care since both operators are the same type but suddenly you get a bug report that the player character in a videogame sometimes shoves his gun up his ass instead of holstering it.
The behaviour of 'tail []' is not a failure of static typing but of the specified type of tail. tail is partial but fails to represent that in its type when it could easily do so. Changing it to List a -> Maybe (List a) would be more accurate, albeit possibly a little more confusing for beginners.
I've only done a bit of Haskell, but when I build a Haskell program, I usually debug a function with GHC's builtin REPL (ghci), by checking expressions inside the function until I find the place where their evaluation does not match my expectation. It's similar to printf debugging, but less tedious and more informative because e.g. you can get type information about subexpressions immediately.
I can't say anything on debugging large-ish programs, though. Regarding profiling, GHC's runtime system includes a profiler that looks fairly competent from what I've seen. (For example, you can mark expressions that you want profiled, or you can just go with profiling per function.)
Also many that praise C's performace, tend to forget that once upon a time, hobby Z80, 6052, 80x86, 68000 programmers in home micros could write better code than C compilers were able to generate.
So there are a few decades of engineering time spent improving code generation of C compilers.
But how much faster would an optimized C be vs a high level language?
There are a few applications where every little bit of performance counts, like if you want to squeeze as many frame per second from a game. But I'd say for 95% of the code written, performance only matters if it is 10x or 50x faster. If you need to run a script and the script runs in 15s instead of 20s, it might be an impressive 25% performance gain but it's probably not worth the additional time spent optimising.
It's my experience that people use the term "fast" and "faster" differently than I do.
Go get some data[1] and see how long it takes you to process 600 million rows of anything. For example, take the 100 most popular symbols and find their last bid price.
Once it's in memory, top open source SQL engines take 5-10 seconds, but writing it in C you can do it within 20msec.
I'd be really curious to see how fast .net actually is: I'm told Spark took 35 seconds to work that out, and it's referred to as "Lightning-fast".
I don't use magnitudes except when being illustrative because multiplying magnitudes like 10x and 50x become more exaggerated as the data gets bigger. When the cpu stalls, and waits for memory to come in, or the "runtime" takes a diversion to garbage collect some things, we're really talking about doing things that don't need to be doing.
When I say something is "fast", I usually mean it isn't doing anything that doesn't need to be done, or I mean I can justify everything that it is doing that doesn't need to be done (as, for example, waiting for other slower things, or making it easier for me to write).
.net may do fewer things that don't need doing than (say) perl, but programming in C we can actually make "fast" programs; we can simply choose to not do anything that doesn't need doing, and that means .net will never catch up.
It would be interesting to run the test, but for that it would take someone proficient in both C and .net to run a consistent algorithm on the same machine (I can only do the latter). But to just process raw data, I'd expect .net to be pretty fast. There is a bit of an overhead for the JIT compilation, but at the end you run machine code. You will loose a little bit of performance for things like array boundary checking, but surely it can't be more than a fraction. I would assume differences would appear when you are running a problem that requires a lot of memory allocation / de-allocation, creating and deleting objects.
> How many programming hours were spent writing the 20ms C program vs the 5-10s in other programming languages.
Sorry for the trick, but it actually wasn't C at all, but a simple, non-optimising interpreted language that beats the pants off all these fancy optimising compilers who can work around shit programmers making shit programs.
The <20msec solution written in K is as follows:
select last bid by sym from quote where date=d,sym in S
I'd love to talk about how to write programs that are fast, but by all means, let's talk about how we don't really need our programs to be fast in the first place.
This is why I think the biggest advance we could have towards removing the efficiency ceiling of high-level code would be having better ways to write C-level code that can interact efficiently and easily with high level code.
For example, Java and .NET can call C code (very easily in the .NET case) but require the computational overhead of a GC transition and the maintenance overhead of having to write wrappers around any calls in the reverse direction. It would be awesome if you could easily write code that intermixed statements dealing with things at a lower level (malloc, pointer arithmetic, etc.) and those that access high level abstractions (GC'ed objects, virtual dispatch, etc.). The CLR and to a lesser degree C# technically allow for this kind of thing, but it's still sub-optimal from both an ease of use standpoint (it's very tedious to write C# like this) and a performance standpoint (the CLR's JIT produces non-optimal code compared to a static C compiler).
.NET Native may solve the second one (though I am skeptical at this point that this will be Microsoft's focus with the technology, as opposed to easing deployment/lowering start up times) but I still feel we lack a language that makes this kind of thing easy.
> It would be awesome if you could easily write code that intermixed statements dealing with things at a lower level (malloc, pointer arithmetic, etc.) and those that access high level abstractions (GC'ed objects, virtual dispatch, etc.). The CLR and to a lesser degree C# technically allow for this kind of thing, but it's still sub-optimal from both an ease of use standpoint (it's very tedious to write C# like this) and a performance standpoint (the CLR's JIT produces non-optimal code compared to a static C compiler).
Yes, unfortunately as far as I can tell it still requires transitions between managed/unmanaged code. Though I will admit it does wonders from a usability perspective.
I think it was a very big mistake to have Java and .NET use a JIT by default instead of AOT compilers like Eiffel, Modula-3, Oberon(-2), Component Pascal had.
It created a bad perception of what memory safe programing languages compilers are capable of.
Not really. Depends how big your list is. If the answer is "not really that big" then cache misses and memory non-locality can cause linked lists to be less efficient. Besides, arrays actually have the same amortized time complexity as lists for dequeue operations. It's just in-the-middle deletions and insertions that can cause problems in arrays.
Using linked-list is not such an issue if nodes are not allocated with the general purpose allocator, and instead are allocated from a pool of memory addresses next to one another.
Right, but then you get fragmentation in the allocator unless all you're doing is stack operations (in which case you might as well use an array). Once you get fragmentation, you have the same problem as with any other linked list setup.
If you find yourself setting out to write Haskell that can outperform C, you will probably be disappointed. I love Haskell, but the myth that it just takes a little bit of elbow grease to make it as fast as C really needs to die. See for example: https://chadaustin.me/2015/02/buffer-builder/
THAT SAID, I have a relevant story. IMVU used this file format called "CFL" for its 3D content. It was basically a kind of zip file except it used LZMA for asset compression. The original CFL library was written in C++, and while it worked fine, it was getting annoying to maintain and compile it, as well as inefficient to pass data from Python file buffers into C++ and back out. So one day I decided to see if I could replace it with a bit of Python code and pylzma. After I made this change, parsing our content files was _twice_ as fast. Having the code in a couple hundred lines of simple Python allowed me to optimize the data flows, minimizing copying from the file buffers into the LZMA decoder and then to the consumer of the data.
Sometimes the best way to make something fast is to write it in a safe, expressive language that lets you directly express your intent. :)
Convoluted data flow and unnecessary copies! The C++ implementation was thousands of lines and the Python one was a couple hundred, so it was much easier to see the path the data had to take.
I think it's worth pointing out though that idiomatic C is probably going to be more consistently performant. It seems common to run in to situations in Haskell where one change can cause 10X speedup, but I don't see that nearly as often with C code. I don't have a lot of evidence on hand to support this, just what I've observed personally. This seen fair? Relevant?
A similar paper on Haskell's stream fusion has a pretty similar PDF filename (http://research.microsoft.com/en-us/um/people/simonpj/papers...). With benchmarks showing the "naive" implementation in Haskell of a simple stream problem beating hand-tuned C.
Haskell is still pretty well-defined in terms of execution, but freedom from how to manage memory is in itself a major liberator for the optimisers.
1. The "reverse-complement" problem here is simply about reading a file, reversing strings and mapping a small set of characters to another one and printing the result. This is really a simple problem and there aren't a lot of optimisations to be done. Really this about having the fastest I/O library and thus doesn't seem like a good way to compare C and Haskell. If you look at the various solutions presented on benchmark games, the core algorithms are all the same and they only differ on how to read/write and the use of threads.
2. The author talks about the importance of reducing cache misses due to pointer indirection and then proceeds by implementing a character buffer as a linked list of small buffers...
3. The C version reads and writes character one by one, this can be greatly improved by reading/writing bigger chunks at once. Actually, the author points out this optimisation but says "that would require significant changes to the code;". So the author spent time optimising the Haskell version, but spending time optimising the C version is too much work? And then arrives at the conclusion Haskell is faster?
4. Commenters on the author's blog cannot reproduce the results...
I've been programming in both for a while now, and I wouldn't use 'faster than C' as a selling point.
In my (limited) experience, what Haskell gives you is conciseness and abstraction. What C gives you is speed and 'close to the machine' programming for when you need it. I'm not sure this article provides any useful insights.
I don't understand why someone thought it was a good idea to require that POSIX stdio should lock the thread. It slows down stdio several times compared to the nonlocking version, and it completely goes against "don't pay for what you don't use" as single threaded programs have to pay the cost too. And locking and unlocking the stream manually is like two lines of code, which a person writing MT code should be well capable of writing.
88 comments
[ 4.6 ms ] story [ 155 ms ] thread[1] http://paulgraham.com/avg.html
but the lack of a real ecosystem is really starting to hurt my progress.
What suggestions do you have for lisp-1 -> lisp-2? While I prefer lisp-1 + syntax-parse, I'd rather have.. quicklisp. quicklisp is life. the quicklisp must flow.
I personally would recommend clojure + cider + emacs for an excellent ecosystem and tooling.
Too true, that's my main complaint about ql since day 1. Give me a simple way to define a ql-repo hierarchy and I'll be fine with it. I did invest a few hours maybe 3 years ago but realized that it's too much work (research and patching) to allow for that. Recent developments (author pitching for money in ql context) make me even more worried :(.
I want both the compiler from the year 2030 and the drugs this guy has.
I guess you can call self-delusion a drug..
Seems like this should just be filed under "right tool for the job".
One thing that slows down change is that new languages come with parallel universes called "runtimes". The D language (https://dlang.org/) is an honourable exception (it has a runtime, but not a parellel universe).
But D is only an incremental improvement on C++, and somehow never took off. Maybe Rust will be the Saviour.
Runtimes are a wonderful thing, they abstract the OS and make it almost irrelevant. Actually, I see POSIX as the unofficial C runtime that for political reasons wasn't made into ANSI C and turning into a separate standard instead.
I stop caring about POSIX, when I left the C++ world as full stack behind and started using it only to optimize specific code paths or OS integration.
Personally I think it is a side effect of the world going all VM/JIT, when the same languages could easily be AOT compiled to native code.
So many developers just kept using C and C++, because they thought they were the only ones giving them compiler toolchains to do AOT.
Lucky we now have Go, D, Swift, .NET Native, OCaml, Haskell, Ada, Rust to change that mentality.
If you just write readable, friendly C code it won't be all that much faster than normal code in a high-level language like Haskell—it might even be slower. I know, I've done that myself. But you never see that in benchmarks, do you? That's not what benchmarks are about.
Here's another illustration: we can compile high-level languages like Haskell and Scheme to C. Does this make them "as fast as C"? Yes—they're literally running as C. But also no. Hand-optimized C code is going to beat those compilers any day. It's not even close.
C is not magical performance sauce you can sprinkle over your code to make it fast. It's just a language that doesn't have much mandatory overhead (no GC, minimal runtime... etc) and gives you access to certain low-level knobs that you can twiddle to optimize your code. I mean, that's important and useful, but unless you're going to apply that level of effort to your whole codebase, most of your C code won't be all that fast. Some applications need this level of optimization; most don't.
[1]: I actually wrote a little article about this myself too: http://www.forbes.com/sites/quora/2014/01/09/can-a-high-leve...
1) Often the "performance critical parts" are actually 60-80% of your code. This is the problem with the "don't optimize prematurely" philosophy. Think of it in advance instead. Do you need the highest possible performance? Are you sure? How fast should it be then? Per request? At setup? Per data point calculated? Choose your tools accordingly.
2) For numerical code I'd actually choose Modern Fortran over C. Much less to do until it's fast.
That has not been my experience at all. But I suppose it depends on what kind of programs you're writing.
For example, people rely on how C lays things out in memory, so C compilers have to lay things out in memory in specific ways. Haskell doesn't have this constraint and so can do a bunch of tricks.
And then there are things where C cannot move a variable/optimise out a variable because it's unknown whether some unrelated bit of code my go poke at its memory.
Now one might say "well if you know what you're doing then you can get the compiler to do what you want." Everything's possible, since everything's turing complete. But I would consider "variables need to be somewhere in memory" to be mandatory overhead.
One can rely on C compilers laying out things in memory in a specific way only within a struct and when compiler-specific alignment/packing attributes are used. All other assumptions about variables being adjacent in memory are wrong and lead to undefined behavior.
This is not just a theoretical thing; at a certain point in the past, both LLVM and GCC had such an optimization, driven by PGO. I think it got removed because it just didn't trigger often enough and didn't give enough benefits to justify the complexity.
Scalar replacement of structs/arrays is of course routinely done by all major compilers and allows to completely optimize away non-escaping local objects.
Debuggability is also often overlooked. You can attach a debugger to a running image, and, provided you have symbol names, figure what's going on. Even if all you have is a core dump you can make a good guess of what has happened most of the time. Trying Haskell and doing some research on debugging it seems the only way other than debug prints is to instrument your program and even then you are not getting much.
Actually the languages that don't, aren't worth using in production environments, this isn't anything C special.
PS. And, again, even if such tools existed they were just for informational purposes since you don't have much control over the generated code so you cannot sensibly modify source to change the binary in expected way. For example, in C, if I see a high number of cache misses in some loop I can: a) change the data layout (e.g. align structure fields or move simultaneously accessed fields together) b) add a prefetch. What am I to do in Haskell?
You can also do layout changes in Haskell, for example use a ByteString instead of Text, or an Array instead of Lists and so on.
What you mean by doing prefetchin C? There isn't any ANSI C feature for that. Only compiler tricks and hope for the best.
And I am saying you cannot.
>What you mean by doing prefetchin C? There isn't any ANSI C feature for that.
Actually, there is, it's called inline assembly. But any decent compiler has an intrinsic anyways.
>You can also do layout changes in Haskell, for example use a ByteString instead of Text, or an Array instead of Lists and so on.
So, I have this type data Vector3 = Vector3 Float Float Float. Can I change its layout without having to change the rest of the code? The only way I know is compiler-specific unboxing and even that has little predictable effect, as we don't even know if this type is going to hold boxed values to begin with.
Which is a language extension and not a C feature.
Nothing prevents an Haskell compiler to provide similar extensions.
> So, I have this type data Vector3 = Vector3 Float Float Float. Can I change its layout without having to change the rest of the code? The only way I know is compiler-specific unboxing and even that has little predictable effect, as we don't even know if this type is going to hold boxed values to begin with.
Yeah, with my limited Haskell knowledge I would say you would need to make use of Data.Vector.Unboxed or use unboxed values as compiler extension like on JHC.
Still, there are ways to improve performance on Haskell applications, even if they require new ways of thinking.
Back in the day higher level languages (including C) were quite bad at generating code on home computers, and we had to use Assembly if we cared about performance.
So like C compiler quality and tooling has improved in these last decades due to high industry use, Haskell tools can also improve if its acceptance in the industry keeps increasing.
And then we can probably enjoy such tools for Haskell as well.
In line assembler has always been C feature. I also fail to see the point of mentioning intrinsics being extension. Do you have an example of a compiler, which does not support cache prefetch on a platform that supports them?
>I would say you would need to make use of Data.Vector.Unboxed
I believe you are missing my point. The run-time layout of this type is not known in the first place. Making it unboxed may or may not change its layout to another unknown one. If you need a specific change, and not a change for the sake of change - there is nothing you can do. In C, I might want this to be a 12 byte type if I access the values sequentially and a 16 byte if randomly. In Haskell you can either have it boxed (and you have an order of magnitude more cache misses) or have either 12 or 16 byte in unboxed, but you cannot tell which one and if you wanted another one - tough cookies.
>Haskell tools can also improve if its acceptance in the industry keeps increasing.
And if they do we will discuss them then.
The Haskell community is silent on getting rid of lazy-ness from it or at least making it completely and entirely optional for people. So let me repeat this even if Haskell is good to train your mind in the comfort of your home, it is miles far from being enterprise ready.
[1] http://anil.recoil.org/papers/2011-cufp-scribe-preprint.pdf
https://wiki.haskell.org/Haskell_in_industry
For people without an argument. Because then you would have to know what those firms actually do.
Read this: http://flyingfrogblog.blogspot.de/2010/05/why-is-haskell-use...
It's from 2010 so things may be different - but it illustrates that you can't just drop names, you have to do your research.
I at least bothered to provide a list of enterprises that bother to make use of Haskell, regardless how much of it they end up writing.
Anyone can easily search for videos of online talks where people describe their experiences in the finance industry with data modeling in Haskell.
Also if it would be so bad, the number of Haskell jobs outside academia wouldn't be increasing.
Sure, but you have to do your research too, into what kind of person Jon Harrop is and about how seriously you should take his critiques of Haskell.
Yeah, but that's because it targets a pre-existing strict runtime, not because of any problems with laziness.
The rationale behind their decision is simple: Laziness comes with its big problems, one of them is difficult to debug and predict runtime behavior.
Mu is strict because it was designed to target an existing strict runtime for which plenty of code was already written (in a language called Lambda, the letter before Mu in the Greek alphabet) and which was already deployed to many users through the corporation. The decision to be strict and not lazy has nothing to do with any downsides of laziness.
I've built fairly complex systems written in similar languages which were capable of crashing in the sense of an un-handleable OOM, but they didn't just trip over a bad pointer and die.
If this is more widely true of such languages, and not just an artefact of the implementation and problem domain I was working in, then perhaps the need to attach a debugger to running systems, or corefile-spelunk, is reduced. What do you think of that?
FWIW, I would still have liked a better debugger on the system I used. We had a small team and they were making hard choices about what was capable of being implemented in the time to hand. So I did a bunch of printf-debugging of my logic, and learned to right some log-file analysis tools. But it never crashed.
I can't say anything on debugging large-ish programs, though. Regarding profiling, GHC's runtime system includes a profiler that looks fairly competent from what I've seen. (For example, you can mark expressions that you want profiled, or you can just go with profiling per function.)
If performance is important, it is a good idea to use an optimizable language.
So there are a few decades of engineering time spent improving code generation of C compilers.
There are a few applications where every little bit of performance counts, like if you want to squeeze as many frame per second from a game. But I'd say for 95% of the code written, performance only matters if it is 10x or 50x faster. If you need to run a script and the script runs in 15s instead of 20s, it might be an impressive 25% performance gain but it's probably not worth the additional time spent optimising.
Surely C can't be that much faster than say .net?
It's my experience that people use the term "fast" and "faster" differently than I do.
Go get some data[1] and see how long it takes you to process 600 million rows of anything. For example, take the 100 most popular symbols and find their last bid price.
Once it's in memory, top open source SQL engines take 5-10 seconds, but writing it in C you can do it within 20msec.
I'd be really curious to see how fast .net actually is: I'm told Spark took 35 seconds to work that out, and it's referred to as "Lightning-fast".
I don't use magnitudes except when being illustrative because multiplying magnitudes like 10x and 50x become more exaggerated as the data gets bigger. When the cpu stalls, and waits for memory to come in, or the "runtime" takes a diversion to garbage collect some things, we're really talking about doing things that don't need to be doing.
When I say something is "fast", I usually mean it isn't doing anything that doesn't need to be done, or I mean I can justify everything that it is doing that doesn't need to be done (as, for example, waiting for other slower things, or making it easier for me to write).
.net may do fewer things that don't need doing than (say) perl, but programming in C we can actually make "fast" programs; we can simply choose to not do anything that doesn't need doing, and that means .net will never catch up.
[1]: ftp://ftp.nyxdata.com/Historical%20Data%20Samples/Daily%20TAQ/
Do those 5-10s vs 20ms actually matter for the data consumer in the overall use case scenario?
Sorry for the trick, but it actually wasn't C at all, but a simple, non-optimising interpreted language that beats the pants off all these fancy optimising compilers who can work around shit programmers making shit programs.
The <20msec solution written in K is as follows:
I'd love to talk about how to write programs that are fast, but by all means, let's talk about how we don't really need our programs to be fast in the first place.For example, Java and .NET can call C code (very easily in the .NET case) but require the computational overhead of a GC transition and the maintenance overhead of having to write wrappers around any calls in the reverse direction. It would be awesome if you could easily write code that intermixed statements dealing with things at a lower level (malloc, pointer arithmetic, etc.) and those that access high level abstractions (GC'ed objects, virtual dispatch, etc.). The CLR and to a lesser degree C# technically allow for this kind of thing, but it's still sub-optimal from both an ease of use standpoint (it's very tedious to write C# like this) and a performance standpoint (the CLR's JIT produces non-optimal code compared to a static C compiler).
.NET Native may solve the second one (though I am skeptical at this point that this will be Microsoft's focus with the technology, as opposed to easing deployment/lowering start up times) but I still feel we lack a language that makes this kind of thing easy.
Have you looked into C++/CLI?
https://blogs.msdn.microsoft.com/dotnet/2016/04/18/whats-new...
I think it was a very big mistake to have Java and .NET use a JIT by default instead of AOT compilers like Eiffel, Modula-3, Oberon(-2), Component Pascal had.
It created a bad perception of what memory safe programing languages compilers are capable of.
Linked lists are slow...
The leap you're making here is about how distant the memory addresses should be in general.
This is entirely dependent on your problem and your data-set.
https://www.youtube.com/watch?v=fHNmRkzxHWs&list=PL8N1fQK8Lw...
THAT SAID, I have a relevant story. IMVU used this file format called "CFL" for its 3D content. It was basically a kind of zip file except it used LZMA for asset compression. The original CFL library was written in C++, and while it worked fine, it was getting annoying to maintain and compile it, as well as inefficient to pass data from Python file buffers into C++ and back out. So one day I decided to see if I could replace it with a bit of Python code and pylzma. After I made this change, parsing our content files was _twice_ as fast. Having the code in a couple hundred lines of simple Python allowed me to optimize the data flows, minimizing copying from the file buffers into the LZMA decoder and then to the consumer of the data.
Sometimes the best way to make something fast is to write it in a safe, expressive language that lets you directly express your intent. :)
Haskell is still pretty well-defined in terms of execution, but freedom from how to manage memory is in itself a major liberator for the optimisers.
https://news.ycombinator.com/item?id=5090717
https://news.ycombinator.com/item?id=5080210
https://wiki.haskell.org/ThreadScope
2. The author talks about the importance of reducing cache misses due to pointer indirection and then proceeds by implementing a character buffer as a linked list of small buffers...
3. The C version reads and writes character one by one, this can be greatly improved by reading/writing bigger chunks at once. Actually, the author points out this optimisation but says "that would require significant changes to the code;". So the author spent time optimising the Haskell version, but spending time optimising the C version is too much work? And then arrives at the conclusion Haskell is faster?
4. Commenters on the author's blog cannot reproduce the results...
I've been programming in both for a while now, and I wouldn't use 'faster than C' as a selling point.
In my (limited) experience, what Haskell gives you is conciseness and abstraction. What C gives you is speed and 'close to the machine' programming for when you need it. I'm not sure this article provides any useful insights.