首页 > 系统相关 >高性能版本的零内存分配LikeString函数(ZeroMemAllocLikeOperator)

高性能版本的零内存分配LikeString函数(ZeroMemAllocLikeOperator)

时间:2024-06-13 10:56:09浏览次数:31  
标签:patternItem return LikeString 内存 char patternSpan patternIndex true ZeroMemAlloc

继上一篇文章在.NET Core,除了VB的LikeString,还有其它方法吗?(四种LikeString实现分享)分享了四种实现方式,笔者对这四种实现方式,不管是执行性能还是内存分配性能上,都不太满意。

那么是否有好的实现方法呢?答案是有的。

今天我们就搬出ReadOnlySpan<T>这个非常好用的结构类型,它是在 .NET Core 2.1 中新引入的类型,与它一同被引入的类型还有:

  • System.Span: 这以类型安全和内存安全的方式表示任意内存的连续部分;
  • System.ReadOnlySpan: 这表示任意连续内存区域的类型安全和内存安全只读表示形式;
  • System.Memory: 这表示一个连续的内存区域;
  • System.ReadOnlyMemory: 类似ReadOnlySpan, 此类型表示内存的连续部分ReadOnlySpan, 它不是 ByRef 类型;

    注:ByRef 类型指的是 ref readonly struct

下面,我们就来看看如何实现高性能和零内存分配的 LikeString 函数吧!

#nullable enable

using System;

namespace AllenCai
{
    /// <summary>
    /// 这是一个模仿Microsoft.VisualBasic.CompilerServices.LikeOperator.LikeString方法,<br />
    /// 实现支持*和?通配符和支持忽略大小写规则以及区域无关性的匹配。<br />
    /// 该实现的目的是为了减少内存分配,提高性能。
    /// </summary>
    public class ZeroMemAllocLikeOperator
    {
        /// <summary>
        /// 对给定的两个字符串执行比较,支持使用*和?通配符。
        /// </summary>
        public static bool LikeString(string? content, string? pattern, bool ignoreCase = true, bool useInvariantCulture = true)
        {
            if (content == null && pattern == null)
                return true;
            if (content == null || pattern == null)
                return false;

            ReadOnlySpan<char> patternSpan = pattern.AsSpan();
            ReadOnlySpan<char> contentSpan = content.AsSpan();

            return LikeString(contentSpan, patternSpan, ignoreCase, useInvariantCulture);
        }

        /// <summary>
        /// 对给定的两个字符Span执行比较,支持使用*和?通配符。
        /// </summary>
        public static bool LikeString(ReadOnlySpan<char> contentSpan, ReadOnlySpan<char> patternSpan, bool ignoreCase = true, bool useInvariantCulture = true)
        {
            char zeroOrMoreChars = '*';
            char oneChar = '?';

            // 如果pattern是由1个星号*组成,那么没必要匹配,直接返回true。
            if (patternSpan.Length == 1)
            {
                ref readonly char patternItem = ref patternSpan[0];
                if (patternItem == zeroOrMoreChars)
                {
                    return true;
                }
            }

            // 如果被匹配内容的长度只有1位,而pattern刚好也是一个问号?,那么没必要匹配,直接返回true。
            if (contentSpan.Length == 1)
            {
                ref readonly char patternItem = ref patternSpan[0];
                if (patternItem == oneChar)
                {
                    return true;
                }
            }

            // 如果pattern是由多个星号*和问号?组成,那么没必要匹配,直接返回true。
            int zeroOrMorePatternCount = 0;
            int onePatternCount = 0;
            for (int i = 0; i < patternSpan.Length; i++)
            {
                ref readonly char patternItem = ref patternSpan[i];
                if (patternItem == zeroOrMoreChars)
                {
                    zeroOrMorePatternCount++;
                }
                else if (patternItem == oneChar)
                {
                    onePatternCount++;
                }
            }
            if (zeroOrMorePatternCount + onePatternCount == patternSpan.Length)
            {
                //只要出现1个或多个星号*,那么就没必要在乎被匹配内容的长度了。
                if (zeroOrMorePatternCount > 0)
                {
                    return true;
                }

                //如果没有星号*,全是问号?,那么就检查是否由问号?组成的pattern长度是否和被匹配内容的长度一致。如果一致,没必要匹配,直接返回true。
                if (patternSpan.Length == contentSpan.Length)
                {
                    return true;
                }
            }

            // 选择合适的EqualsChar方法。
            EqualsCharDelegate equalsChar;
            if (ignoreCase)
            {
                if (useInvariantCulture)
                {
                    equalsChar = EqualsCharInvariantCultureIgnoreCase;
                }
                else
                {
                    equalsChar = EqualsCharCurrentCultureIgnoreCase;
                }
            }
            else
            {
                equalsChar = EqualsChar;
            }

            return LikeStringCore(contentSpan, patternSpan, in zeroOrMoreChars, in oneChar, equalsChar);
        }

        private static bool LikeStringCore(ReadOnlySpan<char> contentSpan, ReadOnlySpan<char> patternSpan, in char zeroOrMoreChars, in char oneChar, EqualsCharDelegate equalsChar)
        {
            // 遍历pattern,逐个字符匹配。
            int contentIndex = 0;
            int patternIndex = 0;
            while (contentIndex < contentSpan.Length && patternIndex < patternSpan.Length)
            {
                ref readonly char patternItem = ref patternSpan[patternIndex];
                if (patternItem == zeroOrMoreChars)
                {
                    // 如果pattern中的下一个字符是星号*,那么就一直往后移动patternIndex,直到找到不是星号*的字符。
                    while (true)
                    {
                        if (patternIndex < patternSpan.Length)
                        {
                            ref readonly char nextPatternItem = ref patternSpan[patternIndex];
                            if (nextPatternItem == zeroOrMoreChars)
                            {
                                patternIndex++;
                                continue;
                            }
                        }
                        break;
                    }

                    // 如果patternIndex已经到了pattern的末尾,那么就没必要再匹配了,直接返回true。
                    if (patternIndex == patternSpan.Length)
                    {
                        return true;
                    }

                    // 如果patternIndex还没到pattern的末尾,那么就从contentIndex开始匹配。
                    while (contentIndex < contentSpan.Length)
                    {
                        if (LikeStringCore(contentSpan.Slice(contentIndex), patternSpan.Slice(patternIndex), in zeroOrMoreChars, in oneChar, equalsChar))
                        {
                            return true;
                        }
                        contentIndex++;
                    }

                    return false;
                }

                if (patternItem == oneChar)
                {
                    // 如果pattern中的下一个字符是问号?,那么就匹配一个字符。
                    contentIndex++;
                    patternIndex++;
                }
                else
                {
                    // 如果pattern中的下一个字符不是星号*,也不是问号?,那么就匹配一个字符。
                    if (contentIndex >= contentSpan.Length)
                    {
                        return false;
                    }

                    ref readonly char contentItem = ref contentSpan[contentIndex];
                    if (!equalsChar(in contentItem, in patternItem))
                    {
                        return false;
                    }

                    //if (ignoreCase)
                    //{
                    //    if (char.ToUpperInvariant(contentItem) != char.ToUpperInvariant(patternItem))
                    //    {
                    //        return false;
                    //    }
                    //}
                    //else
                    //{
                    //    if (contentItem != patternItem)
                    //    {
                    //        return false;
                    //    }
                    //}

                    contentIndex++;
                    patternIndex++;
                }
            }

            // 如果content都匹配完了,而pattern还没遍历完,则检查剩余的patternItem是否都是星号*,如果是就返回true,否则返回false。
            if (contentIndex == contentSpan.Length)
            {
                // 如果pattern中的下一个字符是星号*,那么就一直往后移动patternIndex,直到找到不是星号*的字符。
                while (true)
                {
                    if (patternIndex < patternSpan.Length)
                    {
                        ref readonly char nextPatternItem = ref patternSpan[patternIndex];
                        if (nextPatternItem == zeroOrMoreChars)
                        {
                            patternIndex++;
                            continue;
                        }
                    }
                    break;
                }

                return patternIndex == patternSpan.Length;
            }

            return false;
        }

        private static bool EqualsChar(in char contentItem, in char patternItem)
        {
            return contentItem == patternItem;
        }

        private static bool EqualsCharCurrentCultureIgnoreCase(in char contentItem, in char patternItem)
        {
            return char.ToUpper(contentItem) == char.ToUpper(patternItem);
        }

        private static bool EqualsCharInvariantCultureIgnoreCase(in char contentItem, in char patternItem)
        {
            return char.ToUpperInvariant(contentItem) == char.ToUpperInvariant(patternItem);
        }

        private delegate bool EqualsCharDelegate(in char contentItem, in char patternItem);
    }
}

PS: 以上代码在 .NET Standard 2.1 项目使用,可直接编译通过。

在 .NET Standard 2.0 项目中,需要额外引入 System.Memory 这个 NuGet 包,且需要将 LangVersion(C#语言版本)更改为 8.0 或更高(通常使用defaultlatest也可以)。

标签:patternItem,return,LikeString,内存,char,patternSpan,patternIndex,true,ZeroMemAlloc
From: https://www.cnblogs.com/VAllen/p/18245425/High-performance-and-zero-memory-allocation-Like

相关文章

  • 动态内存管理<C语言>
    导言       在C语言学习阶段,指针、结构体和动态内存管理,是后期学习数据结构的最重要的三大知识模块,也是C语言比较难的知识模块,但是“天下无难事”,只要认真踏实的学习,也能解决,所以下文将介绍动态内存管理涉及到的一些函数以及概念。目录导言为什么存在动态内存管理......
  • java 堆外内存排查
    操作系统:centos7jdk版本8yum-yinstallgccgcc-c++wgethttp://download.savannah.gnu.org/releases/libunwind/libunwind-0.99.tar.gzwgethttps://github.com/gperftools/gperftools/releases/download/gperftools-2.6.1/gperftools-2.6.1.tar.gz./configure--prefix=/u......
  • Java面向对象的介绍 , 类和对象的概念 , 匿名对象的使用 ,对象的内存图 ,成员变量和局
    第一章.类和对象1.面向对象的介绍1.面向过程:自己的事情自己干,代表语言C语言洗衣服:每一步自己要亲力亲为->找个盆,放点水,找个搓衣板,搓搓搓2.面向对象:自己的事情别人帮忙去干,代表语言Java语言洗衣服:自己的事情别人干->全自动洗衣机......
  • linux内存管理(九)- 页面回收
    参考《深入理解linux内核架构》和这篇博客Linux中的内存回收[一]-知乎(zhihu.com)内核代码v6.8-rc2内存在计算机系统中经常是稀缺资源,当系统中内存不足甚至耗尽,为了让系统继续运行必须回收一部分内存。为了回收内存,我们必须首先知道系统中的内存都处于什么状态。内存中的页......
  • 系统内存占用下降 20%,卓创网络应用 OpenCloudOS 实践
    导语:卓创网络作为一家专注于招标采购领域的企业,主营产品「招采星」为超过4000家公司提供电子采购系统及相关配套服务,在使用OpenCloudOS后,系统内存占用由原来的33%降低到11.7%,下降20%+。本文将深入探讨卓创网络从传统架构到OpenCloudOS的转变,分析这一转变带来的技术优势......
  • 你对内存模型(JMM)理解多少?
    Java内存模型(JMM)是一个抽象概念,它规定了在Java并发编程中如何处理多线程之间的内存交互。JMM解决并发程序中最关键的两个问题:线程间的可见性和指令重排序。线程间的可见性:确保当一个线程修改了共享变量的值时,其他线程可以立即看到这一改变。没有良好的可见性保证,一个线程对......
  • yolov5内存分布分析 转载
    yolov5内存分布分析Transpose输出分析假设batch_size为1,yolov5有三个输出,shape分别是:(1,3,80,80,85)(1,3,40,40,85)(1,3,20,20,85)其中3代表anchor数量,20*20代表feature_map大小,85代表boundbox的(x,y,w,h,c+80个类别的概率)其中(x,y,w,h,c+80个类别的概率)在内存中是连续分......
  • FreeRTOS学习笔记-基于stm32(14)内存管理
    一、FreeRTOS内存管理简介        FreeRTOS有两种方法来创建任务,队列,信号量等,一种动态一种静态。静态方法需要手动定义任务堆栈。使用动态内存管理的时候FreeRTOS内核在创建任务、队列、信号量的时候会动态的申请RAM。    我们在移植FreeRTOS时可以看到......
  • 记一次 .NET某游戏币自助机后端 内存暴涨分析
    一:背景1.讲故事前些天有位朋友找到我,说他们的程序内存会偶发性暴涨,自己分析了下是非托管内存问题,让我帮忙看下怎么回事?哈哈,看到这个dump我还是非常有兴趣的,居然还有这种游戏币自助机类型的程序,下次去大玩家看看他们出币的机器后端是不是C#写的?由于dump是linux上的程序,刚好windb......
  • 在.NET Core,除了VB的LikeString,还有其它方法吗?(四种LikeString实现分享)
    Like运算符很好用,特别是它所提供的其中*、?这两种通配符,在Windows文件系统和各类项目中运用非常广泛。但Like运算符仅在VB中支持,在C#中,如何实现呢?以下是关于LikeString的四种实现方式,其中第四种为Regex正则表达式实现,且在.NETStandard2.0及以上平台支持。Operators.LikeStr......