Hacker Newsnew | past | comments | ask | show | jobs | submit | higherhalf's commentslogin

clock_gettime() goes through vDSO, avoiding a context switch. It shows up on the flamegraph as well.


Only for some clocks (CLOCK_MONOTONIC, etc) and some clock sources. For VIRT/SCHED, the vDSO shim still has to invoke the actual syscall. You can't avoid the kernel transition when you need per-thread accounting.


Oh for some time after its introduction, CLOCK_MONOTONIC_RAW wasn't vDSO'd and it took some time and syscall profiling ('huh, why do I see these as syscalls in perf record -e syscalls' ...) to understand what was going on.


Thanks, I really should've looked deeper than that.


no problem at all, I was confused too when I saw the profile for the first time.


If you look below the vDSO frame, there is still a syscall. I think that the vDSO implementation is missing a fast path for this particular clock id (it could be implemented though).


Exactly this.


edit: agh, no. CLOCK_THREAD_CPUTIME_ID falls through the vdso to the kernel which makes sense as it would likely need to look at the task struct.

here it gets the task struct: https://elixir.bootlin.com/linux/v6.18.5/source/kernel/time/... and here https://elixir.bootlin.com/linux/v6.18.5/source/kernel/time/... to here where it actually pulls the value out: https://elixir.bootlin.com/linux/v6.18.5/source/kernel/sched...

where here is the vdso clock pick logic https://elixir.bootlin.com/linux/v6.18.5/source/lib/vdso/get... and here is the fallback to the syscall if it's not a vdso clock https://elixir.bootlin.com/linux/v6.18.5/source/lib/vdso/get...


Seems a little complicated when you can do:

  //usr/bin/env rustc "$0" && ./config "$@"; exit
Or

  //usr/bin/env rustc --edition 2024 "$0" && ./$(basename $0 .rs); rm $(basename $0 .rs); exit


&& in a shebang isn't portable, having more than one argument in a shebang isn't portable, subshell in a shebang isn't portable, semicolon in a shebang isn't portable, basename usage isn't portable, not littering the current working directory is important (it might not even be RW, or you might not have permissions to write to it), your usage won't work with symlinks or with the script added to $PATH, caching the compiled output is a nice speedup and reduces startup costs, correct (and optimal) cache invalidation is one of the known tricky problems in CS.

Shell scripts are ugly :)

(If you didn't mean to use them in a shebang but rather as the script body, that's fine but that wouldn't be possible without the polyglot syntax and #allow abuse I posted.)


Thanks, I looked it up and it's indeed quite ugly, and my shebang isn't portable. A little saddened that even 'env -S' didn't make it to POSIX.


No worries and believe me, I feel your pain (I moonlight as a (fish-)shell dev).

I would have liked to use `exec -a` to set the executable name (as mentioned in the comments on the article) but alas, that too is not portable.


> It doesn't allow you to import `std::iter::Iterator::collect` on its own. It's an associated function, and needs to be qualified.

You probably noticed, but it should become a thing in RFC 3591: https://github.com/rust-lang/rust/issues/134691

So it does kind of work on current nightly:

  #![feature(import_trait_associated_functions)]
  
  use std::iter::Iterator::{filter, map, collect};

  fn get_ids2(data: Vec<Widget>) -> Vec<Id> {
      collect(map(filter(Vec::into_iter(data), |w| w.alive), |w| w.id))
  }  

  fn get_ids3(data: impl Iterator<Item = Widget>) -> Vec<Id> {
      collect(map(filter(data, |w| w.alive), |w| w.id))
  }


Oh, interesting! Thank you, I did not know about that, actually.


What makes this a "systems programming language", especially since it has "no pointers or references"?


Indeed, that makes it not a systems language by any definition I am familiar with.

> Reasonable C interop, and probably, initial compilation to C.

How do you achieve "reasonable C interop" without pointers, I wonder?


You cast integers to pointers and play with fire, of course!


Void*? Int. Char**? Also an Int. I take it back. This is true systems programming - the same type safety that raw assembly is known for.


Ironically Assembly is safer than C and languages that descend from it, because although CPUs might have undefined behaviour when given undocumented opcodes, or operation modes, the CPU doesn't rewrite your code without telling you about it.


That's "safer" only against a very specific and limited set of dangers. But it opens the door to other dangers.


Yes it is. Great comment.


Naturally with PEEK and POKE.


I wonder the same, but aren't pointers just integers?

So if you store a memory address in the integer variable X, you just need a way to access that memory.

In assembly languages, usually, you have no pointers.


Interestingly although all of C's other types are in fact just the machine integers wearing funny hats (e.g. char is just either a signed or unsigned byte depending on platform, float is just the 32-bit unsigned integers as binary fractions) the pointers are not actually just integers.

They could be, but it's much worse from a performance perspective if you just have these raw machine addresses rather than the pointers in the C language so actual C compilers haven't done that for many years. ISO/IEC TS 6010 describes the best current attempt to come up with coherent semantics for these pointers, or here's a Rustier perspective https://www.ralfj.de/blog/2020/12/14/provenance.html [today Rust specifically says its pointers have provenance and what that means, like that TS for the C language]

Now, if you read Ralf's post and want to argue about that I'm afraid there are already lots of HN discussions and your point has probably already been made so: https://news.ycombinator.com/item?id=25419740 or https://news.ycombinator.com/item?id=42878450


> float is just the 32-bit unsigned integers as binary fractions

Note that float and double are a bit particular because they can use different registers! But yeah, when stored in memory they are the same 32/63 bit integers.


Like we did in BASIC, with PEEK and POKE, just have to keep track what those numbers are for.


The term was introduced so long ago, it's basically prehistoric now. Pointers are needed only for system programming language, they can be absent in systems programming language.


  int ptr


It's not due to a move to AI-enhanced workflows. The article speculates that it's caused by companies wanting to develop AI-related products, moving some workforce and culling the unwilling, just like my employer.


This can be done even simpler:

  global _start
  _start:
      jmp next
  string:
      db `Hello World!\n`
  len: equ $ - string
  next:
      mov ecx, string
      mov edx, len
      mov ebx, 1
      mov eax, 4
      int 80h
  
      mov ebx, 0
      mov eax, 1
      int 80h
For NASM, it can also be put into a macro, for example printing to video memory at 0xb8000:

  %macro print 1
      mov ecx, %%loop_start - %%strdata
      mov eax, 0x0700
      jmp %%loop_start
  %%strdata: db %1
  %%loop_start:
      mov al, [%%strdata + ecx - 1]
      mov [0xb8000 + ecx * 2 - 2], ax
      loop %%loop_start
  %endmacro


the author wanted it to be position-independent (PIC), so it works no matter what address the .text segment is loaded to and run.

This example uses a fixed symbolic reference ("string:") and is the normal way to do it. The trick is to it in a PC relative way.


That will require a fixup or a fixed load address. The example in the article is position independent.


Yes. Kind of.

AMD processors have Upper Address Ignore (UAI) with 7 bits of ancillary data, for Intel there is Linear Address Masking (LAM) with 6 or 15 bits [1]. The latter has made it into Linux after some initial resistance [2].

[1]: https://lwn.net/Articles/888914

[2]: https://www.phoronix.com/news/Intel-LAM-Merged-Linux-6.4


Those sound analogous to the TBI feature in Arm, rather than MTE itself (which uses TBI).


They mention git worktrees in the next paragraph.


It's a joke, as some Rust projects used to repetitively claim to be "memory safe & blazing fast", thus becoming a tongue-in-cheek phrase. I also anticipated language to be Rust, but it would be way too comical.

https://github.com/mTvare6/hello-world.rs


> But rust absolutely does not have any C/C++ compatibilty besides [..]

> In zig you _can_ actually start with a c codebase and rewrite it file by file in zig and you _can_ include c headers in your zig files verbatim. Both of these are not possible in rust.

You can. Either via FFI and bindgen-ing headers, or by using c2rust. The latter is not just a toy ambitious project, but actually a very impressive piece of engineering and does produce a result where you can transpile a project or file and start rewriting file-by-file or function-by-function.


c2rust is definitely cool, but it also supports transpiling to a single architecture, which often misses a lot of architecture dependent code and specialisations in real world c code. Especially because it has to do macro expansion and you only get the expanded code.

It also doesn't supportal a good amount of more complicated c features.

It's a help for sure, but the few times I tried I ended up just doing the rewrite by hand instead to actually cover all the cases.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: