Live data from Hacker News

Things Rust shipped without

graydon2.dreamwidth.org

261–270 of 330 posts

Re: Things Rust shipped without

#261

Earlier quoted context omitted.

Yes. The Rust std library had to pick a string encoding, and it picked UTF-8 (which is really the best Unicode encoding). The String type is platform neutral and always UTF-8. However, it does provide an OsString type, which on windows is UTF-16. Maybe there is a library - and if not, one could be written - targeting Windows only, and implementing stronger UTF-16 string processing on the OsString type. EDIT: To be cl…

I believe Rust uses WTF-8 as an intermediate format for windowsy things (cheaper), but I'm not sure.

What is... oh... UTF-16, the gift that keeps on giving... this is, at the same time, utterly hilarious and horribly depressing:

https://simonsapin.github.io/wtf-8/

But there is actually prior art here - Java's contribution to perverse Unicode encodings is called "Modified UTF-8" and encodes every UTF-16 surrogate code unit separately.

http://docs.oracle.com/javase/6/docs/api/java/io/DataInput.h...

Re: Things Rust shipped without

#262
post #179

Earlier quoted context omitted.

For servers that primarily speak RPC or HTTP, do you foresee Rust going thread-per-request or something more callback-y?

Most applications right now should do thread-per-request. Thread spawning is very optimized in both Rust and the Linux kernel, and you can adjust stack sizes if you need to. If you're hitting limits caused by this, you can use mio.

What about systems other than Linux?

Re: Things Rust shipped without

#263
post #68

Earlier quoted context omitted.

A bitecode interpreter is another place where it's nice to have gotos. Here's the base code without gotos: typedef enum { ADD, MUL, ..., END } opcode; void run() { opcode ins; while (1) { ins = fetch_next_inst(); switch (ins) { case ADD: perform_addition(); break; case MUL: perform_multiplication(); break; ... case END: wrap_up(); return; } } } You have 3 jumps on each loop. From the break to the end of the loop, the…

I really dislike 'clever' code like this, even when speed is paramount you will find that by obscuring the flow you make it harder, not easier to really optimize the code. Over time it tends to evolve into ever messier and harder to understand versions of the initial run after which a future maintainer will end up losing sleep and or hair chasing some production bug. Consider this (slower!) much clearer alternative,…

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.

Re: Things Rust shipped without

#264

Earlier quoted context omitted.

I do not use goto in my code. What do I do wrong? :) Ok, I have to admit that I used it on C64 in the 80s. Anyways, exceptions are sort of gotos or at least they can behave that way.

If you're not implementing fast FSMs, fast bytecode interpreters (and dynamic dispatchers in general), and you're not using metaprogrming to the full power, not implementing multiple embedded DSLs - then you can live without a goto. Otherwise it is essential.

Metaprogramming to the full power sounds like Lisp. It doesn't have goto.

Re: Things Rust shipped without

#265
post #6

> goto (not even as a reserved word) I haven't done this for a while, but once upon a graduate program I wrote a compiler from a made-up-language (MUP) to C. MUP had some strange control structures, and if C did not have "goto", it would have been a lot more difficult to implement those structures. Since then, I have always thought languages should have a "goto" statement that human-written code is not allowed to use…

I only used goto in ZX Spectrum Basic, GW Basic and several Assembly flavours.

All its use cases are better served by other language constructs.

Then again, C is a portable macro assembler.

Re: Things Rust shipped without

#266
post #97

Earlier quoted context omitted.

Yeah, that's one thing that's really putting me off looking at Rust - the possibility of it enforcing style in the future. Hanging braces style for C/C++ is rarely allowed in the coding standards I've had to use in the past for embedded and real-time stuff in the defence industry, because it can be a source of errors. Aligned opening and closing braces are much more common (in line with ADA's style).

Rust doesn't enforce style anywhere. You can turn off all style warnings at once too with `#![allow(bad_style)]` or a command line flag. By default it just warns. That's not enforcing.

Come on, even that flag's name is ridiculous. They basically say, for example, that writing camel case, which is the preferred style in most of the projects I've seen, is bad style. Do they like being unnecessarily antipathetic? People think about their personal preferred style for years, and they have very good reasons for choosing them. And in _many_ cases, it's exactly the style that's called bad by Rust's creators.

How about making it the opposite, '#![allow(default_style)]'? Or at least '#![allow(non_default_style)]'?

Re: Things Rust shipped without

#267

Earlier quoted context omitted.

I really dislike 'clever' code like this, even when speed is paramount you will find that by obscuring the flow you make it harder, not easier to really optimize the code. Over time it tends to evolve into ever messier and harder to understand versions of the initial run after which a future maintainer will end up losing sleep and or hair chasing some production bug. Consider this (slower!) much clearer alternative,…

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.

Re: Things Rust shipped without

#268

Earlier quoted context omitted.

Before you use such constructs make sure your team lead or boss is ok with it, it's incompatible with many compilers and ugly to boot. This is the kind of code that gives C its bad reputation.

This is the kind of code which makes OCaml the fastest bytecode interpreter out there, and selection between an ad hoc switch and a comuted goto is done transparently with a single ifdef.

Yes, there may be exceptions when this kind of code is preferable. But it's definitely not the rule and the speed difference between the one and the other is so small that only a profiler can guide you to optimizations like these.

Re: Things Rust shipped without

#269

Earlier quoted context omitted.

Okay, but a much more readable and maintainable way to achieve this same optimization is with function pointers. In your example: void (*[10])() function_table = { &perform_addition, &perform_multiplication, ... } while(1) { ins = fetch_next_inst(); *(function_table[ins])(); } Edit: Seeing it written like this now, it's clear you could save yet another jump by defining a macro for what's in the while loop and putting…

That's an additional pointer-chase per loop. And more function prefix / suffix work. In actuality, I suspect that it'd be optimized out - but you cannot say "you cannot do X because it relies on compiler optimizations" and replace that with Y that relies on compiler optimizations.

A function call is nothing more than putting your return address and parameters on the stack and jumping to the address of the function. By referring directly to the function's address, there's no "additional pointer chase", since calling the function already does exactly that.

If you were to inline all of the perform functions in the GOTO version vs putting a macro at the end of each function, you're right that there's some function overhead, but I think it's as small as a single instruction to put the return address on the stack. Maybe that would be optimized away, maybe not.

To your point: My argument isn't that you can't do the GOTO version. With optimizations, it's essentially identical. My point is, that is a lot more hard-to-grok code to maintain for something that can be achieved in a simpler way.

Re: Things Rust shipped without

#270

Earlier quoted context omitted.

This is the kind of code which makes OCaml the fastest bytecode interpreter out there, and selection between an ad hoc switch and a comuted goto is done transparently with a single ifdef.

Yes, there may be exceptions when this kind of code is preferable. But it's definitely not the rule and the speed difference between the one and the other is so small that only a profiler can guide you to optimizations like these.

> Yes, there may be exceptions when this kind of code is preferable.

In my practice these exceptions are ubiquitous. Multiple tiny interpreted DSLs (which cannot be compiled more efficiently for the latency reasons), efficient protocols, all that stuff. System programming, in other words, and it's exactly the stuff Rust was supposed to be designed for.

> But it's definitely not the rule and the speed difference between the one and the other is so small

2-4 times difference is not "small" in system-level programming.

Post reply on HN