首页 > 系统相关 >深入了解 C# Span:高性能内存操作的利器

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

时间:2024-06-07 15:46:46浏览次数:28  
标签:sp Span C# float Parse int 内存

深入了解 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次调用

image-20240606150647856

结语

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

标签:sp,Span,C#,float,Parse,int,内存
From: https://www.cnblogs.com/Firepad-magic/p/18237293

相关文章

  • SVC推理参数说明
    WebUI参数说明选择主模型文件主模型配套的配置文件主模型配套的扩散模型扩散模型配套的配置文件聚类模型或特征检索。(可选)        聚类模型:需单独训练聚类模型,其可以减小音色泄露,使得音色更接近于原声(效果不是很明显)。单纯的完全使用聚类模型,会导致出现口齿不......
  • SVC数据集准备及预处理
      此文档主要为SVC数据集预处理的详细步骤。音源准备时长要求:训练音源需准备至少20min以上,最好是1-2小时的数据。注:由于歌曲中歌手并不会整首歌都在演唱,因此这里的时长说的是歌手实际演唱的时长,不包括前奏、间奏等无歌声的部分。质量要求:训练音源尽量使用高保真及以上品......
  • Docker 下载redis
    docker拉取redis镜像dockerpullredis:对应的版本号(不写默认为最新版) 查看镜像是否拉取成功dockerimages 创建redis配置文件启动前需要先创建reids的外部挂载的配置文件(/opt/docker/redis/conf/redis.conf)(因为redis本身容器只存在/etc/redis目录,本身就不创建redis......
  • 实验6_C语言结构体、枚举应用编程
    实验任务4#include<stdio.h>#defineN10typedefstruct{charisbn[20];//isbn号charname[80];//书名charauthor[80];//作者doublesales_price;//售价intsales_count;//销售册数}Book;voi......
  • Curl 命令参数解析
    Curl参数:详细解析与示例curl是一个功能强大的命令行工具,用于传输数据。它支持多种协议,如HTTP、HTTPS、FTP、SFTP等。curl提供了丰富的参数,以满足各种传输需求。本文将详细解析curl参数,并通过代码示例说明其用法。1.参数概述curl参数分为两大类:通用参数和协议相关......
  • 基于51单片机煤气天然气CO检测报警器排气风扇断气
    **单片机设计介绍,基于51单片机煤气天然气CO检测报警器排气风扇断气文章目录一概要二、功能设计设计思路三、软件设计原理图五、程序六、文章目录一概要  基于51单片机煤气天然气CO检测报警器排气风扇断气系统概要如下:一、系统概述本系统旨在利用51单片......
  • React Hooks路由传参
    场景:如何把想要的参数带到跳转过去的页面里呢?很简单上代码:在你需要跳转的页面上引入Link用来跳转使用Link跳转并携带参数然后需要什么参数就带什么过去喽 这里record里面存的就是我的数据我只需要id和state然后到你跳转过去的页面(也就是需要使用这个页面的这俩参数的页......
  • Python爬虫-字体加密 ddddocr FontCreator
    目录应用场景解决思路1.下载字体文件2.分析3.代码实现版本1版本2应用场景在爬取网页数据时我们有时可能会遇到如下面的情况,价格数字在网页上能正常显示,但在控制面板查看时却是显示空白,我们通过requests等库爬取该页面后得到的数据也是无法显示出来。解决思路1.......
  • 开源模型应用落地-语音转文本-whisper模型-AIGC应用探索(三)
    一、前言   语音转文本技术具有重要价值。它能提高信息记录和处理的效率,使人们可以快速将语音内容转换为可编辑、可存储的文本形式,方便后续查阅和分析。在教育领域,可帮助学生更好地记录课堂重点;在办公场景中,能简化会议记录工作。同时,该技术也为残障人士提供了便利,让他们能......
  • PHP Standards Recommendations(PSR)
    以下是PHPStandardsRecommendations(PSR)的全部内容:PSR-1:基础编码标准:规定了PHP代码的基本格式和要求,包括文件的编码、标签的使用、代码的组织等。PSR-2:编码风格指南:是对PSR-1的扩展,详细规定了PHP代码的排版、缩进、命名规范等,以提高代码的可读性。PSR-3:日志接口:定义......