Live data from Hacker News

Things Rust shipped without

graydon2.dreamwidth.org

311–320 of 330 posts

Re: Things Rust shipped without

#311
post #163
post #139

Earlier quoted context omitted.

How do you imagine that reflection would be useful for serialization in Rust?

In the way I asked here, http://stackoverflow.com/questions/29109967/why-dont-many-co... I am not very familiar with rust, is what I was describing currently possible?

It's not possible, but there's also no good reason to do it. You might as well just make an e.g. Serializable trait that requires you to manually implement the serialize function. You're not saving a whole lot of work by looping through the object's fields and saving them. Most objects only have a few fields to serialize anyway.

Re: Things Rust shipped without

#312

Earlier quoted context omitted.

I imagine that if you have more instructions (a few hundred) the overhead of the switch starts to become significant. At 256 opcodes you need 8 jumps, and hence 8 CPU cycles. That's non-trivial, especially if most of the opcodes can be implemented more quickly than this. For example, a quick Google search says that for an Intel CPU, addition is 1 cycle and multiplication is 3 cycles.

Switches over enums are usually compiled to jump tables. What you see in source and what you get after the optimizer is done with it are sometimes very far apart. Looking at the output of gcc -S can be very enlightening at various levels of optimization.

[deleted]

Re: Things Rust shipped without

#313

Earlier quoted context omitted.

I imagine that if you have more instructions (a few hundred) the overhead of the switch starts to become significant. At 256 opcodes you need 8 jumps, and hence 8 CPU cycles. That's non-trivial, especially if most of the opcodes can be implemented more quickly than this. For example, a quick Google search says that for an Intel CPU, addition is 1 cycle and multiplication is 3 cycles.

Switches over enums are usually compiled to jump tables. What you see in source and what you get after the optimizer is done with it are sometimes very far apart. Looking at the output of gcc -S can be very enlightening at various levels of optimization.

Since you asked, there's an assembly comparison on pages 5-6 of http://www.jilp.org/vol5/v5paper12.pdf and a performance comparison on page 12 of http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.90.... . Note that the "inline threading" in the second paper is a further optimization that hasn't been brought up here yet.

In summary, the assembly of the goto version is much shorter and there's a 10% speedup over SPEC JVM98. The optimization works out because: a) instruction bodies are often short; b) instruction dispatch is the hottest code path of any interpreter; c) it's orthogonal to all or almost all other optimizations.

Re: Things Rust shipped without

#314

Earlier quoted context omitted.

An offset from the first label, or some nearby alignment point? Makes sense. Do you have a pointer to the relevant part of OCaml's source? The whole thing is probably worth a read, but don't think I can set aside enough time for that soon, and it could take me a while to find the right part.

https://github.com/ocaml/ocaml/blob/trunk/byterun/interp.c See the "Next" macro definition.

Nice. Simple and efficient. I am going to read more from that code base. Looks like this might be a nice read as well: https://ocaml.org/docs/papers.html#BytecodeCompilerandByteco...

Re: Things Rust shipped without

#315

Earlier quoted context omitted.

The compiler implementing the macros (a form of compile-time meta-programming) should be the same compiler as that for the rest of the language.

> should be the same compiler as that for the rest of the language Sorry, I do not understand what are you talking about. Of course it is the same host compiler. But, every macro itself is a small compiler. Sometimes, when your DSL is an elaborate, complex thing, the macro itself is a complicated, big compiler. With all the bells and whistles of a big compiler - multiple stages, multiple intermediate representations,…

    But, every macro itself is a small compiler.
I'm not following you here. What does that mean? The macro gets expanded at compile-time into 'normal' source code, by the compiler.

Re: Things Rust shipped without

#316

Earlier quoted context omitted.

Switches over enums are usually compiled to jump tables. What you see in source and what you get after the optimizer is done with it are sometimes very far apart. Looking at the output of gcc -S can be very enlightening at various levels of optimization.

Since you asked, there's an assembly comparison on pages 5-6 of http://www.jilp.org/vol5/v5paper12.pdf and a performance comparison on page 12 of http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.90.... . Note that the "inline threading" in the second paper is a further optimization that hasn't been brought up here yet. In summary, the assembly of the goto version is much shorter and there's a 10% speedup over…

That's about what I expected, contrary to the 2-4x times speedup mentioned above. Still, I can imagine in isolated cases you really would get a better number than that 10%. So, for code where performance is super important and where the effort of the optimization and lack of transparency is outweighed by the performance boost this makes sense.

Re: Things Rust shipped without

#317

Earlier quoted context omitted.

> should be the same compiler as that for the rest of the language Sorry, I do not understand what are you talking about. Of course it is the same host compiler. But, every macro itself is a small compiler. Sometimes, when your DSL is an elaborate, complex thing, the macro itself is a complicated, big compiler. With all the bells and whistles of a big compiler - multiple stages, multiple intermediate representations,…

But, every macro itself is a small compiler. I'm not following you here. What does that mean? The macro gets expanded at compile-time into 'normal' source code, by the compiler.

> I'm not following you here.

Any marginally non-trivial macro is a compiler, by definition.

> The macro gets expanded at compile-time into 'normal' source code, by the compiler.

Macro expands a DSL inside it into the underlying host meta-language code. In other words, it compiles this DSL into the host language.

For example, a macro which defines a parser. Inside it is a BNF (or PEG), a very high level language. Macro must compile this language into Rust, and, since it is a very high level language, there are tons of optimisation opportunities that would be totally missed by the underlying Rust and LLVM because they lack this domain-specific knowledge.

Your macro will compile this source DSL in multiple stages (well, because this is the only sane approach to compilation anyway, read about the Nanopass framework for more details). First it will operate on an AST level, do some analysis, error reporting, inlining, may annotate the detected left recursive nodes and binary expression nodes. Then you'd lower it down into the trivial Packrat building blocks - still a tree. But now you notice that there is a lot of redundant reads from the input stream, and if you flatten this tree into an SSA you can optimise them all away.

Alas, after such an optimisation you'd have to promote it back to tree in order to generate Rust, because there is no `goto`.

And this is only one trivial example. In my practice there were dozens such DSLs. For example, same story is with an optimising WAM-based embedded Prolog DSL, multiple querying DSLs, tree walking DSLs (which are essential for implementing macros efficiently).

Re: Things Rust shipped without

#318

Earlier quoted context omitted.

> should be the same compiler as that for the rest of the language Sorry, I do not understand what are you talking about. Of course it is the same host compiler. But, every macro itself is a small compiler. Sometimes, when your DSL is an elaborate, complex thing, the macro itself is a complicated, big compiler. With all the bells and whistles of a big compiler - multiple stages, multiple intermediate representations,…

But, every macro itself is a small compiler. I'm not following you here. What does that mean? The macro gets expanded at compile-time into 'normal' source code, by the compiler.

A compiler takes a text from one language and translate it into text of another executable language.

Re: Things Rust shipped without

#319
post #66

Earlier quoted context omitted.

Tail call optimization https://mail.mozilla.org/pipermail/rust-dev/2013-April/00355... https://github.com/rust-lang/rust/issues/217 > I'm sorry to be saying all this, and it is with a heavy heart, but we tried and did not find a way to make the tradeoffs associated with them sum up to an argument for inclusion in rust. > -Graydon

Note that a lot has changed since April of 2013. Rust does have TCO, we just can't guarantee it. And, LLVM has come a long way, so we probably can support guaranteed TCO now, and have 'become' as a reserved keyword for this purpose. https://github.com/rust-lang/rfcs/issues/271 is a better link today.

I praise the idea of making TCO explicit and guaranteed, but wonder if whole new keyword is necessary? It could be avoided by modifying `return` with another keyword which is currently impossible at that place, for example

    return as foobar(x, n-1)
    
    return in foobar(x, n-1)

    override return foobar(x, n-1)

    final return foobar(x, n-1)
It's a little surprising to see entirely new construct invented for something that is just a different implementation of `return`, after all, the end result of computation with tail-return is the same as with normal return, only performance differs.

Re: Things Rust shipped without

#320

Earlier quoted context omitted.

Note that a lot has changed since April of 2013. Rust does have TCO, we just can't guarantee it. And, LLVM has come a long way, so we probably can support guaranteed TCO now, and have 'become' as a reserved keyword for this purpose. https://github.com/rust-lang/rfcs/issues/271 is a better link today.

I praise the idea of making TCO explicit and guaranteed, but wonder if whole new keyword is necessary? It could be avoided by modifying `return` with another keyword which is currently impossible at that place, for example return as foobar(x, n-1) return in foobar(x, n-1) override return foobar(x, n-1) final return foobar(x, n-1) It's a little surprising to see entirely new construct invented for something that is ju…

In theory, we could have done that too, and I guess we still could.
Post reply on HN