Hacker News new | ask | show | jobs
by SomeCallMeTim 3689 days ago
Huh. I find that when my C++ compiles, it almost always works. Correctly.

But I used the language for nearly 20 years, so maybe it's experience and the patterns I've learned?

Not that Rust or Elm aren't better options; don't know, really. Despite feeling like I'm an expert in C++, I don't actually like the language much. I've used Go some, and I like it, but I haven't tried Rust or Elm yet.

5 comments

Well, I find that when my C++ compiles <segmentation fault>

When I first learned Java, still in my undergrad, my first impression (and nearly everybody's) was that when it compiles it works. It was still more work than Perl, Lisp, Prolog, and everything, but didn't require the annoying C debugging cycle.

But, anyway, all that is kids play near Haskell.

Hello! I don't know if English is your first language or not, but that last sentence was significantly confusing that I wanted to correct it for you and others who may read it.

The two idioms are 'child's play' & 'next to'. The words are correct synonyms but the usage is unusual enough that I read as having a totally different meaning my first time.

I was also confused by it. However I read the last sentence as the author making a meta-point by using purposefully incorrect grammar, a sort of reversal of the first sentence about c++ code compiling but leading to segfault--like an artistic statement. If it was an accident, then all the better!
:)

English is not my first language, and it is a rare construct. I thought it was correct.

Out of curiosity, how should it read?

I believe the common form is "X is child's play (next to|compared to) Y".
Don't worry too much, I always enjoy little gotcha's like this. It's like your brain has to think twice to understand the meaning, but in the end it does. It's really fun and colourful to read half modified phrases and that's how language evolves.
>didn't require the annoying C debugging cycle

Are we talking C or C++ ? C compiler is basically useless for compile time error checking because C type system is ridiculously weak. C++ on the other hand can buy you a lot of things with templates, richer type semantics, etc.

And also keep in mind that C++11 and onward is very different from C++ of yore and it catches a lot of errors at the compile time - move semantics and RAII really buy you a lot - the downside is that it's opt-in so the compiler won't force you - and it's less strict than say Rust, but it still gets you there 90% of the way.

Both languages have approximately the same problems for debugging. Explicit memory references are hard to follow, lack of overflowing protection requires a lot of extra caution to catch the errors, and failures lead to an unforgiving stop with at most a core dump. C++ is much better for statically catching errors, but templates and copy/move semantics make it even harder to debug.

And yes, I imagine C++11 is much better. Unfortunately, I didn't write anything big in C++ since then.

You avoid explicit references in C++11 and use STL containers which check for overflow.

I agree that C++ can have the same issues but the frequency is not comparable because you don't drop to those unsafe parts if you don't have to.

So with C++14 I get that "if it compiles it runs" feeling comparable to say C#.

You really owe it to yourself to give Rust a shot. A lot of things that you've encoded in convention(as any good C++ programmer should) are right there at the language level.

Ownership? You get an awesome sliding scale of Borrow Checker <-> Boxed <-> Rc/Arc

Mutation? Covered in Copy+Clone/Cell <-> &/&mut <-> RefCell

Thread Safety? Sync + Send guarantee that things which need to stay on a specific thread cannot be shared.

Combine that with Sum Types, Pattern Matching and much stronger functional constructs makes for a very compelling language.

Rust does sound interesting, but it doesn't seem to be focused on the areas that I need right now.

Go has GoRoutines, which are light threads; they can be executed on multiple CPU threads, but by default Go only allocates one CPU thread per physical CPU, even if you allocate tens of thousands of GoRoutines.

For the kind of networking server code I'm writing, light threading is far more efficient than using an OS thread per connection. 25-50x faster at higher server loads.

That's why I've ignored Rust to date; if Rust has light threading built in, but no one is talking about it (haven't found anything but real threads in my quick Google searches), then I'll take a look. Otherwise it will need to wait for me to have a task that Rust would be good for, and I'll stick with Go and TypeScript for the problems I'm solving right now.

Ah, sounds like Erlang/Elixir would be a better fit since the BEAM is the king of lightweight threads/processes.

You said C++ so that's why I suggested Rust, generally a GC tends to preclude spaces where C++ excels which is why I would think you'd want to try something other than Go.

On the Rust side there's things like mio for fast io but coroutine/light threads aren't really a focus afaik. I would still think you could get solid performance (and much better memory use, see Dropbox's replacement of Go) with an event based Rust system although that's not really my area of expertise :).

I am sure that a Rust team member will be here soon to set the record straight, however, my take:

> Rust side there's things like mio for fast io but coroutine/light threads aren't really a focus afaik

IO and concurrency/parallelism models are a very difficult topic to really discuss and quantify performance around.

The highest-known-performance network server architecture revolves around edge-triggered epoll/kqueue (evented, async IO) and state machines (eschewing the stack entirely.)

pthreads (speaking exclusively about POSIX here - Windows threads are far heavier-weight than pthreads) suffer the cost of context switching due to large (by default - this can be configured) stack sizes.

This is one reason why languages like Go and Erlang have their own stack and lightweight process (greenthread) implementations.

For the record: Rust had green threading, and I believe it was removed due to the overhead relating to supporting it. The simple fact that you really cannot control or know when FFI calls will block (libc included) or not makes greenthreading tricky.

The trickier thing, IMO, relating to async IO is just the compatibility aspect:

- Golang/Erlang control compatibility by offering entire ecosystems built around their greenthread models.

  - Golang even goes as far as replacing the vast majority of libc and much of what you would want to call out to C for.
- Rust, being a systems language, cannot dictate "all code must use async IO and this specific reactor/event loop implementation," the stdlib is too small (by design), calls to libc are too pervasive.

With all of that being said:

- I personally believe Rust core should adopt a blessed async IO system (event loop, OS-level abstraction layers, basically mio) that all code could opt into using. - Additionally, it would be lovely if, on top of said system, compiler support for stackless coroutines (a la async/await) and a multithreaded reactor were implemented.

I love Rust, but I can't use it for network server related tasks until this part of the ecosystem is more developed and made more consistent.

mio is the de facto stdlib AIO implementation, but there's still a lot of fragmentation, and many libs still use the blocking networking builtins, rendering them incompatible.

I've been reading about Elixir recently, yes. Thanks for mentioning it, though. I'll have to look at it at some point, but since my code is primarily run-in-a-browser, I'm currently stuck with compile-to-JavaScript languages. I'm concerned that Elixir is too functional for my tastes (I find Haskell to be too pure (and slow) to be really usable for most tasks I work on, for instance), but I will give it a look at some point.

The entire point of writing in Go or Elixir would be to avoid needing to write an event based system in Rust (or C). Writing code as if it's imperative but getting the speed of an event-based system is what I'm looking for. Event-based systems are notoriously hard to reason about and debug.

You just can't be happy if someone else is using Go, can you.
The parent is already experienced in Go and expressed interests in other languages, excuse me for indulging them :).
> But I used the language for nearly 20 years, so maybe it's experience and the patterns I've learned?

It definitely is. This is not the usual C++ experience. You are probably sidestepping all sorts of undefined behavior due to experience alone.

This matches my experience with C++. My code has improved hugely in simplicity and robustness as I've learned which C++ features are productive and which are just minefields.
> Huh. I find that when my C++ compiles, it almost always works. Correctly. > > But I used the language for nearly 20 years, so maybe it's experience and the patterns I've learned?

By contrast, I learnt OCaml and Haskell 2 weeks ago, and when my code in those languages compiles, it always works. I expect I'll have the same experience with Rust.

Funny. I managed to get Haskell to crash within about 20 minutes of first trying it out.

OCaml is supposed to be pretty fast, but so far Haskell hasn't impressed me with performance either.

What code did you write?
I wrote some code that iteratively would have just taken a while to run, but because Haskell wasn't using true tail recursion, it ran out of stack space.

I also read a paper that talked about extensive research into optimizing a convolutional network algorithm in Haskell, if I remember correctly. The algorithm even used mutable data structures for speed (because of course you need data to be mutable for speed). Roughly speaking, if you ran it on 16 CPUs, it was only 4x slower than the C algorithm running on one CPU. And Amdahl's Law [1] (as well as the graph in the article) hints that it may never actually be faster than the single CPU C version, no matter how many CPUs you split it onto, because of the overhead of sending the data around.

When people claim "immutability makes it easier to run on more threads, which is the future of optimization!" I just want to cringe. Immutability kills most performance enhancements you can possibly make on nontrivial code, even considering a single thread, having all your data be immutable does nothing at all to improve threading, and forcing all data transferred to another thread to be immutable kills a whole category of optimization.

When dealing with large amounts of data, you save tons of time by not copying it around.

/rant

Sorry. I've been listening to too many people talk about Haskell like it's an amazing silver bullet that will solve all of our problems. But There Is No Silver Bullet. Really.

[1] https://en.wikipedia.org/wiki/Amdahl's_law

Well, FWIW worth that's not the primary reason I like those languages. It's the safety.

After some experience with immutable data structures, you know how to make them efficient. For example most of the time you don't need to copy that much data - if you need to change a small part of a large data structure, you arrange it so you can re-use (re-point to) the parts you didn't change. On average you should be able to reduce your cost down to O(log(n)) which usually is "good enough".

That is the correct trade-off IMO - have a newbie start off writing safe code, that gets quicker and less memory intensive with experience. Not start off be blazing quick but unsafe, then add the safety with experience.

Did you know Rust (which defaults to immutable) is currently head-to-head with C in Debian's "fastest languages" comparison?

https://benchmarksgame.alioth.debian.org/u64q/rust.html

Haskell is beautiful - including the syntax and typesystem, but the performance claims made for it are ridiculous.

I switched to Ocaml to avoid the un-evalauted thunk and space complexity overhead.

Ocaml also doesn't compete with well written C or C++, but it's a lot more performant than Haskell for general purpose code.

Something like `main = head []` probably, lol.
It is a bug in the compiler. (Old joke).
I thought it was a bug in the specification? (Older joke.)
I have a marvelous proof that this bug does not exist, but the heap is too small to contain it.