Pre-allocate Like a Pro
Pre-allocate Like a Pro
像专业人士一样预分配内存
I’ve been playing with C# 15 quite a bit this week, and after yesterday’s post about the new with() syntax I started thinking about something I’ve always struggled with — making code both clean and actually fast. You know how it goes. You write a quick list, start adding things in a loop, and everything works fine… until your data set grows and suddenly you’re wondering why things feel sluggish. I’ve been guilty of this more times than I care to admit.
这周我一直在研究 C# 15,在写完昨天关于新 with() 语法的文章后,我开始思考一个我一直很纠结的问题——如何让代码既简洁又高效。大家都懂的:你快速写了一个列表,在循环中添加元素,一切看起来都很正常……直到数据量增长,你突然发现程序变慢了。我在这方面犯过的错多到数不清。
So I decided to do a quick experiment. I created a list with 100,000 items three different ways and timed each one. Nothing fancy, just my laptop and a stopwatch. The results surprised me a little. Here’s what I tried:
所以我决定做一个简单的实验。我用三种不同的方式创建了一个包含 10 万个元素的列表,并分别记录了耗时。没什么复杂的,就是用我的笔记本电脑和秒表测了一下。结果让我有些惊讶。以下是我尝试的方法:
First, the way I used to write it without thinking twice:
List<int> items = new(); for (int i = 0; i < 100_000; i++) items.Add(i);
首先是我以前不假思索的写法:
List<int> items = new(); for (int i = 0; i < 100_000; i++) items.Add(i);
Second, the version where I at least tried to be responsible:
List<int> items = new(100_000); for (int i = 0; i < 100_000; i++) items.Add(i);
其次是我至少尝试过“负责任”的写法:
List<int> items = new(100_000); for (int i = 0; i < 100_000; i++) items.Add(i);
And finally, the C# 15 version:
List<int> items = [with(capacity: 100_000), ..Enumerable.Range(0, 100_000)];
最后是 C# 15 的写法:
List<int> items = [with(capacity: 100_000), ..Enumerable.Range(0, 100_000)];
Day 2 Mini-Challenge
第二天迷你挑战
If you have ten minutes, throw together a small console app that runs all three and prints the times. Don’t make it perfect — just something simple and honest. Here’s the little program I ended up with:
如果你有十分钟时间,可以写一个简单的控制台程序来运行这三种方式并打印耗时。不用追求完美,简单直接就好。这是我最终写出的程序:
using System.Diagnostics;
Console.WriteLine("Testing pre-allocation in C# 15");
const int Size = 100_000;
var sw = Stopwatch.StartNew();
// No capacity at all
sw.Restart();
List<int> noCap = new();
for (int i = 0; i < Size; i++) noCap.Add(i);
Console.WriteLine($"No capacity : {sw.ElapsedMilliseconds} ms");
// Old-school capacity
sw.Restart();
List<int> old = new(Size);
for (int i = 0; i < Size; i++) old.Add(i);
Console.WriteLine($"Traditional capacity : {sw.ElapsedMilliseconds} ms");
// New with() syntax
sw.Restart();
List<int> modern = [with(capacity: Size), ..Enumerable.Range(0, Size)];
Console.WriteLine($"C# 15 with(capacity) : {sw.ElapsedMilliseconds} ms");
When I ran it a few times, the with() version kept coming out on top. Sometimes the difference wasn’t huge, but it was consistently faster, and honestly the code just feels better to look at. I didn’t expect to get this excited about something as basic as pre-allocating memory, but here we are.
当我运行了几次之后,发现 with() 版本总是表现最好。虽然有时差距并不巨大,但它始终更快,而且老实说,这段代码看起来更舒服。我没想到自己会对“预分配内存”这种基础操作感到如此兴奋,但事实就是这样。
— A regular developer who still geeks out over this stuff —— 一个依然会对这些技术细节感到兴奋的普通开发者