II · THE IDEA · ARTIFICIAL INTELLIGENCE
near‑data‑processing-for-embedding-lookup
▶ Listen · narrated
Serving a recommendation is mostly waiting. The processor sits idle while rows of numbers trickle in from memory. One fix: stop carrying the rows to the adder, and put an adder beside the rows.
At a glance
- The operation
- Gather scattered rows from a huge table, then add them
- The bottleneck
- Bytes moved per addition performed
- The idea
- Add the rows inside or beside the memory device
- What returns
- One summed vector, not every row that was read
- Evidence here
- One relevant source; no measured speedups quoted
Suppose all you want is the total of 1 number printed in each of 20 books, and the books sit scattered across many floors of a library. One way: a runner carries all 20 books down to your desk, you copy out the numbers and add them. The books are heavy, the stairs are slow, and you used almost nothing from each one.
The better way: put a clerk with a notepad on each floor. The clerk finds the books, reads the numbers, adds them up, and sends down a single slip of paper with the total. The finding still happens. The stairs still get climbed. But the heavy part — carrying books down — mostly disappears.
A recommendation model's embedding lookup works like the first arrangement. Its table of numbers is far too big for the fast memory next to the processor, so it sits in main memory. Each request needs a few dozen rows from unpredictable spots in that table, and the very next thing the model does is add those rows together. So the processor waits while the rows are carried over, adds them in almost no time, and throws the individual rows away. Near-data processing puts the clerk on the floor: a small adder next to the memory does the summing and sends back 1 result.
Embedding lookup in a recommendation model is a sparse gather followed by a pooling reduction — usually a sum or mean over the gathered rows. Its arithmetic intensity, meaning operations performed per byte fetched from memory, is roughly 1 operation per element read. Dense matrix multiplication reaches ratios orders of magnitude higher through register and cache blocking, which reuse each loaded value many times. The lookup is therefore bandwidth-bound on every plausible accelerator: adding arithmetic units or raising clock speeds gains nothing, because the limit is achievable bytes per second on the memory path.
Two effects compound the problem. The tables exceed on-chip SRAM — the small, fast memory on the processor die — and often exceed a single device's HBM, the high-bandwidth memory stacked beside it, so rows come from DRAM or from a remote device across an interconnect. And the access pattern is irregular, driven by per-request feature values, so spatial locality is poor: a row narrower than the DRAM burst or cache line still pays for a full transfer, and prefetchers have nothing to predict. Effective bandwidth falls well below peak, and much of what crosses the wire is discarded.
Near-data processing exploits the reduction. Pooling collapses n gathered rows into 1 row of the same width, so performing it at or near the memory device cuts link traffic by roughly a factor of n. The price is fixed-function accumulation logic plus a command interface that can express gather-and-reduce rather than plain reads. Candidate placements: logic on the DRAM die, on a buffer or interface chip in front of the DRAM, or on a near-memory accelerator on the same module. The placement decides which segment of the path is relieved, and so how much of the possible win is realised.
The limitations are substantial. Fixed-function logic cannot absorb a change of pooling operator or number format without redesign. Training writes to the tables as well as reading them, so a near-memory scheme must handle embedding updates and their consistency, not just read-only inference. The command interface is a standardisation problem more than an engineering one. And the benefit scales with pooling factor and row width, which vary by feature and by model, so aggregate speed-ups are configuration-dependent.
The adjacent work cited here — the 2021 software-hardware co-design paper on scalable training of recommendation models — answers the same pressure at a different level of the hierarchy. It treats table sharding, placement and inter-device communication as variables to optimise jointly with the hardware, on the premise that data movement is the binding constraint. It is the rack-scale form of the same argument, and better evidence for the framing than for any particular near-memory implementation.
Look closer
The arithmetic is almost nothing
Count the work per number fetched. A row of an embedding table is a short list of numbers, and the model's next step is usually to add the fetched rows together position by position. That is roughly one addition per number read. Dense matrix multiplication is the opposite: it reads a number and then reuses it many times over. The ratio between bytes moved and arithmetic performed decides whether a processor is busy or idle, and for embedding lookup that ratio sits at the wrong end. Making the adder faster changes nothing, because the adder was never the queue.
The reads are scattered, not streaming
The rows a request needs are chosen by which items a user interacted with, so their positions in the table are effectively arbitrary. Memory hardware is built to reward the opposite behaviour: read a block, and the neighbouring bytes arrive almost free and are likely to be wanted next. Here they are not wanted. So a small row can cost a whole block transfer, and most of what crosses the wire is discarded. That gap between bytes transferred and bytes actually used is the specific waste near-data processing aims at.
Summing is the compressible step
The saving depends on one structural detail: the rows are pooled, meaning added together, before the model does anything else with them. Twenty rows go in and one row of the same width comes out. If the addition happens at the memory, only that single result needs to travel back, and traffic on the link falls by something close to the number of rows pooled. If the model instead needed all twenty rows separately downstream, there would be nothing to compress and no reason to move the work. The trick is not general. It is specific to the shape of this operation.
The story
A recommendation model — the software that decides which video, advert or product to show next — is 2 machines bolted together. One half is an ordinary neural network: dense arithmetic on long lists of numbers, the work that graphics processors were built to do quickly. The other half is a lookup. The model's inputs include categorical features — facts with no natural number attached, such as which item was clicked or which video was watched. These live in embedding tables: very large arrays in which every possible value of a feature owns 1 row of numbers, and that row is the model's learned description of the value. For these features the model's whole job is to find the right rows and add them together, position by position — first numbers added to first numbers, second to second. That adding step is called pooling.
The lookup is where the time goes, for 2 reasons.
First, the tables are too big to fit in the small, fast memory on the processor chip itself. The rows sit in DRAM — the ordinary main memory chips — or further away still, on another device across a network link. Every lookup is a trip off the chip.
Second, the rows a request needs are scattered. Which rows depends on which user and which items, so the addresses jump around the table with no pattern. Memory hardware is built for the opposite habit. It rewards reading in order: fetch 1 block of bytes, and the neighbouring bytes arrive almost free — and a reader working in order wants them next. When the addresses jump, that generosity becomes waste. A short row arrives wrapped inside a much larger block, and the rest of the block is thrown away. The circuitry that guesses what will be read next has nothing to guess with. The memory system delivers a fraction of its rated speed, and much of what crosses the wire is never used.
Now count the work per number that does arrive. Each fetched number is added once, and that is the end of it. Dense matrix multiplication is the opposite: it fetches a number and then reuses it many times, which is why processors stay busy while running it. The ratio of arithmetic done to bytes moved decides whether a processor works or waits, and for embedding lookup that ratio sits at roughly 1 addition per number read — the wrong end of the scale.
So the machine starves. The processor's adders stand idle while the memory system dribbles rows in. This is a bandwidth bottleneck: the speed limit is how many bytes per second can be moved, not how many additions per second can be done. A faster processor changes nothing, because the adder was never the queue. The queue is on the wire.
Near-data processing is the answer, and it is almost embarrassingly simple. The rows are fetched only in order to be added together, and addition is among the cheapest things a computer does. So put a small adder next to the memory — on the memory chip itself, on the buffer chip that sits in front of it, or on a small helper processor on the same board — and let the pooling happen there. Instead of shipping 20 rows across the link so the processor can sum them, ship 1 summed row. The scattered reads still happen, but inside the memory system, where each hop is short and cheap. The expensive long-distance link carries perhaps a twentieth of the traffic it did.
The saving depends on 1 structural detail, and it is worth being exact about it. Pooling takes many rows in and produces 1 row of the same width out. That reduction is what makes the trick work: the result is much smaller than its ingredients, so computing it at the source shrinks what must travel. If the model instead needed all 20 rows delivered separately for some later step, the same bytes would cross the link wherever the adder sat, and moving it would save nothing. Near-data processing is not a general speed-up for memory. It is a claim about operations shaped like this one: consume a lot, emit a little.
Behind this sits a general fact about modern computers: moving data costs more than computing on it, and the gap has widened for decades, because arithmetic got cheap faster than data movement did. Accept that, and the design question flips. You stop asking where the computation would be convenient and start asking how few bytes you can get away with moving.
The co-design paper by Mudigere and colleagues, published in 2021, records a system built under exactly this pressure. Its subject is training rather than serving. The embedding tables are too large for any 1 device, so they are split across many; the communication between devices is engineered deliberately rather than left to defaults; hardware and software are shaped together, with the memory system treated as a design problem rather than a given. That is the same instinct as near-data processing, applied to a whole rack of machines instead of a single memory chip. In both cases, the thing being shortened is the distance the data travels.
What that paper does not give — and this should be said plainly — is a clean, general number for how much near-data pooling wins. The direction of the effect follows from the arithmetic above: fewer bytes cross the link. The size of the effect depends on the table sizes, the row widths, how many rows each request pools, and where the adder sits — and much of the published work on near-memory hardware reports simulations rather than measurements on shipping chips. Anyone quoting a single speed-up figure for near-data processing in general is quoting 1 configuration.
There are real costs, too. An adder near memory must be told what to add, which means a new command language between processor and memory — and interfaces of that kind take years to standardise, because every maker of processors and memory must agree on them. The near-memory logic is fixed once built: it can add, and perhaps average, but it cannot run whatever operation next year's models prefer. And during training the rows are written as well as read, so any scheme that keeps the tables in 1 place and the arithmetic in another must handle updates correctly. Read-only serving is the easy case. Training is not.
Why it mattered then
By about 2020 the mismatch was hard to ignore. At the big internet companies, recommendation models consumed a large share of serving capacity, yet they looked nothing like the models the hardware had been designed for. Accelerators — the specialised chips built to run neural networks — assumed dense arithmetic with heavy reuse: load a small tile of a matrix once, then multiply with it many times. Embedding lookup breaks that assumption completely, because each number fetched is used roughly once and then discarded. The result was expensive hardware running far below its rated speed on a workload that mattered commercially. The 2021 co-design paper is a document of that moment. It treats the embedding tables, their placement across devices, and the communication between them as first-class design problems, on the premise that no amount of faster arithmetic can fix a workload limited by data movement. Near-data processing starts from the same observation: the bottleneck had moved, and the honest response was to redesign around the new one rather than polish the old.
Why it matters now
The imbalance has not eased; it has become the defining constraint of machine learning systems. Large language models hit their own version of the same wall. They produce text 1 token — 1 word-fragment — at a time, and each step reads a great many stored weights while doing little arithmetic per byte read, so the pace is often set by memory bandwidth, not by compute. The vocabulary differs — key-value caches, weight streaming — but the diagnosis is the same, and so is the family of cures: move less data, move it a shorter distance, or do the work where the data already sits. Recommendation remains the clearest case study because the operation is too simple to hide anything. A gather followed by a sum has no clever kernel to blame. If it is slow, bytes are moving. That gives a useful diagnostic habit for any system: when the arithmetic units sit idle, do not ask which operation is expensive. Ask which bytes travel furthest, and whether any later step shrinks them. A step that reduces its inputs to something smaller — as pooling does — is a candidate for moving closer to the data.
The surprising detail
The striking thing is how little cleverness the idea needs. No new algorithm, no numerical trick — just an adder, one of the simplest parts in a computer, placed somewhere unusual. The difficulty is entirely institutional. Memory has spent decades defined as a thing that stores bytes and hands them back, with no opinion about them, and every interface and protocol in the stack encodes that assumption. Asking memory to also add is a small technical request made against every standard in between. The gap between how simple the computation is and how hard it is to put in the right place sums up why so much of modern systems work is about placement, not invention.
What is disputed
This lesson describes a mechanism and its motivating arithmetic, not a measured result. The single relevant source cited here is a 2021 co-design paper concerned with distributed training of recommendation models; it supports the claim that embedding tables and their data movement are treated as central design constraints, but it is not a benchmark of near-memory pooling hardware, and no speedup figure should be inferred from it. Reported gains for near-data processing in the wider literature vary widely with row width, pooling factor, table placement and where the logic sits, and much of the published work is simulated rather than measured on shipping silicon. Treat the direction of the argument as sound and any particular number as configuration-specific. The other listed source, a mathematics paper on extensions of beta functions, is unrelated to this topic and is not drawn on.
Remember this
Embedding lookup does roughly 1 addition per number read, so bytes moved set its speed, not arithmetic. The rows get summed anyway — so sum them beside the memory and send back 1 row instead of many.
Test yourself
Near-data processing helps embedding pooling because the operation reduces many rows to one. Suppose a model instead needed each fetched row kept separate, to be fed into an attention mechanism downstream. Would placing an adder near memory still help, and what does your answer tell you about when this technique applies?
It would not help in the same way, because there is nothing to compress. The saving in embedding pooling comes from the output being much smaller than the input: twenty rows in, one row out, so only one row crosses the expensive link. If every row must arrive intact at the processor, the same bytes travel regardless of where an adder sits, and the traffic is unchanged. The general lesson is that near-data processing pays off where the near-memory step reduces data volume. Look for operations that consume a lot and emit a little — sums, counts, maximums, filters that reject most of what they read. Operations that pass their input through unchanged, or expand it, gain nothing from relocation. This is why the technique is best understood as a claim about a specific data-flow shape rather than a general improvement to memory.
Go deeper
- [2006.15352] Extensions of beta and related functions · arxiv.org
- [2104.05158] Software-Hardware Co-design for Fast and Scalable Training of Deep Learning Recommendation Models · arxiv.org
Image: Original diagram, The Daily Triptych. Licence: Original work. Source.