惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
云风的 BLOG
云风的 BLOG
Microsoft Security Blog
Microsoft Security Blog
WordPress大学
WordPress大学
GbyAI
GbyAI
C
Check Point Blog
M
MIT News - Artificial intelligence
T
The Blog of Author Tim Ferriss
Jina AI
Jina AI
博客园 - 【当耐特】
U
Unit 42
月光博客
月光博客
腾讯CDC
Y
Y Combinator Blog
小众软件
小众软件
博客园_首页
Last Week in AI
Last Week in AI
酷 壳 – CoolShell
酷 壳 – CoolShell
The GitHub Blog
The GitHub Blog
博客园 - 聂微东
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
MongoDB | Blog
MongoDB | Blog
博客园 - Franky
T
Tailwind CSS Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Data-Oriented Design in C#: Why Objects Are Slowing You Down
Ian Cowley · 2026-06-23 · via DEV Community

Ian Cowley

Data-Oriented Design in C#: Why Objects Are Slowing You Down

In my previous article, we talked about starving the Garbage Collector by moving away from heap-allocated class types and leaning heavily into struct, Span<T>, and ArrayPool<T>.

That’s a critical first step, but it only solves half the problem. You’ve stopped the GC from pausing your app, but you might still be leaving massive amounts of CPU performance on the table. Why? Because of how your data is structured.

It’s time to talk about Data-Oriented Design (DoD).

The Object-Oriented Trap

We are taught from day one to model our code after the real world. If you are building a social network graph, you might write something like this:

public class UserNode
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Edge> Connections { get; set; }
}

public class Edge
{
    public UserNode Target { get; set; }
    public int Weight { get; set; }
}

This makes perfect logical sense. A user has connections, and those connections point to other users.

But modern CPUs don't care about your logical models. A CPU only cares about reading data from memory into its L1/L2 caches as fast as possible. When a CPU reads a byte from RAM, it doesn't just read that one byte; it pulls a whole 64-byte "cache line" under the assumption that you will probably want the neighboring bytes next.

When you loop through a List<UserNode>, traversing from object to object, you are jumping randomly across the heap. The CPU pulls a cache line, reads your data, and then has to go fetch a completely different block of RAM for the next node. This is called pointer chasing, and the resulting cache misses are devastating to performance.

Enter Data-Oriented Design: Struct of Arrays (SoA)

Data-Oriented Design says: Stop modeling the real world. Model the data the way the hardware wants to consume it.

Instead of an Array of Structs (AoS) (or an array of objects), we invert the architecture to a Struct of Arrays (SoA).

If we look at how the native DataFrame engine Glacier.Polaris or the graph engine Glacier.Graph operates, there are no Node or Edge classes. Instead, we use flat, primitive arrays.

To represent a graph, Glacier.Graph uses the Compressed Sparse Row (CSR) format. The entire graph structure is flattened into a few dense integer arrays:

public class CsrGraph
{
    // The index in the _to array where a node's edges begin
    private readonly int[] _head; 

    // The target node IDs
    private readonly int[] _to;   

    // The relationship types or weights
    private readonly int[] _relation; 
}

The Cache-Friendly Loop

Let's say we want to find all connections for Node 5. In the OOP world, we follow pointers on the heap. In the CSR world, we do this:

int startEdgeIndex = _head[5];
int endEdgeIndex = _head[6];

// Look at how perfectly sequential this is!
for (int i = startEdgeIndex; i < endEdgeIndex; i++)
{
    int targetNode = _to[i];
    int relationWeight = _relation[i];

    // Process connection...
}

Why is this so much faster?
Because _to and _relation are dense, contiguous int arrays. As we loop through i, the CPU's pre-fetcher easily predicts our access pattern. It loads the cache lines ahead of time. By the time our loop needs _to[i+1], it is already sitting in the blazing fast L1 cache.

The Secret Weapon: SIMD

But cache lines are only the beginning. Once your data is sitting in a flat primitive array, you unlock the real superpower of modern .NET: SIMD (Single Instruction, Multiple Data).

You cannot pass an array of UserNode objects into an AVX-512 vector register to evaluate them simultaneously. But you can load 8 consecutive integers from that _relation array into a Vector256<int> and evaluate them all in a single CPU clock cycle.

When you align your memory this way, C# allows you to drop right down to the metal. This is exactly how Glacier.Chrono hits over 2 Billion operations per second without breaking a sweat.

The Bottom Line

Object-Oriented Programming is fantastic for UI components and high-level business logic. But when you drop down into the engine room—when you are building dataframes, graph databases, or processing millions of records a second—you have to think like the CPU.

Flatten your objects. Separate your properties into contiguous arrays. Design for cache hits, and your code will run orders of magnitude faster.