最近一段时间,我在做Code Review和优化重构的工作。我发现在很多代码场景下,如果使用Span<T>
之后,无往不利,代码的性能提升显著,神挡杀神佛挡杀佛。迫不及待地给大家安利一下~
我将拿出其中两个案例,给大家介绍一下如何用Span<T>
来优化代码,达到性能提升的效果。
在开始我的表演之前,还是先简单介绍一下Span是什么玩意吧。下面这一段话是网上copy过来的,如果看不懂的话,直接去搜索一下,或者忽略它直接看代码就行了!
什么是Span<T>
?
Span<T>
是在 C# 7.2 后开始出现的语法,它为我们提供了一种高效且安全地对内存进行读写操作。通过 Span<T>
,我们可以直接操作数组、堆栈、堆等内存区域,可以避免不必要的内存分配和拷贝,从而提高代码的性能和效率。
Span<T>
有哪些应用场景?
数组操作: Span 可以直接操作数组中的元素,而不需要额外的内存拷贝,适用于需要高效处理数组数据的场景。
字符串处理: Span 可以用于高效地处理字符串,例如字符串拆分、搜索、替换等操作,避免不必要的字符串分配和拷贝。
内存池管理: Span 可以与内存池一起使用,提高内存分配和释放的效率,减少 GC 压力。
文件 I/O 操作: 在文件读写等 I/O 操作中,Span 可以减少内存拷贝开销,提高读写效率。
网络编程: 在网络编程中,Span 可以用于处理网络数据包、解析协议等操作,提高网络数据处理的效率。
异步编程: Span 可以与异步编程结合使用,提供高效的数据处理方式,例如在处理大量数据时减少内存拷贝开销。
使用 Span 进行内存操作
// 创建一个包含整型数据的数组
int[] array = new int[] { 1, 2, 3, 4, 5 };
// 使用 Span 对数组进行操作
Span<int> span = array.AsSpan();
span[2] = 10; // 修改第三个元素的值为 10
// 输出修改后的数组
foreach (var num in array)
{
Console.Write(num);
}
// 输出结果:121045
通过以上简单的代码示例,我们可以看到如何使用 Span<T>
对数组进行直接操作,并修改其中的元素值。下面继续,举几个我在实际开发场景中所用到的案例,通过这些示例,希望能够让大家更进一步的了解Span<T>
的实际运用。并且,我会拿出优化前的代码来进行性能的对比,这样大家就能够直观感受到Span<T>
的威力了。
案例一:字符串的拆分及数组类型的转换
我需要将以下的字符串中逗号连接的数字才分出来,并且转成int数组。
private const string StringForSplit = "666,747,200,468,471,395,942,589,87,353,456,536,772,599,552,338,553,925,532,383,668,96,61,125,621,917,774,146,54,885";
1、优化前的代码:
public int[] TestSplitString()
{
return StringForSplit.Split(',').Select(x => Convert.ToInt32(x)).ToArray();
}
2、Span<T>
优化的代码:
public int[] TestSplitStringWithSpan()
{
var span = StringForSplit.AsSpan();
var separator = ",".AsSpan();
var sepLen = separator.Length;
var index = -1;
var result = new List<int>();
do
{
index = span.IndexOf(separator);
if (index == -1)
{
result.Add(int.Parse(span));
}
else
{
var value = span.Slice(0, index);
result.Add(int.Parse(value));
span = span.Slice(index + sepLen);
}
}
while (index != -1);
return result.ToArray();
}
3、性能比对
从上图数据,我们可以看出代码优化效果非常明显,内存节省了64%,运行速度提升了30%。
案例二:提取HTML代码中的文本内容
我需要将以下的HTML代码中的Country信息提取出来。
const string HtmlCode = @"<html>
<head>
<meta charset=""utf-8"">
<title>Test Page</title>
</head>
<body>
<header>...</header>
<main>
<article>
<h3>Country list</h3>
<ul>
<li><span>Australia</span></li>
<li><span>Brazil</span></li>
<li><span>Canada</span></li>
<li><span>China</span></li>
<li><span>France</span></li>
<li><span>Germany</span></li>
<li><span>Japan</span></li>
<li><span>South Korea</span></li>
<li><span>United States</span></li>
<li><span>United Kingdom</span></li>
</ul>
</article>
</main>
<footer>...</footer>
</body>
</html>";
1、优化前的代码:
解决这个问题,大家首先会想到的是用正则表达式。没错,之前写代码的家伙也是这么想的。
public string[] TestFilterWithRegularExpression()
{
const string REGX_PTN = @"<li><span>(.*)<\/span><\/li>";
var matches = Regex.Matches(HtmlCode, REGX_PTN);
var result = new List<string>();
foreach (Match match in matches)
{
result.Add(match.Groups[1].Value);
}
return result.ToArray();
}
2、Span<T>
优化的代码:
public string[] TestFilterWithSpan()
{
const string countryBegin = "<h3>Country list</h3>";
const string countryEnd = "</ul>";
const string startTag = "<li><span>";
const string endTag = "</span>";
var span = HtmlCode.AsSpan();
var countrySpan = countryBegin.AsSpan();
int index = span.IndexOf(countrySpan);
span = span.Slice(index + countrySpan.Length);
index = span.IndexOf(countryEnd.AsSpan());
span = span.Slice(0, index);
var startTagSpan = startTag.AsSpan();
var endTagSpan = endTag.AsSpan();
var startTagLen = startTagSpan.Length;
var endTagLen = endTagSpan.Length;
var result = new List<string>();
while (true)
{
index = span.IndexOf(startTagSpan);
var endIndex = span.IndexOf(endTagSpan);
if (index == -1 || endIndex ==-1) break;
var value = span.Slice(index + startTagLen, endIndex -index-startTagLen);
result.Add(value.ToString());
span = span.Slice(endIndex + endTagLen);
}
return result.ToArray();
}
3、性能比对
这一组数据对比下来,用“惊艳”两个来形容也不为过吧?Span<T>
优化过后的代码,无论在运行速度,还是内存使用方面的指标,都是数倍甚至10倍的提升!
总结
总的来说, Span