dotnet 提升 ToUpper 性能
在应用软件启动过程中,客户端应用软件是对性能敏感的。比如在解析命令行参数的时候,有时候需要进行字符串处理逻辑。一般来说命令行参数都是语言文化无关的,在需要进行全大写或全小写转换过程中,采用 ToUpperInvariant 替换 ToUpper 方法可以避免初始化 icu 模块,减少 icu 模块初始化过慢影响启动性能
特别感谢 lsj 调查此问题和 walterlv 在 https://github.com/dotnet-campus/dotnetCampus.CommandLine/pull/37 上优化命令行解析库性能
在进行 dotnet 的客户端应用启动性能分析的时候,客户端应用从逻辑上需要等待命令行参数解析完成,从而决定后续启动过程。命令行解析的性能将会影响总启动时间。在进行调查命令行解析库的性能时,发现了在命令行解析里面的某个逻辑需要对字符串转换为全大写时调用的是 ToUpper 里面传入 CultureInfo.InvariantCulture 参数方法,用来进行语言文化无关的转换大写,如以下代码
chars[0] = char.ToUpper(chars[0], CultureInfo.InvariantCulture);
以上代码将会导致在启动过程中初始化 ICU 模块,而 ICU 模块的初始化是需要耗费资源的,以下是使用 dotTrace 测量的结果

尽管 dotTrace 测量出来的 12ms 的时间是属于基本可以忽略的耗时,但是在一个以 Tick 计时的命令行解析库里面进行耗时对比,可以看到基本命令行解析所有时间都用在了 ICU 初始化上,这是不合理的
优化的方法是换成 ToUpperInvariant 从而规避 ICU 的初始化,如以下代码
chars[0] = char.ToUpperInvariant(chars[0]);
为什么这两个方法的调用会有 ICU 上的差异?原因是 ToUpper 方法里面有一个初始化判断逻辑,如 dotTrace 测量的结果图,在 IsAsciiCasingSameAsInvariant 属性里面需要进入 PopulateIsAsciiCasingSameAsInvariant 方法用来判断是否在此语言文化之下,进行大小写转换和语言文化无关是相同的结果
以下是 dotnet 运行时里面对 Char 类型的 ToUpper 方法定义,可以看到实际调用的是 CultureInfo 的 TextInfo 属性提供的 ToUpper 方法
public readonly struct Char
{
public static char ToUpper(char c, CultureInfo culture)
{
if (culture == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.culture);
}
return culture.TextInfo.ToUpper(c);
}
}
以下是 TextInfo 的 ToUpper 核心实现逻辑,代码有部分删除。通过以下代码可以看到在 ToUpper 里面需要判断一次 IsAsciiCasingSameAsInvariant 属性。在 IsAsciiCasingSameAsInvariant 属性里面只有首次需要调用到 PopulateIsAsciiCasingSameAsInvariant 方法,此方法需要执行一次判断当前语言文化进行大小写转换时和语言文化无关情况下是相同的结果
public sealed partial class TextInfo
{
/// <summary>
/// Converts the character or string to upper case. Certain locales
/// have different casing semantics from the file systems in Win32.
/// </summary>
public char ToUpper(char c)
{
if (GlobalizationMode.Invariant)
{
return InvariantModeCasing.ToUpper(c);
}
if (UnicodeUtility.IsAsciiCodePoint(c) && IsAsciiCasingSameAsInvariant)
{
return ToUpperAsciiInvariant(c);
}
return ChangeCase(c, toUpper: true);
}
private bool IsAsciiCasingSameAsInvariant
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (_isAsciiCasingSameAsInvariant == Tristate.NotInitialized)
{
PopulateIsAsciiCasingSameAsInvariant();
}
Debug.Assert(_isAsciiCasingSameAsInvariant == Tristate.True || _isAsciiCasingSameAsInvariant == Tristate.False);
return _isAsciiCasingSameAsInvariant == Tristate.True;
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void PopulateIsAsciiCasingSameAsInvariant()
{
bool compareResult = CultureInfo.GetCultureInfo(_textInfoName).CompareInfo.Compare("abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", CompareOptions.IgnoreCase) == 0;
_isAsciiCasingSameAsInvariant = (compareResult) ? Tristate.True : Tristate.False;
}
private Tristate _isAsciiCasingSameAsInvariant = Tristate.NotInitialized;
}
虽然这里传入的是 CultureInfo.InvariantCulture 语言文化无关,但是 TextInfo 层不知道,还是需要跑一次 PopulateIsAsciiCasingSameAsInvariant 判断逻辑。这个判断逻辑里面需要初始化 ICU 模块
而调用 Char 的 ToUpperInvariant 则是走完全的静态的 TextInfo 的 ToUpperInvariant 方法,如以下代码
public readonly struct Char
{
public static char ToUpperInvariant(char c) => TextInfo.ToUpperInvariant(c);
}
在静态的 TextInfo 的 ToUpperInvariant 方法是明确知道语言文化无关的,不需要进行任何判断逻辑,如此即可减少 ICU 模块初始化,在启动时调用的速度将会更快
public sealed partial class TextInfo
{
internal static char ToUpperInvariant(char c)
{
if (GlobalizationMode.Invariant)
{
return InvariantModeCasing.ToUpper(c);
}
if (UnicodeUtility.IsAsciiCodePoint(c))
{
return ToUpperAsciiInvariant(c);
}
return Invariant.ChangeCase(c, toUpper: true);
}
}
值得一提的是本文所讲的性能差异仅仅只是在应用启动过程中有效,如果不是应用启动过程,基本上 ICU 也初始化过了,不会存在耗时问题,而且非性能敏感的逻辑也不会有如此严格的耗时要求
相同的启动问题性能优化,也在 MAUI 仓库里面执行,在 MAUI 里面引入了 CA1311: Specify a culture or use an invariant version 警告提示,意思就是如果发现代码里面写了不带语言文化的 String.ToUpper() 或 String.ToLower() 方法,将会提示换成 ToUpper(CultureInfo) 或 ToUpperInvariant() 或 ToLower(CultureInfo) 或 ToLowerInvariant() 方式减少语言文化加载性能

如在 .NET 8 Performance Improvements in .NET MAUI - .NET Blog 博客里面提到的
Address CA1307 and CA1309 for performance
Profiling a .NET MAUI sample application from a customer, we noticed time spent during “culture-aware” string operations:
77.22ms microsoft.maui!Microsoft.Maui.Graphics.MauiDrawable.SetDefaultBackgroundColor()
42.55ms System.Private.CoreLib!System.String.ToLower()
This case, we can improve by simply calling ToLowerInvariant() instead. In some cases you might even consider using string.Equals() with StringComparer.Ordinal.
更改的代码如下
https://github.com/dotnet/maui/pull/14627
通过 ToLowerInvariant 和 ToUpperInvariant 转换大小写等方法代替引入语言文化相关的判断,在逻辑等价变更的情况下,可以减少启动耗时
dotnet 提升 ToUpper 性能的更多相关文章
- dotnet 启动 JIT 多核心编译提升启动性能
用2分钟提升十分之一的启动性能,通过在桌面程序启动 JIT 多核心编译提升启动性能 在 dotnet 可以通过让 JIT 进行多核心编译提升软件的启动性能,在默认托管的 ASP.NET 程序是开启的, ...
- dotnet 使用 Crossgen2 对 DLL 进行 ReadyToRun 提升启动性能
我对几个应用进行严格的启动性能评估,对比了在 .NET Framework 和 dotnet 6 下的应用启动性能,非常符合预期的可以看到,在用户的设备上,经过了 NGen 之后的 .NET Fram ...
- 2019-8-31-dotnet-启动-JIT-多核心编译提升启动性能
title author date CreateTime categories dotnet 启动 JIT 多核心编译提升启动性能 lindexi 2019-08-31 16:55:58 +0800 ...
- [.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能
[.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能 本节导读: 上节说了缓存是以空间来换取时间的技术,介绍了客户端缓存和两种常用服务器缓布,本节主要介绍一种. ...
- 提升PHP性能的21种方法
提升PHP性能的21种方法. 1.用单引号来包含字符串要比双引号来包含字符串更快一些.因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会.2.如果能将类的方法定义成static,就尽量定义成st ...
- HHVM 是如何提升 PHP 性能的?
背景 HHVM 是 Facebook 开发的高性能 PHP 虚拟机,宣称比官方的快9倍,我很好奇,于是抽空简单了解了一下,并整理出这篇文章,希望能回答清楚两方面的问题: HHVM 到底靠谱么?是否可以 ...
- 使用异步HTTP提升客户端性能(HttpAsyncClient)
使用异步HTTP提升客户端性能(HttpAsyncClient) 大家都知道,应用层的网络模型有同步.异步之分. 同步,意为着线程阻塞,只有等本次请求全部都完成了,才能进行下一次请求. 异步,好处是不 ...
- 极光开发者沙龙 之 移动应用性能优化实践 【一】旧酒新瓶——换个角度提升 App 性能与质量
旧酒新瓶--换个角度提升 App 性能与质量 主讲人:高亮亮 --- 饿了么移动技术部高级iOS工程师,负责饿了么商家版iOS APP开发,对架构和系统底层有深入研究,擅长移动性能分析,troub ...
- React爬坑秘籍(一)——提升渲染性能
React爬坑秘籍(一)--提升渲染性能 ##前言 来到腾讯实习后,有幸八月份开始了腾讯办公助手PC端的开发.因为办公助手主推的是移动端,所以导师也是大胆的让我们实习生来技术选型并开发,他来做code ...
- 十个技巧迅速提升JQuery性能
本文提供即刻提升你的脚本性能的十个步骤.不用担心,这并不是什么高深的技巧.人人皆可运用!这些技巧包括: 使用最新版本 合并.最小化脚本 用for替代each 用ID替代class选择器 给选择器指定前 ...
随机推荐
- python面向对象(选课系统)
一.需求分析(课程与班级合为一体) -管理员视图 -1.注册 -2.登录 -3.创建学校 -4.创建课程(先选择学校) -5.创建讲师 -学员视图 -1.注册 -2.登录功能 -3.选择校区 -4.选 ...
- L7结合Turf.js实现空间分析与数据可视化
1. 概述 AntV L7 是蚂蚁集团 AntV 数据可视化团队推出的基于 WebGL 的开源大规模地理空间数据可视分析引擎,其特点是通过简单的代码进行配置,即可在前端网页中绘制精美的地图以及相关的图 ...
- linux系统centos7.9如何安装nginx
1.官网下载nginx nginx官网:https://nginx.org/ 选择稳定版进行下载,也可以下载老版本,下载成功后上传到服务器. 2.使用wget下载 访问nginx官网,在下载页面鼠标右 ...
- KingbaseES 控制文件损坏的恢复
sys_ control文件损坏: 需要手工指定一些参数完成sys_resetwal相关操作 当前数据库信息 test=# \d 关联列表 架构模式 | 名称 | 类型 | 拥有者 --------- ...
- .net core ECDsa
ECDsa(Elliptic Curve Digital Signature Algorithm)是一种基于椭圆曲线密码学的数字签名算法.在.NET Core中,System.Security.Cry ...
- #区间dp#CF1114D Flood Fill
题目 有一个长度为\(n\)的颜色序列,在游戏前选择一个固定的位置, 若当前轮该位置的颜色为\(x\),那么可以将所有颜色为\(x\)的连通块改为任意颜色, 问最少进行多少轮使得区间\([1,n]\) ...
- Python 集合(Sets)1
集合 集合用于在单个变量中存储多个项.集合是 Python 中的 4 种内置数据类型之一,用于存储数据集合,其他 3 种是列表(List).元组(Tuple)和字典(Dictionary),它们都具有 ...
- C#实现文件加密、解密及文件拖拽至程序图标直接打开
引用:https://www.cnblogs.com/longqi293/archive/2010/07/23/1783672.html 下载源码:http://files.cnblogs.com/l ...
- 如何翻译 Markdown 文件?-2-几种商业及开源解决方案介绍
背景 近期在搭建英文博客-<e-whisper.com>, 需要对现有的所有中文 Markdown 翻译为英文. 需求如下: 将 Markdown 文件从中文 (zh-CN) 翻译为英文 ...
- opencv读取中文路径图片
点击查看代码 img = cv2.imdecode(np.fromfile(filename, dtype=np.uint8), cv2.IMREAD_GRAYSCALE)