4 ms·
If it isn't a vDSO call, I think 50ns figure shouldn't be possible.
by menaerus 1y ago
If it isn't a vDSO call, I think 50ns figure shouldn't be possible.
- deleted 1y ago[deleted]
- ori_b 1y agoNo need to guess, it's 10 lines of code. And you can use bpftrace to watch the test program enter the kernel. Using the libc wrapper will use the vdso. Using syscall() will enter the kernel. I haven't measured, but calling the vdso should be closer to 5ns. Someone else did more detailed measurements here: https://arkanis.de/weblog/2017-01-05-measurements-of-system-call-performance-and-overhead/ https://arkanis.de/weblog/2017-01-05-measurements-of-system-...
- menaerus 1y ago50ns on a 3GHz CPU core is ~150 cycles. Pushing and popping back the registers to L1 cache is 5-10 cycles each. With having to handle 16 general purpose registers on x86-64 this is already close to or even more than 150 cycles, no?
- ori_b 1y agoWhen you measure, what numbers do you get? Also: register renaming is a thing, as is write combining and pipelining. You're not flushing to L1 synchronously for every register, or ordinary userspace function calls would regularly take hundreds of cycles for handling saved registers. They don't.
- menaerus 1y agoI'm on my mobile. Store to L1 width is typically 32B and you're probably right that CPU will take advantage of it and pack as much registers as it can. This still means 4x store and 4x load for 16 registers. This is ~40 cycles. So 100 cycles for the rest? Still feels minimal.
- ori_b 1y agoA modern x86 processor has about 200 physical registers that get mapped to the 16 architectural registers, with similar for floating point registers. It's unlikely that anything is getting written to cache. Additionally, any writes, absent explicit synchronization or dependencies, will be pipelined. It's easy to measure how long it takes to push and pop all registers, as well as writing a moderate number of entries to the stack. It's very cheap. As far as switching into the kernel -- the syscall instruction is more or less just setting a few permission bits and acting as a speculation barrier; there's no reason for that to be expensive. I don't have information on the cost in isolation, but it's entirely unsurprising to me that the majority of the cost is in shuffling around registers. (The post-spectre TLB flush has a cost, but ASIDs mitigate the cost, and measuring the time spent entering and exiting the kernel wouldn't show it even if ASIDs weren't in use)
- menaerus 1y agoWhere is the state/registers written to then if not L1? I'm confused. What do you say about the measurements from https://gms.tf/on-the-costs-of-syscalls.html https://gms.tf/on-the-costs-of-syscalls.html? Table suggests that the cost is by a magnitude larger, depending on the CPU host, from 250 to 620ns.
- ori_b 1y agoThe architectural registers can be renamed to physical registers. https://en.wikipedia.org/wiki/Register_renaming https://en.wikipedia.org/wiki/Register_renaming As far as that article, it's interesting that the numbers vary between 76 and 560 ns; the benchmark itself has an order of magnitude variation. It also doesn't say what syscall is being done -- __NR_clock_gettime is very cheap, but, for example, __NR_sched_yield will be relatively expensive. That makes me suspect something else is up in that benchmark. For what it's worth, here's some more evidence that touching the stack with easily pipelined/parallelized MOV is very cheap. 100 million calls to this assembly costs 200ms, or about 2ns/call: f: .LFB6: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $8, %rsp movq $42, -128(%rbp) movq $42, -120(%rbp) movq $42, -112(%rbp) movq $42, -104(%rbp) movq $42, -96(%rbp) movq $42, -88(%rbp) movq $42, -80(%rbp) movq $42, -72(%rbp) movq $42, -64(%rbp) movq $42, -56(%rbp) movq $42, -48(%rbp) movq $42, -40(%rbp) movq $42, -32(%rbp) movq $42, -24(%rbp) movq $42, -16(%rbp) movq $42, -8(%rbp) nop leave .cfi_def_cfa 7, 8 ret