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

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
aimingoo的专栏
aimingoo的专栏
Microsoft Security Blog
Microsoft Security Blog
NISL@THU
NISL@THU
T
Threatpost
T
The Exploit Database - CXSecurity.com
T
Threat Research - Cisco Blogs
S
Securelist
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
人人都是产品经理
人人都是产品经理
B
Blog RSS Feed
S
Secure Thoughts
MyScale Blog
MyScale Blog
O
OpenAI News
P
Palo Alto Networks Blog
美团技术团队
C
Cyber Attacks, Cyber Crime and Cyber Security
TaoSecurity Blog
TaoSecurity Blog
量子位
L
Lohrmann on Cybersecurity
G
GRAHAM CLULEY
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Tailwind CSS Blog
Know Your Adversary
Know Your Adversary
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Simon Willison's Weblog
Simon Willison's Weblog
宝玉的分享
宝玉的分享
PCI Perspectives
PCI Perspectives
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
Cybersecurity and Infrastructure Security Agency CISA
T
Tenable Blog
I
InfoQ
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Microsoft Azure Blog
Microsoft Azure Blog
Recent Announcements
Recent Announcements
S
Security @ Cisco Blogs
S
Schneier on Security
B
Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
The Cloudflare Blog
AWS News Blog
AWS News Blog
IT之家
IT之家
V
Vulnerabilities – Threatpost
The Hacker News
The Hacker News
H
Heimdal Security Blog
I
Intezer
A
Arctic Wolf
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
H
Help Net Security
W
WeLiveSecurity

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 Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
The Rust Performance Trap I Hit While Sorting Small Network Datasets
amir · 2026-06-20 · via DEV Community

The Rust Performance Trap I Hit While Sorting Small Network Datasets

I recently ran into one of those performance problems that looks completely obvious in hindsight, but was surprisingly hard to notice while I was inside the code.

I was working on a deep search and ranking part of a Rust project that processed network-related data.

The system had to evaluate many small groups of candidates repeatedly. You can think of these candidates as possible network paths, intermediate nodes, route-like records, latency candidates, or weighted decisions inside a search tree.

At each step, I had a small list of candidates.

Not thousands of items.

Usually something like 10, 20, maybe 40 or 50.

Each candidate needed to be scored and ordered before the search could continue.

The first version of the code was simple and readable:

let mut candidates = Vec::new();

for item in input {
    candidates.push(item);
}

candidates.sort_by_key(|item| compute_score(item));

It worked.

It was clean.

But after profiling it with a flamegraph, I noticed something uncomfortable: the program was spending too much time around temporary allocations.

At first, that sounds strange. A small Vec does not look expensive.

But in a deep search algorithm, a "small allocation" repeated millions of times is no longer small.

That was the beginning of the rabbit hole.


The First Bottleneck: Heap Allocation

The first problem was heap allocation.

Every time the search visited a node, it created temporary vectors to collect, score, and sort candidates. Since the search tree was deep, this happened again and again.

One allocation does not matter much.

Thousands of allocations may still be fine.

But millions of tiny allocations inside a hot path can become a serious bottleneck.

The important thing is this:

Heap allocation is not just "getting some memory".

There is real machinery behind it.

When a program allocates memory on the heap, the allocator has to find a suitable block of memory, update internal metadata, sometimes handle alignment, sometimes split or reuse blocks, and later track that memory so it can be freed safely.

Even if the allocator is very optimized, this is still more expensive than using memory that is already available on the stack.

With stack memory, the cost is usually very small. The program mostly adjusts the stack pointer. It is simple, predictable, and extremely cache-friendly.

Heap memory is more flexible, but that flexibility has a price.

In my case, I did not need flexibility.

I already knew the candidate lists were small. I did not need an unbounded dynamic vector for every step of the search. I needed a tiny temporary buffer.

But I was still paying the cost of heap allocation over and over again.


Why Heap Allocation Can Be Expensive in a Hot Path

Heap allocation becomes especially problematic when it happens inside a tight loop or a deeply repeated algorithm.

There are a few reasons for that.

1. Allocator Overhead

A heap allocator has to manage memory dynamically.

That means it needs bookkeeping.

When I create a Vec, Rust itself is not doing something inefficient. Vec is a great data structure. But when it needs capacity, it asks the allocator for heap memory.

The allocator has to find a free region that fits the requested size. It may reuse an existing block, split a larger block, or request more memory from the system.

Even when this is fast, it is not free.

In normal application code, this cost may not matter. In a hot path, it can dominate the runtime.

2. Memory Locality

Stack memory is usually very close to the current execution context.

Small arrays on the stack tend to be friendly to the CPU cache. The CPU can load nearby memory efficiently, and the access pattern is usually predictable.

Heap memory can be more scattered.

If temporary vectors are allocated and freed repeatedly, the actual memory locations may not be as local as a compact stack buffer.

This matters because modern CPUs are extremely fast, but memory is relatively slow. A lot of real-world performance comes down to whether the CPU can keep useful data in cache.

If the CPU has to wait for memory, the algorithm may look good on paper but still run slower in practice.

3. Fragmentation and Metadata

Heap allocators also maintain metadata.

They need to know which blocks are free, which blocks are used, and how large those blocks are.

Over time, allocation and deallocation patterns can create fragmentation. For small short-lived allocations, modern allocators are usually smart, but they still have to manage the lifecycle of memory.

For a deep search algorithm, this was unnecessary noise.

The lifetime of the data was very short. Each temporary list only lived during one search step.

That is exactly the kind of data that usually fits better on the stack.

4. More Pressure on the CPU Cache

The real issue was not only the cost of allocation itself.

The bigger problem was the combination of allocation, sorting, scoring, and repeated traversal.

The algorithm was doing many tiny operations. Each operation looked harmless, but together they created pressure on the CPU cache.

Temporary heap allocations increased the amount of memory traffic. The CPU had to touch more memory, follow more pointers, and deal with less predictable access patterns.

When code runs once, this is not a big deal.

When code runs inside the hottest part of a search engine, it matters.


The First Optimization: Remove Temporary Heap Allocation

So my first idea was simple:

Stop creating temporary vectors in the hot path.

Instead of collecting candidates into a heap-allocated Vec, I moved toward a fixed-size temporary buffer.

Conceptually, something like this:

const MAX_CANDIDATES: usize = 64;

let mut buffer: [MaybeUninit<ScoredCandidate>; MAX_CANDIDATES] = unsafe {
    MaybeUninit::uninit().assume_init()
};

The exact implementation depends on the project, of course. The important part is the idea:

  • the number of candidates was bounded
  • the temporary data had a very short lifetime
  • the buffer could live on the stack
  • no heap allocation was needed for every search node

After this change, I expected the program to become faster.

And partially, I was right.

The allocation cost disappeared from the flamegraph.

But then I hit the second trap.


The Surprising Part: Removing Allocations Made It Slower

After removing the temporary heap allocation, I used Rust's standard sorting method:

items.sort_unstable_by_key(|item| compute_score(item));

At first glance, this looked like the right choice.

It is in-place. It avoids extra allocation. It is part of the standard library. It uses a serious optimized sorting algorithm.

But the program became slower.

That was confusing.

I had removed heap allocation. I had improved memory usage. I was using a well-optimized standard method.

So why did performance drop?

The flamegraph gave me the answer.

The allocation cost was gone, but now the program was spending too much time recomputing scores.

The key detail is that sort_unstable_by_key does not cache the key for every item.

The closure can be called multiple times during sorting.

That is completely fine when the key is cheap, like reading an integer field:

items.sort_unstable_by_key(|item| item.priority);

But my key was not cheap.

The score calculation was based on multiple network-related signals, such as:

  • estimated latency
  • hop penalty
  • node priority
  • route freshness
  • failure probability
  • domain-specific weights

So every comparison could trigger real computation again.

And again.

And again.

I had removed heap allocation, but I had accidentally introduced repeated CPU work in the hottest part of the program.

The code looked clean, but it was doing more work than it appeared.


Why the Default Sort Was Not the Best Tool Here

Rust's sort_unstable_by_key is not bad.

Actually, it is very good.

The problem was not Rust.

The problem was that I used a general-purpose sorting method for a very specific workload.

Modern sorting algorithms are excellent for large arrays and general cases. Rust's unstable sort is designed to perform well across many real-world patterns.

But my workload had two special properties:

  1. The arrays were very small.
  2. The key calculation was relatively expensive.

For large arrays, an O(N log N) sort is usually the obvious choice.

But for tiny arrays, Big-O can be misleading.

When N is 10, 20, or 40, constant factors matter a lot.

At that size, the overhead of the sorting algorithm, the number of comparisons, branch behavior, cache locality, and repeated score calculation can matter more than the theoretical complexity.

In my case, the theoretical advantage of the sorting algorithm was less important than the practical cost of using it in a tiny hot loop.


The Fix: Cache the Scores and Sort a Small Stack Buffer

The final solution was simple:

  1. Compute the score for each candidate exactly once.
  2. Store the candidate and its score in a small stack buffer.
  3. Sort the scored candidates using insertion sort.

Conceptually:

struct ScoredCandidate<T> {
    score: i32,
    item: T,
}

Instead of asking the sorting algorithm to repeatedly call compute_score, I made the score part of the temporary data.

The scoring step became linear:

for candidate in candidates {
    let score = compute_score(candidate);

    scored.push(ScoredCandidate {
        score,
        item: candidate,
    });
}

After that, sorting only compared already-computed integers.

That completely changed the cost profile.

The expensive part happened once per candidate, not many times per comparison.


Why Insertion Sort Worked Better

For the sorting step, I used insertion sort.

Yes, insertion sort.

The algorithm that is usually introduced early in computer science classes and then quickly ignored because it is O(N²).

But for small arrays, insertion sort can be extremely fast.

Why?

Because it is simple.

It has very little overhead. It works well with contiguous memory. It does not need extra allocation. It has predictable behavior. And when the array is small, the quadratic complexity does not have enough room to become a problem.

For example, sorting 20 or 30 items with insertion sort is not scary.

Especially when the data is already in a small stack buffer and comparisons are cheap integer comparisons.

In this case, insertion sort matched the workload better than a more advanced general-purpose sorting algorithm.

This is one of those cases where the "worse" algorithm on paper was better for the real machine.


The Result

After the change, the flamegraph looked completely different.

The repeated scoring calls disappeared from the hot path.

The allocation-related noise was gone.

The middle layers of the search became much faster.

The biggest improvement did not come from one magical trick. It came from aligning the implementation with the real shape of the workload:

  • small candidate lists
  • deep repeated search
  • short-lived temporary data
  • expensive scoring
  • no need for heap flexibility
  • stack-friendly memory layout
  • simple sorting over cached scores

The final version was not more complicated in spirit.

It was actually more honest about what the program was doing.

The program did not need a dynamic vector and a general-purpose sort at every search node.

It needed a tiny local ranking buffer.


The Lesson

The main lesson for me was this:

The fastest algorithm in theory is not always the fastest algorithm on real hardware.

Big-O matters, but it is not the whole story.

In performance-sensitive code, especially backend, networking, infrastructure, or systems code, the real questions are often more practical:

  • How large is the data really?
  • How often does this code run?
  • Is this allocation inside a hot path?
  • Is the temporary memory short-lived?
  • Is the key cheap or expensive?
  • Are we recomputing something that could be cached?
  • Is the memory layout friendly to the CPU cache?
  • Does a general-purpose standard method match this exact workload?

Standard library methods are great defaults. Most of the time, they are exactly what we should use.

But "default" does not mean "always optimal".

In my case, the better solution was:

  • avoid repeated heap allocations
  • compute scores once
  • store small temporary data on the stack
  • use insertion sort for tiny arrays

That was enough to beat the more advanced-looking approach.


Final Thought

This experience reminded me that performance work is not only about knowing algorithms.

It is about understanding the shape of the data, the lifetime of memory, and how the CPU actually executes the code.

Sometimes the bottleneck is not where you expect it to be.

Sometimes removing an allocation exposes another hidden cost.

And sometimes the "bad" O(N²) algorithm is exactly what your CPU wanted.