deadcanard
deadcanard on Hacker News
Posted by
deadcanard on Monday December 11, 2023 @10:19AM
from the hudsonrivertrading.com dept.
Take this with a hefty grain of salt. This a one-author paper from a MD that got their license suspended by MA's medical board. The author also pushes some anti vax rhetoric on their website.
That does not mean the claims in the paper are incorrect of course. But there are reasons to be skeptical
2) DDIO must write its data somewhere in the L3 cache. It ends up in the shareable slice. So either you're okay with sharing your cache or cannot use these slices if you want exclusive access for your processes. That was my point.
3) CAT does not recognize processes but resctl does. Feels we're kinda nitpicking here...
Last of your point: Yeah, that gives you 9ish usable slices which is not very much. Again that was my point.
Again, I am biased. But the article explains mem translation in fairly simple terms, hammers the main advantages of HPs (better use of the TLB, simpler and smaller PT). Explains clearly what how much mem the TLB can cover, what a page walk is and how much time it takes (before even loading actual data), the importance of the cache wrt PT. It shows some perf numbers of random vs iterative mem accesses.
I don't think you'll find many articles that detail these points. Now, they might be trivial to you and that's totally fair. But the goal is to address a wide audience. Additionally, the article is not addressing how to use HPs but that's for part 2.
Wrt to other points, I certainly agree they are important topics to explore. I would add using perf is super important to easily access the perf counters
IMO the easiest way (but certainly not the only way) is to allocate a new stack and switch to it with makecontext(). The manpage has a full code example. You just need to change the stack alloc. This approach has a few drawbacks but is hard to beat in terms of simplicity.
I'd argue that understanding what happens on every single memory access qualifies as fundamental.
I am biased but I don't think it's fair to say that your article covers as much. There is more content in the article written in a way that's trying to be approachable. I certainly will agree it's wordy but it's hard to make content that's interesting to a wide audience
Your article does cover how to use/enable them while the post does not. But it's meant for part 2
Wrt code, look at the bench in the article. Even with sequential access, you can get a decent speedup using huge pages. But unless you have a good profile and using PGO, it'll likely not be that sequential for code.
Like everything else you'll need to measure it to know exactly what benefit you might get. As a starting point, you can start at looking at the itlb load misses with perf stat -d
Stack access is another story as it's usually local and sequential so it might not be that useful.
CAT is indeed a good thing to look at. But there are some important caveats 1) unless you have a very small number of cores, it's not possible to reserve a cache slice for all programs running (some slices are shared for things like DDIO), 2) it's still not possible to lock some specific data in the cache because any collision will replace the data 3) the slices are kinda big, so it's hard to be properly fine grained.
Basically, CAT just prevents other processes from stealing all the cache. It does that by reserving ways (as in the cache associativity meaning)
Then, instantiate the smart pointer with a custom no-op deleter and if you use the nt typedef I mentioned above, you'll get a similar compiler output to Rust's.
e.g nt_shared_ptr<Data> x(&d, [](Data*) -> void{});
https://godbolt.org/z/d3qv4cE1v
It's because shared_ptr uses an atomic count to synchronise between threads while the Rust version is assuming only one thread.
There is no equivalent in the standard C++ lib. Though it's very easy to write one. If you use gcc's libstdc++, the internal shared_ptr is actually templated on the lock policy so you can do something like:
template<typename T> using nt_shared_ptr = std::__shared_ptr<T, __gnu_cxx::_S_single>;
Then if you use nt_shared_ptr instead, the code will be much simpler.
That said, your C++ code is incorrect as well. It gives the ownership to the shared ptr of a stack variable. During the destruction of the shared_ptr, delete will be called on a stack address leading to undefined behavior