Colored-pencil caricature portrait of Tamir Yirga

Tamir Yirga

Software engineer building high-performance data infrastructure and distributed systems. I primarily write Rust, with a growing interest in ML inference systems. Currently finishing my MS in Computer Science at Northeastern.

Projects Showcase

Some of my personal projects and experiments. Every number here was measured.

Every kernel is custom built, benchmarked, and rewritten when it turned out slower than I expected. A GGUF parser memory-maps the weights, attention is grouped-query over a paged KV cache, and the SIMD backend is chosen once at startup so the hot path pays a single atomic load.

Generating a token runs that shape once per layer, every token, and it is bound by memory bandwidth rather than arithmetic. The INT8 weight block fits in L2. The f32 block does not. Square-matrix benchmarks put INT8 at roughly 2x and miss this entirely.
f32 GEMM at 1024 square, 10 cores
157.9 Gelem/s
Naive scalar version of the same kernel
23.3 Gelem/s
Tests passing across kernels, cache and sampling
923

Criterion benchmarks on an M1 Pro. The footprint is read out of the running process, not calculated on paper. These are kernel and memory numbers though. A full 7B generation run is still unfinished, so there is no tokens per second figure here yet.

Dynamo-style leaderless quorums and Raft, running over the same LSM storage engine. A linearizability checker in CI decides which guarantee each path actually gives you, so the difference between them is a test result rather than a README claim.

Grafana dashboard showing operations per second, p99 latency, quorum failures and repairs across five LedgerKV nodes while one node is killed and restarted Open full size
Grafana reading Prometheus off all five nodes during that run. I killed node2 at 15:14 and brought it back at 15:16: its line drops out, the other four carry the load, and quorum failures stay flat at zero throughout. The two p99 spikes are JVM warm-up and node2 restarting cold, not the kill.
Steady-state p99 latency, p50 was 2 ms
15 ms
Time node2 spent dead mid-run
90 s
Tests passing in CI, linearizability checker included
361

One run, one cluster, scraped from each node's /metrics endpoint. Hinted handoff and read repair are both implemented, but I have not measured how long a recovered replica takes to converge, so there is no number for that here.

RabbitMQ into DynamoDB with Redis-cached reads, fronted by a token-bucket admission controller that samples queue depth every 200 ms and steers with AIMD. Retreat is a hundred times faster than recovery: it halves the rate on a severe backlog and claws it back ten permits a second. If the broker cannot be reached it holds the current rate instead of reading silence as an empty queue, which was the original bug.

Fleet awareness coordinates the instance count rather than the rate. Each replica heartbeats into a Redis sorted set and divides the admission floor by what it sees, because sharing one rate would collapse it by half for every replica. A shrink needs three consecutive confirmations, so a Redis restart cannot talk a replica into thinking it is alone.

A closed-loop harness waits for each response before sending the next one, so when the system slows down the harness politely slows down with it and the delay never lands in the numbers. Rebuilding it open loop, charging latency from when a request was due rather than when it went out, moved p50 from 1,120 ms to 10,617 ms. Nothing about the system changed. The first number was just wrong, and it is the number the assignment asked for.
The AWS run: 200,000 requests, every one a 200
4,309 req/s
Cardinality write requests on the DynamoDB counter
25,163
Same workload once HyperLogLog took over the counting
9,635
Defects documented, each one pinned by a regression test
38
Decision records, rejected alternatives included
11

Two rigs, two eras. The original ran on AWS, a Spring server on EC2 with RabbitMQ and DynamoDB behind it, and the one report kept from that build records 200,000 requests at 4,309 req/s with every response a 200. Everything after the rebuild ran against LocalStack on one laptop, where LocalStack is the binding constraint in every run, so read those throughput figures as a property of the rig rather than the architecture. Peak offered load in that sweep was 4,376 req/s and the controller shed hard above it. A batching result that looked like a 16% gain did not survive swapping the arm order, so it is not on this page. The CI workflow is written but has never actually run.

I owned the data layer. Clinical events live in JSONB because their shape changes by type, with a GIN index over the payload and a partial index that keeps soft-deleted rows out of the query path everything else uses. Deletes are soft throughout, so the audit trail survives.

The part that mattered most was the spec. Three teams were building against the same records, so I wrote the one document that says what a clinical event is: every enum lowercase, every field named exactly, and the required fields listed per event type. Blood pressure gets systolic and diastolic and never a generic value field. A named drug is always a medication, never a procedure, even when it is injected.

vital_signvital_type, then per type. Blood pressure needs systolic and diastolic. Heart rate carries no unit field because the unit is fixed.
symptomsymptom. Onset is a timestamp, duration is a length of time, and they are not interchangeable.
mental_statestate, one of twelve. No severity field on this type, and nothing outside the list.
medicationmedication_name and status, always. The field is dose_amount, not dose or dosage.
procedureprocedure_name and status.
allergyallergen and allergy_type. Severity describes a past reaction, criticality describes future risk.
otherdescription.
The required fields per event type, from the schema spec. Pinning the names and the enum casing is what stopped three teams from inventing three different shapes for the same record.
Flyway migrations behind the schema, seven of them mine
8
Containers in the Compose stack I assembled
6
Mental state values the spec allows, up from five
12

Team project, so the lines matter. The schema, the migrations, the event spec and the Compose stack are mine. The transcript confirmation gate, the Python extraction consumer and the architecture diagrams are other people's work, and so were the speech-to-text vendor comparison and the model-selection decision. Nobody measured throughput or latency on this, so there are no performance numbers to show.

The interesting problem here is not speed, it is measuring speed without lying to yourself. MPS and CUDA queue work asynchronously, so a timer wrapped around a forward pass measures how long it took to hand the kernels to the GPU, not how long the GPU spent on them. Every timed call is bracketed by a device synchronize, and ten warmup passes run first so Metal shader compilation stays out of the numbers.

01Warm upTen untimed passes. Absorbs Metal shader compilation and the allocator settling.
02SynchronizeWait for the queue to actually drain, so the clock starts on a quiet device.
03Time 100 passesperf_counter around each one, synchronizing after every call rather than at the end.
04Report the spreadMean, standard deviation, min and max. Throughput is derived from the mean, not counted separately.
The measurement protocol. Skip the synchronize and a model can look ten times faster than it is, which is the failure mode this exists to avoid.
Architectures through the same harness and hardware
4
Warmup passes before the clock starts
10
Timed passes per measurement
100

Straight answer: the harness writes its results into a gitignored directory, so nothing is committed that I can quote here, and I am not going to publish a throughput number from memory. Rerunning it and publishing the table is the obvious next step. This was a three-person course project and the architecture comparison was my track.

Threshold, segment, then describe each region with seven features chosen to survive rotation and scale: how much of its oriented box the region fills, aspect ratio, the first three Hu moments, compactness and extent. The thresholding, blur and morphology are implemented directly rather than called out of OpenCV, which was the point of the exercise.

The eigenspace path rotates each object onto its axis of least central moment, flattens it to a 64 by 64 patch, and projects that 4096 pixel vector onto 20 principal components. Matching happens in those 20 dimensions, and one image per object is enough to add it.

01ThresholdISODATA over a 6.25% random pixel sample, so the cutoff tracks the scene without reading every pixel.
02Segment and orientConnected regions, each rotated onto its axis of least central moment.
03DescribeSeven invariant features, or a 64 by 64 canonical patch for the eigenspace path.
04ClassifyNearest neighbour under scaled Euclidean distance, with a cutoff that lets it answer "unknown".
Every stage runs per frame on live video. The 6.25% sample is what keeps the threshold step cheap enough to do that.
Principal components, down from 4096 pixels
20
Rotation and scale invariant features per region
7
Pixels sampled to estimate the threshold
6.25%

No benchmark is committed for this one. There are accuracy figures in the old write-up, but they sit in an example output block rather than a recorded run, so I am not going to repeat them. There is no timing code in the project either, which means I cannot honestly quote a frame rate. The demo video is the real evidence.

Work Life

Spark 4.1.1 TIME data types in Rust Arrow-native PySpark UDF framework 15x throughput speedup via zero-copy Arrow transfer https://lakesail.com/
RAG text-to-SQL at 98% accuracy Analytics platform with AI chat Self-maintaining pipeline for 700M+ records https://decanaria.com/
8 e-commerce platforms with $50M+ revenue Platform migration with 60% legacy refactor Reduced cart abandonment by 18% https://scandiweb.com/
Non-custodial wallet for 7,000+ users Real-time sync with Firestore + WASM 60% throughput boost via UTXO caching https://github.com/Endubis-Solutions/

Education

GPA 4.0/4.0 Research Assistant (LLM Evaluation) Teaching Assistant (Scalable Distributed Systems, Computer Vision) Key coursework: Distributed Systems, ML, CV, Algorithms

Certifications

Featured Writing

View all writing