深入了解 C# Span:高性能内存操作的利器

在 C# 7.2 中引入的 Span<T> 类型为我们提供了一种高效且安全地对内存进行操作的方式。Span<T> 是一个轻量级的结构体,用于表示一段连续的内存区域,可以避免不必要的内存分配和拷贝,提高代码的性能和效率。

什么是 Span?

Span<T> 是一个用于表示连续内存区域的结构体,它提供了一组方法来对内存进行读写操作,而无需额外的内存分配或拷贝。通过 Span<T>,我们可以直接操作数组、堆栈、堆等内存区域,从而提高代码的性能和效率。

主要特点:

  • 零分配:Span 提供了零分配的内存操作方式,避免了在一些情况下不必要的内存分配和拷贝。这对于性能敏感的应用程序非常有益。

  • 安全性:Span 提供了安全的内存访问方式,确保在访问内存时不会发生越界访问或其他不安全操作。

  • 高效性:通过 Span,可以直接对内存进行读写操作,避免了额外的内存拷贝和装箱操作,提高了代码的性能和效率。

  • 可变性:Span 是可变的,可以修改指向的内存中的数据,从而实现高效的数据操作和处理。

应用场景

  • 数组操作:Span 可以直接操作数组中的元素,而不需要额外的内存拷贝,适用于需要高效处理数组数据的场景。

  • 字符串处理:Span 可以用于高效地处理字符串,例如字符串拆分、搜索、替换等操作,避免不必要的字符串分配和拷贝。

  • 内存池管理:Span 可以与内存池一起使用,提高内存分配和释放的效率,减少 GC 压力。

  • 文件 I/O 操作:在文件读写等 I/O 操作中,Span 可以减少内存拷贝开销,提高读写效率。

  • 网络编程:在网络编程中,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.WriteLine(num);
}

通过以上示例,我们可以看到如何使用 Span<T> 对数组进行直接操作,并修改其中的元素值。

案例一: 借助Span字符串转int和float

public static int ParseToInt(this ReadOnlySpan<char> rspan)
{
Int16 sign = 1;
int num = 0;
UInt16 index = 0;
if (rspan[0].Equals('-')){
sign = -1; index = 1;
}
for (int idx = index; idx < rspan.Length; idx++){
char c = rspan[idx];
num = (c - '0') + num * 10;
}
return num * sign;
} public static float ParseToFloat(this ReadOnlySpan<char> span)
{
bool isNegative = false;
int startIndex = 0; // 判断是否为负数
if (span.Length > 0 && span[0] == '-')
{
isNegative = true;
startIndex = 1;
} bool hasDecimal = false;
float result = 0;
float decimalPlace = 0.1f; for (int i = startIndex; i < span.Length; i++)
{
char c = span[i]; if (c == '.')
{
hasDecimal = true;
continue;
} if (c < '0' || c > '9')
{
Debug.LogError("Invalid digit in input");
} if (!hasDecimal)
{
result = result * 10 + (c - '0');
}
else
{
result += (c - '0') * decimalPlace;
decimalPlace /= 10;
}
} if (isNegative)
{
result *= -1;
} return result;
}

案例二:字符串分割转换

 public void TestSpanParse()
{
List<Vector3> vertices = new List<Vector3>();
List<Vector3> colors = new List<Vector3>();
string[] lines = new string[lineCount];
for (int i = 0; i < lineCount; i++)
{
lines[i] = $"{i} {i + 0.1f} {i + 0.5f} {i} {i + 0.1f} {i + 0.5f}";
} using (CustomTimer ct = new CustomTimer("TestSpanParse", -1))
{
foreach (var line in lines)
{
ReadOnlySpan<char> sp = line.AsSpan();
int spaceIndex1 = sp.IndexOf(' ');
int spaceIndex2 = sp.Slice(spaceIndex1 + 1).IndexOf(' ');
int spaceIndex3 = sp.Slice(spaceIndex1 +spaceIndex2 + 2).IndexOf(' ');
int spaceIndex4 = sp.Slice(spaceIndex1 +spaceIndex2+spaceIndex3 + 3).IndexOf(' ');
int spaceIndex5 = sp.Slice(spaceIndex1 +spaceIndex2+spaceIndex3 +spaceIndex4 + 4).IndexOf(' ');
var xSp = (sp.Slice(0, spaceIndex1));
var ySp = sp.Slice(spaceIndex1 + 1, spaceIndex2);
var zSp = sp.Slice(spaceIndex1 +spaceIndex2 + 2, spaceIndex3);
var rSp= sp.Slice(spaceIndex1 +spaceIndex2+spaceIndex3 + 3, spaceIndex4);
var gSp = sp.Slice(spaceIndex1 +spaceIndex2+spaceIndex3 +spaceIndex4 + 4, spaceIndex5);
var bSp = sp.Slice(spaceIndex1 +spaceIndex2+spaceIndex3 +spaceIndex4 + spaceIndex5+5);
vertices.Add(new Vector3(float.Parse(xSp), float.Parse(ySp), float.Parse(zSp)));
colors.Add(new Vector3(float.Parse(rSp), float.Parse(gSp), float.Parse(bSp)));
}
Debug.Log(vertices.Last().ToString()+" "+colors.Last().ToString());
} using (CustomTimer ct = new CustomTimer("TestParse", -1))
{
foreach (var line in lines)
{
var split = line.Split(' ');
vertices.Add(new Vector3(float.Parse(split[0]), float.Parse(split[1]), float.Parse(split[2])));
colors.Add(new Vector3(float.Parse(split[3]), float.Parse(split[4]), float.Parse(split[5])));
}
Debug.Log(vertices.Last().ToString()+" "+colors.Last().ToString());
}
}

性能测试结果:150000次调用

案例三:字符串处理

using UnityEngine;

public class StringProcessor : MonoBehaviour
{
void Start()
{
string text = "Hello, World!";
Span<char> span = text.AsSpan(); Debug.Log(ReverseString(span)); // 输出 "!dlroW ,olleH"
} string ReverseString(Span<char> span)
{
char[] reversed = new char[span.Length];
for (int i = 0; i < span.Length; i++)
{
reversed[i] = span[span.Length - 1 - i];
}
return new string(reversed);
}
}

案例四:优化网络数据处理

using UnityEngine;
using System;
using System.Net.Sockets; public class NetworkProcessor : MonoBehaviour
{
private Socket socket; void Start()
{
// 假设socket已经连接
byte[] buffer = new byte[1024];
int bytesRead = socket.Receive(buffer);
ProcessData(buffer, bytesRead);
} void ProcessData(byte[] data, int length)
{
Span<byte> dataSpan = new Span<byte>(data, 0, length); // 处理数据
// 例如:解析消息,处理命令等
for (int i = 0; i < dataSpan.Length; i++)
{
Debug.Log(dataSpan[i]);
}
}
}

案例五:图像处理

using UnityEngine;
using System; public class ImageProcessor : MonoBehaviour
{
public Texture2D texture; void Start()
{
Color32[] pixels = texture.GetPixels32();
ProcessImage(pixels); texture.SetPixels32(pixels);
texture.Apply();
} void ProcessImage(Color32[] pixels)
{
Span<Color32> pixelSpan = new Span<Color32>(pixels); for (int i = 0; i < pixelSpan.Length; i++)
{
Color32 pixel = pixelSpan[i];
pixel.r = (byte)(255 - pixel.r); // 反转红色分量
pixel.g = (byte)(255 - pixel.g); // 反转绿色分量
pixel.b = (byte)(255 - pixel.b); // 反转蓝色分量
pixelSpan[i] = pixel;
}
}
}

结语

Span<T> 是 C# 中一个强大且高效的工具,可以帮助我们更好地进行

深入了解 C# Span:高性能内存操作的利器的更多相关文章

  1. span<T>之高性能字符串操作实测

    .net中的字符串操作性能问题由来已久,幸运的是微软推出了span<T>高性能指针操作封装工具类.这个类到底有多高的性能呢?网上传言反正很高,但是实际上在网上很难找到合适的测试实例,这让本 ...

  2. 如何使用T-SQL备份还原数据库及c#如何调用执行? C#中索引器的作用和实现。 jquery控制元素的隐藏和显示的几种方法。 localStorage、sessionStorage用法总结 在AspNetCore中扩展Log系列 - 介绍开源类库的使用(一) span<T>之高性能字符串操作实测

    如何使用T-SQL备份还原数据库及c#如何调用执行? 准备材料:Microsoft SQL Server一部.需要还原的bak文件一只 一.备份 数据库备份语句:user master backup ...

  3. 支撑百万级并发,Netty如何实现高性能内存管理

    Netty作为一款高性能网络应用程序框架,实现了一套高性能内存管理机制 通过学习其中的实现原理.算法.并发设计,有利于我们写出更优雅.更高性能的代码:当使用Netty时碰到内存方面的问题时,也可以更高 ...

  4. JUC并发编程与高性能内存队列disruptor实战-下

    并发理论 JMM 概述 Java Memory Model缩写为JMM,直译为Java内存模型,定义了一套在多线程读写共享数据时(成员变量.数组)时,对数据的可见性.有序性和原子性的规则和保障:JMM ...

  5. java 21-11 数据输入、输出流和内存操作流

    IO数据流: 可以读写基本数据类型的数据 数据输入流:DataInputStream DataInputStream(InputStream in)   数据输出流:DataOutputStream ...

  6. 【转】《深入理解计算机系统》C程序中常见的内存操作有关的典型编程错误

    原文地址:http://blog.csdn.net/slvher/article/details/9150597 对C/C++程序员来说,内存管理是个不小的挑战,绝对值得慎之又慎,否则让由上万行代码构 ...

  7. c++ void,内存操作函数

    void的含义 void的字面意思是“无类型”, void * 则为“无类型指针”, void * 可以指向任何类型的数据 void几乎只有“注释”和限制程序的作用,因为从来没有人会定义一个void变 ...

  8. java基础知识回顾之javaIO类--内存操作流ByteArrayInputStream和ByteArrayOutputSteam(操作字节数组)

    直接看代码: package cn.itcast.io.p6.bytestream; import java.io.ByteArrayInputStream; import java.io.ByteA ...

  9. Java API —— IO流(数据操作流 & 内存操作流 & 打印流 & 标准输入输出流 & 随机访问流 & 合并流 & 序列化流 & Properties & NIO)

    1.操作基本数据类型的流     1) 操作基本数据类型 · DataInputStream:数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型.应用程序可以使用数据输出 ...

  10. 【C++基础】内存操作 getMemory改错

    内存操作的考察点:①指针 ②变量生存期及作用范围 ③动态内存申请和释放 笔试题************************************************************* ...

随机推荐

  1. [Trading] 买卖如何移动 ( 影响 ) 市场价格

    大多数人都知道市场价格的变化是因为买卖行为,但却没有多少人了解买卖行为是如何影响市场价格的. 乍一看,这可能令人困惑,因为每一笔市场交易都要求总有一个买家和一个卖家. 首先,重要的是要明白市场上总是有 ...

  2. 火山引擎VeDI:如何高效使用A/B实验,优化APP推荐系统

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 在移动互联网飞速发展的时代,用户规模和网络信息量呈现出爆炸式增长,信息过载加大了用户选择的难度,这样的背景下,推荐 ...

  3. CentOS-7卸载了python2.7,yum不可用的解决方法

    1.mount挂载iso镜像 [root@localhost software]# mount -t iso9660 -o loop CentOS-7-x86_64-DVD-2003.iso /med ...

  4. 模型压缩与部署-书生浦语大模型实战营学习笔记5&大语言模型11

    大语言模型-11.模型压缩与部署 书生浦语大模型实战营学习笔记4-模型压缩与部署 本文包括第二期实战营的第5课内容,介绍关于模型压缩的相关内容,主要包括.模型量化和模型部署的相关内容. 模型部署 定义 ...

  5. WebKist Inside: CSS 样式表的组成

    1 StyleSheet 一张 StyleSheet 由一系列 Rules 组成,这些 Rules 可以分成 2 大类: 1 Style Rule 2 At-Rule 下面的例子展示了 Style R ...

  6. RemoteView 替代品和类似软件

    RemoteView 是一款远程控制软件,使您可以通过Internet连接远程访问计算机和移动设备,而不受时间和地点的限制. 您可以快速,安全地实时轻松地控制计算机和移动设备. 您可以使用我们的iOS ...

  7. URP(Universal Render Pipeline)渲染管线在使用中的一些分享

    本篇文章整理了URP管线使用中的一些简单的心得记述 1.使用ScriptableRendererFeature自定义渲染特性 在内建(Build-in)管线中可以使用CommandBuffer并添加到 ...

  8. java学习之旅(day.04)

    运算符 算术运算符:+ ,- ,* ,/,%(取余或模运算), ++(自增),-- (自减) 赋值运算符:= 关系运算符:>, <,>=, <=, ==, !=(不等于),in ...

  9. 带你阅读Naive Ui Admin后台管理源码,并手撸JS版本

    Naive Ui Admin 是一个基于 Vue3.0.Vite. Naive UI.TypeScript 的中后台解决方案,它使用了最新的前端技术栈,并提炼了典型的业务模型,页面,包括二次封装组件. ...

  10. flask blinker信号

    Flask框架中的信号基于blinker,其主要就是让开发者可是在flask请求过程中定制一些用户行为. pip3 install blinker 1.内置信号 request_started = _ ...