const Coherency = enum {
// writes have defined order across threads, reads observe some write
atomic,
// happens atomically, but writes dont need to have a defined order across threads
unordered,
// need not be atomic: on data-race, produces an unspecified value instead of UB
racy,
// not atomic. data-race is UB| const std = @import("std"); | |
| const el: u256 = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed; | |
| const L = 1 * el; | |
| fn heea(v: u256) struct { u128, u128 } { | |
| std.debug.assert(len(L) == 253); | |
| std.debug.assert(v < L); | |
| std.debug.assert(v > 0); |
modification order:
- atomic vars are an array of writes, each write tagged if its Release + tagged if its rmw
- reads to it return some write in the array, each read tagged if its Acquire or not
- an rmw, atomically, pushes a write and returns the previous last-write as a read. So it can be tagged both Acquire (for the read part) & Release (for the write part)
coherence:
- each thread could be reading from different points in a given atomic-var write-array
- in each thread, a read never goes backwards i.e.
read a[1]; read a[0]is invalid, but1; 1or1; 2is fine - different read points also means threads could see writes to multiple arrays happen in different orders e.g. T1 does a.push(x); b.push(y) but T2 could see b's push before seeing a's push (T2's a-cursor hasnt updated yet)
Currently, the std.Io interface isn't too optimizable:
- All forms of concurrent execution goes through
io.async/asynConcurrent(f)andio.awaitwhich can't be statically aware of when.awaitwill be called, so it must dynamically allocate the context needed to runf. - The main api for blocking/unblocking on an arbitrary state is through Mutex & Condvar which require locking a mutex to wait (& wake correctly) + restrict implementors to only a
usizewith a biased representation for each state. - It ties cancellation to the task/concurrency model instead of to blocking operations (which are really the ones getting cancelled); To cancel a set of operations, they must be wrapped in a new spawned Future. It's also unclear, due to the racy nature of
io.cancel, if a blocking operation consumes the stored cancellation request, or if it persists & causes all future blocking ops in that Future to return Cancelled.
I've thought of some ideas on how to address these + the all-encompassing nature of th
-
Every atomic object has a timeline (TL) of writes:
- A write is either a store or a read-modify-write (RMW): it read latest write & pushed new one.
- A write is either tagged Relaxed, Release, or SeqCst.
- A read observes some write on the timeline:
- On the same thread, future reads can't go backwards on the timeline.
- A read is either tagged Relaxed, Acquire, or SeqCst.
- RMWs can also be tagged Acquire (or AcqRel). If so, the Acquire refers to the "read" portion of "RMW".
-
Each thread has its own view of the world:
- Shared write timelines but each thread could be reading at different points.
| // zig build-exe benchmark.zig -OReleaseFast -lc | |
| // ./benchmark 4096 | |
| const std = @import("std"); | |
| const allocator = std.heap.c_allocator; | |
| const iterations_per_byte = 1000; | |
| const warmup_iterations = 10; | |
| comptime { |
Consider this an informal guide to reading the Solana snapshot format. This guide is written for Solana Labs versions v1.14 through v1.17.
You are probably reading this because you want to read the accounts in a snapshot without going through the pain of interfacing with the Solana Labs codebase.
We assume general familiarity with the Solana ledger.
For simplicity, this will just go over the unsigned case
Written for someone, such as myself, who isn't as quick to put together the same logical steps
Given
Find
To motivate what we'll be doing, lets start by considering:
$$ \begin{align*}
| const std = @import("std"); | |
| pub fn main() !void { | |
| var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | |
| defer std.debug.assert(gpa.deinit()); | |
| const allocator = &gpa.allocator; | |
| const num_threads = std.math.max(1, (std.Thread.getCpuCount() catch 1) / 2); | |
| const epolls = try allocator.alloc(std.os.fd_t, num_threads); | |
| defer allocator.free(epolls); |