Live data from Hacker News

How is Ultrassembler so fast?

jghuff.com

31–40 of 53 posts

Re: How is Ultrassembler so fast?

#31
post #16

Earlier quoted context omitted.

Ditto. Perfect hashing strings smaller than 8 bytes has been the fastest lookup method in my experience.

Problem is, there are a lot of RISC-V instruction way longer than that (like th.vslide1down.vx) so hashing is going to be slow.

Note th.vslide1down.vx is a T-Head instruction, a vendor custom extension.

It is not part of RISC-V, nor supported by any CPUs outside of that vendors' own.

Re: How is Ultrassembler so fast?

#33
post #20

Exceptions in C++ are never zero-overhead. There is a time-space tradeoff for performance of uncaught exceptions, and G++ picks space over time.

There's a time-space tradeoff to basically any means of error checking.

Including checking return codes instead of exceptions. It's even possible for exceptions as implemented by g++ in the Itanium ABI to be cheaper than the code that would be used for consistently checking return codes.

Re: How is Ultrassembler so fast?

#34
post #7

Earlier quoted context omitted.

Overall, this is a fantastic dive into some of RISC-V's architecture and how to use it. But I do have some comments: > However, in Chata's case, it needs to access a RISC-V assembler from within its C++ code. The alternative is to use some ugly C function like system() to run external software as if it were a human or script running a command in a terminal. Have you tried LLVM's C++ API [0]? To be fair, I do think th…

> LLVM's C++ API I think I read something about this but couldn't figure out how to use it because the documentation is horrible. So, I found it easier to implement my own, and as it turns out, there are a few HORRIBLE bugs in the LLVM assembler (from cross reference testing) probably because nobody is using the C++ API. > There are plenty of cppcon presentations [1] about exceptions, performance, caveats, blah blah.…

A specific presentation I'd point to is Khalil Estell's presentation on reducing exception code size on embedded platforms at https://www.youtube.com/watch?v=bY2FlayomlE

But honestly you'd get vast majority of the benefit just by skimming through the slides at https://github.com/CppCon/CppCon2024/blob/main/Presentations...

With a couple of symbols you define yourself a lot of the associated g++ code size is sharply reduced while still allowing exceptions to work. (Slide 60 on)

Re: How is Ultrassembler so fast?

#35
post #16

Earlier quoted context omitted.

Ditto. Perfect hashing strings smaller than 8 bytes has been the fastest lookup method in my experience.

Problem is, there are a lot of RISC-V instruction way longer than that (like th.vslide1down.vx) so hashing is going to be slow.

Is there a handy list of all RISC-V instructions?

Re: How is Ultrassembler so fast?

#36
> Additionally, in C++, requesting that heap memory also requires a syscall every time the container geometrically changes size

That is not true - no allocator I know of (and certainly not the default glibc allocator) allocates memory in this way. It only does a syscall when it doesn’t have free userspace memory to hand out but it overallocates that memory and also reuses memory you’ve already freed.

Re: How is Ultrassembler so fast?

#37
post #23

I wonder if you thought about perfect hashing instead of that comparison tree. Also, flex (as in flex and bison) can generate what amounts to trees like that, I believe. I haven't benchmarked it compared to a really careful explicit tree though.

You're probably thinking of gperf, not flex and bison.

I meant flex, for generating a switch table for that type of lexer. gperf is for hashing which is different. But, there may be better methods by now since the field has changed a lot.

Re: How is Ultrassembler so fast?

#38
post #22

Earlier quoted context omitted.

You might look into using memory mapped IO for reading input and writing your output files. This can save some memory allocations and file read and write times. I did this with a project where I got more than 10x speed up. For many cases file IO is going to be your bottleneck.

mmap-based I/O still needs to go through the kernel, including memory allocation (in the page cache) and all. If you've got 10x speedup from mmap, it is usually because your explicit I/O was very inefficient; there are situations where mmap is useful, but it's rarely a high-performance strategy, as it's really hard for it to guess what your intended I/O patterns are just from the page faults it's seeing.

Windows uses memory mapped IO for loading all executable processes because it allows you to start executing a process after loading a few pages even if the exe is megabytes. You can use the same to reduce latency for starting to assemble data before the rest of the file loads, the rest can be loaded using more efficienct asynchronous mechanisms. Using for output also means your process doesnt waits on flushes that is also async. And in memory constrained environments the OS doesn’t have to write your data to swap, it can just reload it from the meeting mapped file.

Re: How is Ultrassembler so fast?

#39
post #22

Earlier quoted context omitted.

mmap-based I/O still needs to go through the kernel, including memory allocation (in the page cache) and all. If you've got 10x speedup from mmap, it is usually because your explicit I/O was very inefficient; there are situations where mmap is useful, but it's rarely a high-performance strategy, as it's really hard for it to guess what your intended I/O patterns are just from the page faults it's seeing.

Windows uses memory mapped IO for loading all executable processes because it allows you to start executing a process after loading a few pages even if the exe is megabytes. You can use the same to reduce latency for starting to assemble data before the rest of the file loads, the rest can be loaded using more efficienct asynchronous mechanisms. Using for output also means your process doesnt waits on flushes that is…

Linux also uses mmap for running executables. But explicit I/O does not mean you have to start off by a gigabyte-long read().

Re: How is Ultrassembler so fast?

#40

> Additionally, in C++, requesting that heap memory also requires a syscall every time the container geometrically changes size That is not true - no allocator I know of (and certainly not the default glibc allocator) allocates memory in this way. It only does a syscall when it doesn’t have free userspace memory to hand out but it overallocates that memory and also reuses memory you’ve already freed.

Wasn't there also over allocate for the first geometric expansion and mark the 2nd as for space for likely shortlived objects?
Post reply on HN