HNHacker News
TopNewBestAskShowJobs

samsquire

2,684 karma · joined June 16, 2013

submissionscomments
samsquire··on A lock-free ring-buffer with contiguous reservations (2019)
Thank you loeg, I appreciate you and information you brought that TIL.

I've been using a compiler fence to force reloads from memory to prevent -O3 from optimising away my variables/structs changing by other threads and keeping data in registers rather than reloading from memory each time. I saw the volatile recommended against from the Linux kernel programmers.

such as my thread->running == 1 in my event loops for my threads.

https://www.kernel.org/doc/html/latest/process/volatile-cons...

samsquire··on A lock-free ring-buffer with contiguous reservations (2019)
When you check available, you might have to do it as a (__atomic_load_n(&sender->realend, __ATOMIC_SEQ_CST) and do __atomic_store_n when setting available.

rather than just a plain load.

samsquire··on A lock-free ring-buffer with contiguous reservations (2019)
Thanks for your reply. This subject is still new to me.

My understanding of that syntax is that it is a compiler memory barrier, not a CPU memory barrier because the asm block is empty (no sfence or mfence).

samsquire··on A lock-free ring-buffer with contiguous reservations (2019)
I tried to write a lock free ringbuffer with weak atomics, I haven't proved it right with TLA+ yet but I started writing a model in it. I use tagging to avoid the ABA problem.

they're all on https://github.com/samsquire/assembly, i tried to write multiple disruptor with multiple consumers, then one with multiple producers then one with multiple consumers AND multiple producers, inspired by LMAX Disruptor. (There's files for each of them and table in the repo. it's not proven yet!)

the contention on the same memory address (the read/write index) is the thing that seems difficult to address.

One thing I've learned about thread safety:

I think if you have thread-owned values then you can be thread safe with a simple semaphore, providing that you have unique, DISTINCT values for each thread.

If you have two threads that have this in a hot loop in parallel:

  // thread 0                        
  if buffer[x].available == 1:
    // do stuff
    buffer[x].available = 0           

  // thread 1
  if buffer[x].available == 0:
    // do stuff
    buffer[x].available = 1
Due to causality, no matter the interleaving, thread 0 owns the buffer[x].available and body of the if statement when it is 1 and thread 1 owns the body of the if statement buffer[x].available when it is 0.

The CMP is a cheap mutex with distinct valued memory locations.

Even though thread 1 is writing to buffer[x].available and thread 0 is writing to buffer[x].available it doesn't matter because the causality is mutually exclusive. There is no interleaving of buffer[x].available = x because of the if statement.

The buffer[x].available = 0 will never run while buffer[x].available is equal to 0 overwriting or causing a data race when setting buffer[x].available to 1. So the second line cannot happen in parallel.

I need to write a TLA model to assert its safety.

If you have more than 2 threads, then you need different tokens to provide admissability to the if statement.

Remember to use compiler memory barrier

   asm volatile ("" ::: "memory");  
so you don't need volatile struct values.
samsquire··on Show HN: AboutIdeasNow – search /about, /ideas, /now pages of 7k+ personal sites
Thank you phgn

I submitted my personal homepage.

Those GitHub conventions are helpful. I was hoping people would create entire repositories in a standard convention too. Similar to Rails conventions.

samsquire··on Scheduling Internals
Love these kind of animations

I have my own animation here - if you scroll down. You can see the parsing I do for an async syntax (scroll to the hearing named Definition) - you can edit the syntax and it reparses and displays the parsed output. Sorry for the mess it's kind of a place I work on ideas. Further down is my graph renderer.

https://processes3.replit.app/

I want to auto parallelize async tasks.

I am working on a multithreaded runtime in C which is incomplete but can communicate between threads in roughly 60 nanoseconds at 6 million requests per second or closer to LMAX throughout of 20 million requests per second at a latency of 90-120 nanoseconds.

In my learnings Mutexes do not scale.

I am inspired by bulk synchronous parallel which means threads synchronize in a rhythm without Mutexes and a lock free algorithm to do with semaphores and observation rather than mutability.

I run two io_urings in different threads - one thread does send and another thread does recv. One ioring is talking to an epoll instance for network buffer readiness with EPOLLOUT.This means sending and receiving is full duplex - you can send and receive to different clients in parallel. I call it split IO.

I plan to write up all my learnings soon.

My adapted HTTP server from the loti examples gets a throughout that scales with the number of connections I am at 7000 requests per second. I am not nginx.

I use mailboxes and double buffering for thread safe communication. Each thread can communicate with another thread through its mailbox. This is like Erlang.

I plan to add coroutines based on Marce Colls coroutines. I can deschedule a coroutine when there is an await.

samsquire··on Software Infrastructure 2.0: A Wishlist (2021)
I enjoyed this post, thank you.

I'm encouraged by the same ideas.

Sometimes you just want something that stays running and doesn't go down and can scale to zero and scale upwards, ideally with revenue.

I kind of want a special mega HTTP form endpoint which I can define a pipeline from, that can go to database and cause background jobs and goes into a mega API automatically.

samsquire··on Show HN: AboutIdeasNow – search /about, /ideas, /now pages of 7k+ personal sites
I don't know if my website is eligible.

My homepage is a small introduction and a table of projects with links to those projects, which are also github repositories with a blog underneath.

This community standardisation I really like /about /now /ideas, I would like people to create github repositories with this format:

"wants" -> things that this person wants

"perspectives" -> technical perspectives on different subjects

"work" -> work that someone thinks need doing

"collaboration" -> what someone would be willing to work with others on

"commissions" -> what you'll pay someone for

"ideas" -> ideas

"blog" -> if your blog is hosted on github

"dotfiles" -> if you have them

samsquire··on Planner programming blows my mind
This is interesting. The dream of telling the computer where to end up is something I have to.

It might be interesting to someone but I used A* to do code generation to go from one state to target state. I'm not experienced with the planning community or solvers except for playing around naively with ortools.

I generate assembly instructions to move between states.

  start_state = {
    "memory": [0, 0, 0, 0],
    "rax": 0,
    "rbx": 1,
    "rcx": 2,
    "rdx": 3,
    "rsp": -1,
    "rdi": -1,
    "rbp": -1
  }

  end_state = {
    "memory": [3, 1, 2, -1],
    "rax": 3,
    "rbx": 2,
    "rcx": 1,
    "rdx": 0,
    "rsp": 6,
    "rdi": -1,
    "rbp": -1
  }
Generates

  [start, mov %rax, (%rdx), mov %rbx, (%rbx), mov %rcx, (%rcx), mov %rdx, (%rsp), mov %rax, %rsp, mov %rdx, %rax, mov %rsp, %rdx, mov %rcx, %rsp, mov %rbx, %rcx, mov %rsp, %rbx, call minus1(rdi=-1) -> rsp=4, call fourtofive(rsp=4) -> rsp=5, call fivetosix(rsp=5) -> rsp=6]
It finds all the hidden state transitions of function calls to get to the goal.

I also run it in parallel to speed up search using python multiprocessing and do dynamic neighbour generation because neighbour generation is different between threads and I couldn't parallelise A* with my original attempts without sharding this.

The dream of my experimentation is that you tell the computer what you have and what you want it works out the correct traversals for you.

For my personal intuition programming is logistics like factorio or a factory. This is why it's called "sliding puzzle", it's a puzzle where you have to move things around to see the correct picture.

Github repo with some notes: https://github.com/samsquire/sliding-puzzle-codegen-memory

Replit: https://replit.com/@Chronological/SlidingPuzzle3

samsquire··on A vision of a multi-threaded Emacs (2022)
Thank you, this was really interesting.

Multithreading and parallelism is my hobby, so I'm curious how emacs implements this. I imagine it would be really difficult to migrate the entire ecosystem from code that is not designed to be accelerated by multicore.

I feel that if you just add a mutex, the code will fall very short of expectations of Amdahls law because:

* you cannot accelerate any single memory address mutability rates/performance by adding threads * to accelerate write/read performance (to get up to theoretical memory bandwidth) you need no-locks and multiple threads with DIFFERENT memory addresses

You need a different model to programming that is efficient.

One thing I can think of is that each thread owns a few buffers and you load balance that way.

samsquire··on Maybe Everything Is a Coroutine
Would love to talk to you more about this, do you have an email address I can get to you at?
samsquire··on Maybe Everything Is a Coroutine
Would love to talk more about this with you, do you have an email address I can email you with?

Did you regex engine support commutative events? (Happen in either order?)

samsquire··on Maybe Everything Is a Coroutine
I've read casually around petri nets and graphical diagrams. I would love to talk more about this.

Would you like to talk more about this subject?

samsquire··on Ask HN: How do you come up with side project ideas in 2024?
I just hope that within 6 months to 1 year you'll have found and built something you really enjoy working on and learn from, even if unfinished. Keep going. Positive conviction and optimism. Have faith. I hope you don't think negatively about your ideas and potential because that won't help.

What do you find extremely satisfying working on in programming?

Do you write all the time? It works for me! I keep journalling my ideas in a markdown README.md file publicly on GitHub since 2013. While I was writing I felt inspired by an idea and actually felt I desired to try write some code to implement this idea.

I encourage you! You can write your thoughts down and do small achievable things repeatedly.

I recommend using replit to get an environment quick and ready for programming in. When I was a child I wanted to be an inventor because I liked the idea of creating things.

My interest is low level things such as JIT compilers, database internals and distributed systems.

see my profile for what I've done with this strategy and my programming side projects.

samsquire··on Is something bugging you?
I am thinking about state machine progressions and TLA+ style specifications which are invariants over a progression of variables.

Your package manager knows your operating system's current state and the state space of all the control flow graph through the program and configuration together can go to, it can verify that everything lines up and there will be no error when executed a bit like a compiler but without causing the Halting problem.

In TLA+ you can dump a state graph as a dot file, which I turn into a SVG and run with TLA+ graph visualiser.

Types verify possible control flow is valid at every point. We just need to add types to the operating system and file system and represent state space for deterministic verification.

You could hide packages that won't work.

The package manager would have to lookup precached state spaces or download them as part of the verification process.

samsquire··on Is something bugging you?
This is really exciting.

I am an absolute beginner at TLA+ but I really like this possible design space.

I have an idea for a package manager that combines type system with this style of deterministic testing and state space exploration.

Imagine knowing that your invocation of

   package-manager install <tool name>
Will always work because file system and OS state are part of the deterministic model.

or an next gen Helm with type system and state space exploration is tested:

   kubectl apply <yaml>
will always work when it comes up because all configuration state space exploration has been tested thanks to types.
samsquire··on Non-code contributions to open source
Things I want:

* lots of screenshots

* a README.md that is extremely long and detailed

* tutorials, reference, design documents, architecture diagrams

* mental model documents to explain how things are thought of by the authors

samsquire··on Maybe Everything Is a Coroutine
I designed a syntax for this: that everything is a state machine progression, a bit like sequence types in the article.

   state1a state1b state1c | state2a state2b state2c | state3a state3b state3c
This means wait for state1a, state1b state1c in any order, then move to the next sequence of things to wait for.

In a multithreaded server or multimachine distributed system, there are global states you want to wait for and then trigger behaviour. The communication can be inferred and optimised and scheduled.

It's BNF syntax - inspired by parsing technology for parsing sequences of tokens but tokens represent events.

If you use printf debugging a lot, you know the progression of what you see is what happened and that helps you understand what went wrong. So why not write or generate the log of sequence of actions you want directly not worry about details?

But wait! There's more. You can define movements between things.

So take an async/await thread pool, this syntax defines an async/await thread pool:

  next_free_thread(thread:2);
  task(task:A) thread(thread:1) assignment(task:A, thread:1) = running_on(task:A, thread:1) | paused(task:A, thread:1);

  running_on(task:A, thread:1)
  thread(thread:1)
  assignment(task:A, thread:1)
  thread_free(thread:next_free_thread) = fork(task:A, task:B)
                                | send_task_to_thread(task:B, thread:next_free_thread)
                                |   running_on(task:B, thread:2)
                                    paused(task:A, thread:1)
                                    running_on(task:A, thread:1)
                                    assignment(task:B, thread:2)
                               | { yield(task:B, returnvalue) | paused(task:B, thread:2) }
                                 { await(task:A, task:B, returnvalue) | paused(task:A, thread:1) }
                               | send_returnvalue(task:B, task:A, returnvalue); 
  
Why not just write what you want to happen and then the computer works out how to schedule it and parallelize it?

I think iteration/looping and state persistence and closures are all related.

I have a parser for this syntax and a multithreaded barrier runtime which I'm working on, I use liburing. I want to get to 500 million requests per second of the and ~50ish nanosecond latency of LMAX Disruptor.

The notation could be used for business programming and low level server programming I think.

samsquire··on Drowning in code: The ever-growing problem of ever-growing codebases
Thanks for the article.

Regarding the article which talks about multicore. Multicore and distributed systems are hard. Behaviour implementation, and transforming event sequences across multicore and multimachine consistently is the hard part of computing for me (distributed systems). And understanding large existing systems such as Chromium, Firefox or GCC. Any enterprise K8s cluster with operators everywhere. That's a lot of code.

This is just my thoughts, I find it easier to write code than read large code bases because like trying to understand a very large mathematical formulae in one go. You cannot even get the entire system into a screenful to understand it, like you could potentially a small formulae. I have to break it out into ordered steps to understand mathematics. They're like an object in the mind that you interact with intuitively. My mental model is the key driver to the act of programming. I sometimes get the mental model right and serialise it into code. Writing is an expression of thoughts and there is a thought that gets turned into notation. Do either of them (thought,notation) represent the other losslessly?

I once used OpenGrok, a web based sourcecode repository viewer to tell myself I would try reading and understanding a large code base.

The semantics of programming languages have an impedance mismatch to how my mental model works.... I have mental models about relational databases and what is fast on a computer. And then there is behaviour, state and IO to work with.

I could change my mental model to match the programming language semantics on the computer but I would probably be less happy that way.

I feel programming languages such as C++ and Rust and Haskell introduce abstractions that work with a certain mental model of solving problems, but not everyone has the same mental model. They work great for the compiler codegen writer, but not for implementing behaviours.

If you've ever worked in a enterprise Java codebase or a frontend software React codebase, from my perspective they're a mess because of the imposed semantics of the language and false problems of trying to get the computer to have the right sequence of actions or behaviour sequence.

samsquire··on Hyperbridge: Fast multi-producer, multi-consumer unbounded channel in Rust
Interesting. Thanks for sharing.

I am in early stages of writing a low latency barrier which provides low latency lock free communication between thread pairs ( 1 thread bidirectionally with 1 other thread ) but wiring up to other barriers is proving difficult.

The number of sent messages doesn't match the number of received messages I think I need a mutex unfortunately

So it's interesting that there is options in this space in Rust.

samsquire··on Plan 9 design rule: if a feature doesn't work for everything, don't do it
This is interesting because in some kinds of software, if you just keep adding features then the software becomes hard to understand and reason about because of the multiplied interactions between features. The software become so heavy that very few people understand it.

I think you see this in programming languages themselves. I am not criticising C++ or Rust (they do good work, which I admire) but from my perspective the languages have many features that all interact in tricky ways. An example would be coroutines, threads, async, memory management.

There's also social interactions that multiply complexity: see the Python 2 to 3 migration pain that splintered the language for a while. Or Red Hat and its source.

I really enjoy this blog post about writing accidentally writing a compiler and handling the permutations of AST Nodes.

https://rachit.pl/post/you-have-built-a-compiler/

A billing or invoice system, that should be simple? But it isn't because everything interacts: failed jobs, holidays, daylight saving time, timezones, time, business processes, procedures, edge cases.

This is actually a subject that I am investigating as a hobby which is how do you handle interactions between arbitrary behaviours: such as you want a system that is Windows, Linux and Mac and platforms have different semantics that make writing your software tricky.

I think type systems get some of the way there but TLA+ and other formal verification tools help us get another part of the way there. But I am still learning.

samsquire··on If Lisp is so great
LISP is postorder evaluation order of a tree. We can go further and more mind expansive than this!

The future I am trying to design is a language where traversal order is arbitrarily repetitive and arbitrary and corecursive and has term rewriting traversals.

I think algorithms are just reified traversal evaluation orders and joins - of algebra and mathematics.

samsquire··on You (probably) don't need to learn C
I really enjoy C programming. I am a beginner. I wrote a JIT compiler in C recently. Why didn't I write it in Rust?

The semantics of C really fit how what I think about what computers do: that we move around things between boxes of memory and between registers. That computers is largely logistics between "places". Edit: It's a bit like a factory as in factorio

I didn't write it in Rust because I find the semantics of C++, Rust, Haskell and other functional languages to be much more abstract and harder than this simple idea of stateful movement.

https://github.com/samsquire/compiler

samsquire··on Htmx changes license to Zero-Clause BSD
I would have hope that anybody who uses your code doesn't want to keep it a secret, because it would be good if people use the same common base code and changes can benefit everyone.

Since there's no requirement to credit a zero clause licenced BSD code, someone who receives the BSD code compiled might not know it's included but that's the risk I'm taking.

samsquire··on Htmx changes license to Zero-Clause BSD
I use the zero clause licence for my all my work because it just removes all the overhead of using my code.

I think (please correct my understanding) zero clause BSD can be embedded in a GPL project too, it just becomes one-way included, subsumed into the GPL project and then under the GPL licence. (Relicenced)

Could the author of HTMX explain their reasoning for changing the licence? I am curious?

samsquire··on Atomics and Concurrency
I use a tag masked into the stored data to try prevent the ABA problem.

Every thread has its own unique tag.

  long original = me->realend;                                                                                                              
  int tag = (original & TAG_MASK);                                                                                                          
  changed = (((original & END_MASK) >> 32)) % me->size;                                                                                   
  long new = (data->thread_tag) | (((changed + 1) % me->size) << 32); 
Then compare and swap as usual. If another thread updates then they shall fail the compare and swap and we have to reloop and try again.

I am still learning TLA+ to write a model.

samsquire··on Why use strace in 2023? [video]
This would be an interesting GUI that could be in a web browser to cross reference effects between programs such as communication between programs and Wayland. Specifically the parameters of syscalls create an effect, like write to block. Wireshark for syscalls. I think it already exists.

I kind of think of the interactions with the OS as the "effects" of the application like the IO Monad.

It's like a protocol too, function application in a certain order causes the effects of the software. The syscalls is a stream.

There is also a inner protocol between the buffers of binary protocols inside the syscalls such as a network buffer.

samsquire··on Visualizing Ext4
I kind of think of computer microarchitecure as a factory, as in factorio (Which I admit I have not played, because I've been warned against it)

There's instruction stream streams into L3, L2, L1 caches from DRAM, PCIE, DMA

then there's multiple cores and they each have register files, they shuffle numbers between registers and the caches.

There's reordering going on, there's parallelisation going on, lots of conveyor belts.

It's all so complicated factory.

samsquire··on Teaching C (2016)
I don't mean that there is a 1-1 constant relation between C statements and exact assembly, but if you run gcc with -S or run

   objdump -b binary -Matt,x86-64 -D main.bin -m i386 ;
(note: this command places the assembly and C code side next to eachother, I think you have to compile with -g debug flag with gcc)

You can work that the assembly implements the semantics of C's virtual machine model and it is this that I find easier to understand than some algebraic systems. I kind of think of struct->ptrarray[indx]->struct.something corresponds to the calculation of ultimately a single memory address even if there is various shift lefts or adds or ors.

Java has its template interpreter which is interesting to read about and there is copy and patch JIT compilers.

samsquire··on Teaching C (2016)
Agreed.

I find C easier to understand than the algebra of other languages such as Haskell, Ocaml, Rust because of how C statements correspond to assembly.

Memory is just a big grid of numbered locations and computing is logistics between grid locations. Execution is transition between memory locations.

LEAQ or "&" (C addressof) is an address calculation or LEAQ for one of those grid locations.

← PreviousPage 3 of 34Next →