【微软技术栈】使用新的C#功能减少内存分配

本文内容

  1. 通过引用传递和返回
  2. 引用安全上下文
  3. 安全的上下文和 ref 结构
  4. 统一内存类型
  5. 通过参考安全提高性能

本节中介绍的技术可提高应用于代码中的热路径时的性能。热路径是代码库中在正常操作中经常重复执行的部分。将这些技术应用于不经常执行的代码将产生最小的影响。在进行任何更改以提高性能之前,测量基线至关重要。然后,分析该基线以确定内存瓶颈发生的位置。

测量内存使用情况并确定可以减少分配后,请使用本节中的技术来减少分配。每次连续更改后,再次测量内存使用情况。确保每个更改都会对应用程序中的内存使用产生积极影响。

.NET 中的性能工作通常意味着从代码中删除分配。您分配的每个内存块最终都必须被释放。较少的分配可减少垃圾回收所花费的时间。它通过从特定代码路径中删除垃圾回收,允许更可预测的执行时间。

减少分配的常用策略是将关键数据结构从类型更改为类型。此更改会影响使用这些类型的语义。参数和返回现在按值传递,而不是按引用传递。如果类型很小,只有三个字或更少(考虑到一个字的自然大小为一个整数),则复制值的成本可以忽略不计。它是可衡量的,可以对较大的类型产生真正的性能影响。为了消除复制的影响,开发人员可以传递这些类型来获取预期的语义。

使用 C# 功能,您可以表达所需的类型语义,而不会对其整体可用性产生负面影响。在实现这些增强功能之前,开发人员需要求助于具有指针和原始内存的构造,以实现相同的性能影响。编译器为新的相关功能生成可验证的安全代码可验证的安全代码意味着编译器检测到可能的缓冲区溢出或访问未分配或释放的内存。编译器会检测并防止某些错误。

1、通过引用传递和返回

C# 中的变量存储。在类型中,该值是该类型的实例的内容。在类型中,该值是对存储该类型实例的内存块的引用。添加修饰符意味着变量存储对值的引用。在类型中,引用指向包含该值的存储。在类型中,引用指向包含对内存块的引用的存储。

在 C# 中,方法的参数是按值传递的,返回值是按值返回的。参数的值将传递给该方法。返回参数的值是返回值。

或 修饰符指示参数是通过引用传递的。对存储位置的引用将传递给该方法。添加到方法签名意味着通过引用返回返回值。对存储位置的引用是返回值。

您还可以使用 ref 赋值让一个变量引用另一个变量。典型的赋值将右侧的值复制到赋值左侧的变量。ref 赋值将右侧变量的内存位置复制到左侧的变量。现在指的是原始变量:

int anInteger = 42; // assignment.
ref int location = ref anInteger; // ref assignment.
ref int sameLocation = ref location; // ref assignmentConsole.WriteLine(location); // output: 42sameLocation = 19; // assignmentConsole.WriteLine(anInteger); // output: 19

分配变量时,会更改其。当您 ref 赋值一个变量时,您可以更改它所引用的内容。

您可以使用变量、通过引用传递和引用赋值直接处理值的存储。编译器强制执行的范围规则可确保直接使用存储时的安全性。

和修饰符都指示参数应通过引用传递,并且不能在方法中重新赋值。区别在于,该方法将参数用作变量。该方法可能会捕获参数,也可能通过只读引用返回参数。在这些情况下,应使用修饰符。否则,修饰符将提供更大的灵活性。您无需将修饰符添加到参数的参数中,因此您可以使用修饰符安全地更新现有 API 签名。如果未将 or 修饰符添加到参数的参数中,编译器将发出警告。

2、引用安全上下文

C# 包含表达式规则,以确保在表达式引用的存储不再有效的情况下无法访问表达式。请看以下示例:

public ref int CantEscape()
{int index = 42;return ref index; // Error: index's ref safe context is the body of CantEscape
}

编译器报告错误,因为无法从方法返回对局部变量的引用。调用方无法访问所引用的存储。ref 安全上下文定义表达式可以安全访问或修改的范围。下表列出了变量类型的 ref 安全上下文。 字段不能在 a 或 non-ref 中声明,因此这些行不在表中:

声明ref 安全上下文
非 ref 本地声明 local 的块
non-ref 参数current 方法
ref参数ref readonlyin调用方法
out参数current 方法
class调用方法
non-ref 字段structcurrent 方法
ref领域ref struct调用方法

如果变量的 ref 安全上下文是调用方法,则可以返回该变量。如果其 ref 安全上下文是当前方法或块,则不允许返回。以下代码片段显示了两个示例。可以从调用方法的作用域访问成员字段,因此类或结构字段的 ref 安全上下文是调用方法。带有 或修饰符的参数的 ref 安全上下文是整个方法。两者都可以从成员方法返回:

private int anIndex;public ref int RetrieveIndexRef()
{return ref anIndex;
}public ref int RefMin(ref int left, ref int right)
{if (left < right)return ref left;elsereturn ref right;
}

编译器确保引用无法转义其引用安全上下文。您可以安全地使用参数、 和局部变量,因为编译器会检测您是否意外编写了代码,在表达式存储无效时可以访问表达式。

3、安全的上下文和 ref 结构

ref struct类型需要更多的规则来确保它们可以安全使用。类型可以包含字段。这需要引入一个安全的环境。对于大多数类型,安全上下文是调用方法。换言之,始终可以从方法返回不是 ref structrefref struct的值。

非正式地,a 的安全上下文是可以访问其所有字段的范围。换句话说,它是其所有字段的 ref 安全上下文的交集。以下方法返回 a to a member 字段,因此其安全上下文是该方法:ref structrefrefReadOnlySpan<char>

private string longMessage = "This is a long message";public ReadOnlySpan<char> Safe()
{var span = longMessage.AsSpan();return span;
}

相反,以下代码会发出错误,因为 的成员引用了堆栈分配的整数数组。它无法转义方法:ref fieldSpan<int>

public Span<int> M()
{int length = 3;Span<int> numbers = stackalloc int[length];for (var i = 0; i < length; i++){numbers[i] = i;}return numbers; // Error! numbers can't escape this method.
}

4、统一内存类型

System.Span<T> 和 System.Memory<T> 的引入为使用内存提供了统一的模型。System.ReadOnlySpan<T> 和 System.ReadOnlyMemory<T> 提供用于访问内存的只读版本。它们都提供了对存储类似元素数组的内存块的抽象。区别在于 和 是类型,而 和 是类型。跨度包含一个 .因此,跨度的实例不能离开其安全上下文。a 的安全上下文是其 的 ref 安全上下文。实施并删除此限制。您可以使用这些类型直接访问内存缓冲区。Span<T>ReadOnlySpan<T>ref structMemory<T>ReadOnlyMemory<T>structref fieldref structref fieldMemory<T>ReadOnlyMemory<T>

5、通过参考安全提高性能

使用这些功能提高性能涉及以下任务:

  • 避免分配:将类型从 a 更改为 时,会更改其存储方式。局部变量存储在堆栈上。分配容器对象时,成员以内联方式存储。此更改意味着分配更少,从而减少了垃圾回收器所做的工作。它还可能会降低内存压力,从而降低垃圾回收器的运行频率。
  • 保留引用语义:将类型从 a 更改为 a 会更改将变量传递给方法的语义。修改其参数状态的代码需要修改。现在参数是 ,该方法正在修改原始对象的副本。您可以通过将该参数作为参数传递来还原原始语义。更改后,该方法将再次修改原始内容。
  • 避免复制数据:复制较大的类型可能会影响某些代码路径的性能。还可以添加修饰符,以便通过引用而不是按值将较大的数据结构传递给方法。
  • 限制修改:当通过引用传递类型时,被调用的方法可以修改结构的状态。您可以将修饰符替换为 or 修饰符,以指示无法修改参数。当方法捕获参数或通过只读引用返回参数时,首选。还可以创建类型或具有成员的类型,以便更好地控制可以修改的成员。
  • 直接操作内存:当将数据结构视为包含一系列元素的内存块时,某些算法最有效。和类型提供对内存块的安全访问。

这些技术都不需要代码。如果使用得当,您可以从安全代码中获得性能特征,而以前只能使用不安全技术才能实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/161861.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[数据结构]-AVL树

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、AVL树基…

OpenGL 绘制线(Qt)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 这里同样对OpenGL中的绘制线操作进行封装,便于后续的操作,很多形状也都是基于线来生成的,如圆形等。 二、实现代码 LineDrawable.h #ifndef LINE_DRAWABLE_H #define LINE_DRAWABLE_H#include

IBM ELM—系统工程全生命周期管理平台

产品概述 Engineering Lifecycle Management是IBM提供的工程全生命周期管理组合工具&#xff0c;帮助企业降低开发成本&#xff0c;应对开发挑战并更快地发展其流程和实践。 随着产品变得更加复杂且数字化&#xff0c;传统的工程开发不再能及时且有效地满足系统工程的复杂度&a…

【Django-DRF】多年md笔记第5篇:Django-DRF的Request、Response和视图详解

本文从分析现在流行的前后端分离Web应用模式说起&#xff0c;然后介绍如何设计REST API&#xff0c;通过使用Django来实现一个REST API为例&#xff0c;明确后端开发REST API要做的最核心工作&#xff0c;然后介绍Django REST framework能帮助我们简化开发REST API的工作。 Dj…

[点云分割] 基于最小切割的分割

效果&#xff1a; 代码&#xff1a; #include <iostream> #include <vector>#include <pcl/point_types.h> #include <pcl/io/pcd_io.h> #include <pcl/visualization/cloud_viewer.h> #include <pcl/filters/filter_indices.h> #include…

Can‘t open the append-only file: Permission denied

redis rdb aof-CSDN博客 Cant open the append-only file: Permission denied E:\Document_Redis_Windows\redis-2.4.5-win32-win64\64bit E:\Document_Redis_Windows\redis-2.4.5-win32-win64\64bit\redis.conf 还是不行&#xff0c;就要修改权限了&#xff0c;windows【完全控…

matlab 最小二乘拟合平面并与XOY平面对齐

目录 一、算法原理二、代码实现1、绕原点对齐2、绕质心对齐三、结果展示1、绕原点对齐2、绕质心对齐四、测试数据本文由CSDN点云侠原创,原文链接。爬虫网站自重。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、算法原理 首先,使用最小二乘拟合平面…

priority_queue简单实现(优先级队列)(c++)

priority_queue priority_queue介绍逻辑实现框架调整算法adjust_up()adjust_down() 仿函数/比较函数仿函数特性 构造函数迭代器区间构造 完整优先级队列代码 priority_queue介绍 pri_que是一个容器适配器&#xff0c;它的底层是其他容器&#xff0c;并由这些容器再封装而来。类…

C语言指针相关练习题

​ C语言指针相关练习题 文章目录 C语言指针相关练习题题目一题目二题目三题目四题目五题目六题目七 题目一 #include <stdio.h> int main() {int a[5] { 1, 2, 3, 4, 5 };int *ptr (int *)(&a 1);printf( "%d,%d", *(a 1), *(ptr - 1));return 0; }…

[Unity+OpenAI TTS] 集成openAI官方提供的语音合成服务,构建海王暖男数字人

1.简述 最近openAI官方发布了很多新功能&#xff0c;其中就包括了最新发布的TTS语音合成服务的api接口。说到这个语音合成接口&#xff0c;大家可能会比较陌生&#xff0c;但是说到chatgpt官方应用上的聊天机器人&#xff0c;那个台湾腔的海王暖男的声音&#xff0c;可能就有印…

深度合成算法的基础与原理

深度合成算法是人工智能领域中备受瞩目的研究方向之一。它的应用范围涵盖了图像合成、文本生成、音频合成等多个领域&#xff0c;为人们提供了令人惊叹的创新和娱乐体验。本文将深入探讨深度合成算法的基础原理&#xff0c;了解它们是如何工作的以及它们在不同领域的应用。算法…

轻量封装WebGPU渲染系统示例<38>- 动态构建WGSL材质Shader(源码)

实现原理: 基于宏定义和WGSL功能文件实现 当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/DynamicShaderBuilding.ts 当前示例运行效果: 此示例基于此渲染系统实现&#xff0c;当前示例TypeScript源码如下&#x…

编写bat程序 快速开启 redis 服务

一键开启redis服务 编写txt文件&#xff0c;代码如下&#xff1a;cd /d E:\Redis\Redis-x64-5.0.14.1 redis-server.exe redis.windows.conf这里的redis的安装目录记得改成自己的 将文件后缀的.txt改成.bat&#xff0c;然后双击运行就可以啦

前缀和及差分数组

前缀和 原数组x0x1x2x3x4x5前缀和数组x0x0x1x0x1x2x0x1x2x3x0x1x2x3x4x0x1x2x3x4x5前缀和数组代数形式x0’x1’x2’x3’x4’x5’ 计算原数组某区间的和 sum[x1,x2,x3] 利用前缀和计算 x3-x0 x0x1x2x3-x0 x1x2x3 差分数组 x0x1x2x3x4x5原数组x0x1x2x3x4x5差分数组x0x1-x0x…

模拟电路定理

模拟电路是指由电子元件、电路拓扑和信号处理单元等构成的电路&#xff0c;用于模拟现实世界中的信号和系统。在模拟电路中&#xff0c;有许多重要的定理和规律&#xff0c;下面列举了一些常见的定理。 1. 基尔霍夫电流定律&#xff08;Kirchhoffs Current Law&#xff09; 基…

HTTP四大参数类型及请求参数的方式和如何接收

HTTP 请求中4大参数类型和接收方法。 1、请求头参数head 请求头参数顾名思义&#xff0c;是存放在请求头中发送给服务器的参数&#xff0c;服务器通过解析请求头获取参数内容。通常会存放本次请求的基本设置&#xff0c;以帮助服务器理解并解析本次请求的body体。 参数形式如…

C++学习 --string

目录 1&#xff0c; 什么是string 2&#xff0c; 创建string 3&#xff0c; 操作string 3-1&#xff0c; 赋值 3-1-1&#xff0c; 赋值() 3-1-1&#xff0c; 赋值(assign) 3-2&#xff0c; 修改 3-2-1&#xff0c; 拼接 3-2-1-1&#xff0c; 拼接() 3-2-1-2&#xff…

srs的webrtc信令分析

关于webrtc的流信令只有四个 /rtc/v1/publish/&#xff0c;这是推流接口&#xff0c;是推流客户端跟SRS交换SDP的接口 /rtc/v1/play/&#xff0c;这是拉流接口&#xff0c;是拉流客户端跟SRS交换SDP的接口 /rtc/v1/whip/&#xff0c;这也是推流接口&#xff0c;作用是也是交换…

C#开发的OpenRA游戏之属性RenderSprites(8)

C#开发的OpenRA游戏之属性RenderSprites(8) 本文开始学习RenderSprites属性,这个属性是跟渲染有关的,因此它就摄及颜色相关的内容,所以我们先来学习一下调色板,这是旧游戏的图片文件保存的格式,如果放在现代来看,不会再采用这种方法,毕竟现在存储空间变大,便宜了,并…

JDBC 操作 SQL Server 时如何传入列表参数

本文是作为将要对 PostgreSQL 的 in, any() 操作的一个铺垫&#xff0c;也是对先前用 JDBC 操作 SQL Server 的温习。以此记录一下用 JDBC 查询 SQL Server 时如何传递一个列表参数。比如想像一下查询语句 select * from users where id in (?) 我们是否能给这里的问题参数传递…