U3D客户端框架之 拓展StringBuilder实现InsertNoGC、IndexOf、LastIndexOf、ReplaceNoGC、AppendNoGC API 减少GC

简介: String 是引用类型,在堆上分配内存,运算时会产生一个新的实例。 的缺点是每次字符串变量的内容发生了改变时,都必须重新分配内存。试想如创建一个迭代100000次的循环,每次迭代都将一个字符连接到字符串,这样内存中就会有100000个字符串,每个字符串仅仅与前一个字符只是有一个字符不同,性能影响是很大的。

StringBuilderExtenion模块设计


为什么要使用StringBuilder类?


String 是引用类型,在堆上分配内存,运算时会产生一个新的实例。 的缺点是每次字符串变量的内容发生了改变时,都必须重新分配内存。试想如创建一个迭代100000次的循环,每次迭代都将一个字符连接到字符串,这样内存中就会有100000个字符串,每个字符串仅仅与前一个字符只是有一个字符不同,性能影响是很大的。


StringBuilder通过分配一个缓存,就是一个工作区来解决这些问题,在工作区中用字符串StringBuilder类的相关方法。包括添加,删除,移除,插入和替换字符等等。执行完之后,将调用ToString方法把工作区中的内容转换为一个字符串,这样StringBuilderGC次数上会有一些减少。


StringBuilderEx类 实现了 正向反向模式串匹配函数,其他的基本上都是在原API的基础上做了一些减少GC的优化。


1 InsertNoGC函数


仅允许在主线程中使用,要把已有的和新增的先拷贝到bufferCharArray里面,然后在拷贝回sb


    public static void InsertNoGC(this StringBuilder sb, string from, int startIndex, int count = -1)
    {
        if (count < 0)
        {
            count = from.Length;
        }
        if (null == from || from.Length < count || null == sb)
            return;
        //插入的位置,比stringBuilder的总长度还要长,位置无效
        if (startIndex > sb.Length)
            return;
        int originLen = sb.Length;
        if (sb.Capacity < sb.Length + count)
        {
        }
        //sb的长度-索引+本次的数量
        //512-0+512=1024
        //要把已有的和新增的先拷贝到bufferCharArray里面,然后在拷贝回sb
        //数组的动态扩容
        if (originLen - startIndex + count > bufferCharArray.Length)
        {
            /*
             * 如果没超过 MaxBufferCharLen,每次扩容就2倍扩容,
             * 如果超过了 MaxBufferCharLen,new 一个实际需要的大小
             */
            bufferCharArray = new char[Math.Max(bufferCharArray.Length * 2 > MaxBufferCharLen ?
                                                MaxBufferCharLen : bufferCharArray.Length * 2,
                                                originLen - startIndex + count)];
        }
        //把from放到最前面,容器里原本的数据往后移,有点像push_front的感觉
        from.CopyTo(0, bufferCharArray, 0, count);
        //从插入之后的位置开始拷贝数据,拷贝到bufferCharArray后面(插入startIndex之前的数据保留)
        sb.CopyTo(startIndex, bufferCharArray, count, originLen - startIndex);
        //把startIndex插入位置往后的数据全部删除,前面就是保留了原始的数据,中间是from的数据,后面又是原始的数据,这样一操作,就完成了
        //从startIndex之前插入from字符串的目的。
        sb.Remove(startIndex, originLen - startIndex);
        //把字符串buffer写入到stringBuilder里去
        sb.Append(bufferCharArray, 0, originLen - startIndex + count);
    }


2 IndexOf函数


//正向匹配,返回匹配到的第一个的开始索引


    public static int IndexOf(this StringBuilder sb, string value, int startIndex = 0, bool ignoreCase = false)
    {
        int index;
        int lenth = value.Length;
        int maxSearchLenth = (sb.Length - lenth) + 1;
        if (ignoreCase)
        {
            for (int i = startIndex; i < maxSearchLenth; ++i)
            {
                if (Char.ToLower(sb[i]) == Char.ToLower(value[0]))
                {
                    index = 1;
                    while ((index < lenth) && (Char.ToLower(sb[i + index]) == Char.ToLower(value[index])))
                        ++index;
                    if (index == lenth)
                        return i;
                }
            }
            return -1;
        }
        for (int i = startIndex; i < maxSearchLenth; ++i)
        {
            if (sb[i] == value[0])
            {
                index = 1;
                while ((index < lenth) && (sb[i + index] == value[index]))
                    ++index;
                if (index == lenth)
                    return i;
            }
        }
        return -1;
    }


3 LastIndexOf函数


仅在主线程中使用


在主字符串中查找模式串的位置


从后向前查找


查找在主串中最后一次出现模式串的起始位置(例如主串abcabc,模式串abc,最后一次出现在模式串中的起始位置是3)


如果要查找的子字符串没有出现,则返回-1


public static int LastIndexOf(this StringBuilder sb, string value)
    {
        if (string.IsNullOrEmpty(value))
            return -1;
        int index;
        int lenth = value.Length;
        int sblen = sb.Length;
        int maxSearchLenth = (sb.Length - lenth) + 1;
        //abcddd abc = 6-3 = 3
        for (int i = 0; i < maxSearchLenth; ++i)
        {
            //反着匹配
            if (sb[sblen - 1 - i] == value[lenth - 1])
            {
                index = 1;
                while ((index < lenth) && (sb[sblen - 1 - i - index] == value[lenth - 1 - index]))
                    ++index;
                //循环结束后,判断是否匹配上,如果匹配上了,则返回最后一个匹配到的起始位置
                if (index == lenth)
                    return sblen - 1 - i - lenth + 1;
            }
        }
        return -1;
    }


4 ReplaceNoGC函数


 public static StringBuilder ReplaceNoGC(this StringBuilder sb, string oldValue, string newValue, int startIndex, int count)
    {
        if (sb.Length == 0 || string.IsNullOrEmpty(oldValue) || count == 0)
        {
            return sb;
        }
        int findIdx = sb.IndexOf(oldValue, startIndex);
        //找到的index+替换旧值得长度要<=开始索引+总长度。不能超长,否则会越界
        while (findIdx >= 0 && findIdx + oldValue.Length <= startIndex + count)
        {
            //移除从找到的位置开始,到这个字符串的结束的这段长度(说的再直白点就是删除这个字符串)
            sb.Remove(findIdx, oldValue.Length);
            //新的字符串插入到老字符串的位置
            sb.InsertNoGC(newValue, findIdx);
            //位置偏移+=newValue的长度
            findIdx += newValue.Length;
            //偏移一个newValue字符串的长度,往后继续找
            findIdx = sb.IndexOf(oldValue, findIdx);
        }
        return sb;
    }


5 AppendFormatNoGC函数


 public static StringBuilder AppendFormatNoGC(this StringBuilder sb, string format, params object[] args)
    {
        int argsLen = args.Length;
        int realLen = 0;
        sb.Clear();
        sb.Append(format);
        for (int i = 0; i < argsLen; ++i)
        {
            if (null != args[i])
            {
                Type t = args[i].GetType();
                if (t.IsValueType)
                {
                    if (t == typeof(double))
                    {
                        StringHelper.m_StringParamBuffer[i] = StringHelper.DoubleToStr((double)args[i]);
                    }
                    else if (t == typeof(int))
                    {
                        StringHelper.m_StringParamBuffer[i] = StringHelper.IntToStr((int)args[i]);
                    }
                    else if (t == typeof(ulong))
                    {
                        StringHelper.m_StringParamBuffer[i] = StringHelper.ULongToStr((ulong)args[i]);
                    }
                    else if (t == typeof(float))
                    {
                        StringHelper.m_StringParamBuffer[i] = StringHelper.FloatToStr((float)args[i]);
                    }
                    else
                    {
                        StringHelper.m_StringParamBuffer[i] = args[i].ToString();
                    }
                }
                else
                {
                    StringHelper.m_StringParamBuffer[i] = args[i].ToString();
                }
                ++realLen;
            }
        }
        for (int j = 0; j < realLen; ++j)
        {
            if (null != StringHelper.m_StringParamBuffer[j])
            {
                if (j < ParamNumArray.Length)
                {
                    sb.ReplaceNoGC(ParamNumArray[j], StringHelper.m_StringParamBuffer[j]);
                }
                else
                {
                    sb.ReplaceNoGC("{" + j.ToString() + "}", StringHelper.m_StringParamBuffer[j]);
                }
            }
        }
        for (int i = 0; i < argsLen; ++i)
        {
            StringHelper.m_StringParamBuffer[i] = null;
        }
        return sb;
    }


6 测试


功能开发完成之后就要测试以下功能是否正常,有无BUG,经过测试修改好功能完好


private void TestStringBuilderEx()
    {
        Debug.Log("开始测试StringBuilderEx类");
        /*
        //insertNoGC
        Debug.Log("测试1 InsertNoGC方法测试");
        StringBuilder sbIns = new StringBuilder();
        sbIns.Append("今天是个好日子today is good day");
        Debug.Log("1.1 Append原始字符串后:" + sbIns.ToString());
        sbIns.InsertNoGC("2022年11月6日",0);
        Debug.Log("1.2 InsertNoGC 往0插入string:"+sbIns.ToString());
        sbIns.InsertNoGC('h', 4);
        Debug.Log("1.3 InsertNoGC 往4插入h字符后"+sbIns.ToString());
        sbIns.InsertNoGC(new StringBuilder("我是StringBuilder,我插进来啦!"),0);
        Debug.Log("1.4 InsertNoGC 往0插入StringBuilder后:"+sbIns.ToString());
        Debug.Log("测试1 InsertNoGC方法测试 完毕\n\r");
        */
        /*
        //indexOf
        Debug.Log("测试2 IndexOf方法测试 ");
        StringBuilder sbIdxOf = new StringBuilder("大象abcd_abc_123!!()7878*(&*…abc");
        int idx;
        idx = sbIdxOf.IndexOf("大象");
        Debug.Log("2.1 大象 index:"+ idx);
        idx = sbIdxOf.IndexOf("abcd");
        Debug.Log("2.2 abcd index:" + idx);
        idx = sbIdxOf.IndexOf("ABC",0,true);
        Debug.Log("2.3 ABC ignoreCase index:" + idx);
        idx= sbIdxOf.IndexOf(new StringBuilder("7878"), 10);
        Debug.Log("2.4 StringBuilder=7878 startIndex:10 index:" + idx);
        Debug.Log("测试2 IndexOf方法测试 完毕\n\r");
        */
        /*lastIndexOf
        Debug.Log("测试3 LastIndexOf方法测试 ");
        int lastIdx;
        StringBuilder sbLastIdxOf = new StringBuilder("我是个大笨蛋xyz_abcd_abc_123!!()笨蛋 999感冒灵*(&*…abc");
        lastIdx = sbLastIdxOf.LastIndexOf("笨蛋");
        Debug.Log("3.1 笨蛋 lastOf idx:"+lastIdx);
        lastIdx = sbLastIdxOf.LastIndexOf('a');
        Debug.Log("3.1 'a' lastOf idx:" + lastIdx);
        Debug.Log("测试3 LastIndexOf方法测试 完毕\n\r");
        */
        /*replaceNoGC
        StringBuilder sbRep = new StringBuilder("x.docx     y.docx      z.docx");
        Debug.Log("测试4 ReplaceNoGC 方法测试");
        Debug.Log(" 4.1 sbRep 原始字符串:"+sbRep.ToString());
        sbRep.ReplaceNoGC(".docx", ".pdf");
        Debug.Log("4.2 string string 替换成.pdf后:"+sbRep.ToString());
        sbRep.ReplaceNoGC(".pdf", new StringBuilder(".xml"));
        Debug.Log("4.2 string StringBuilder 替换成.xml 后:" + sbRep.ToString());
        Debug.Log("测试4 ReplaceNoGC 方法测试 完毕\n\r");
        */
        /*
         * appendNoGC
        Debug.Log("测试5 appendNoGC 方法测试 ");
        StringBuilder sbAppend = new StringBuilder("noting");
        Debug.Log("5.1 原始字符串:"+sbAppend.ToString());
        sbAppend.AppendFormatNoGC_Int(" {0}",1);
        Debug.Log("5.2 appendNoGc_Int:"+ sbAppend.ToString());
        sbAppend.AppendFormatNoGC_Int_String(" {0} {1}",2,"apple");
        Debug.Log("5.3 appendNoGc_Int_String:" + sbAppend.ToString());
        sbAppend.AppendFormatNoGC_String_Int(" {0} {1}","todayisgoodDay",3);
        Debug.Log("5.4 appendNoGc_String_Int:" + sbAppend.ToString());
        sbAppend.AppendFormatNoGC_String_String(" {0}/{1}","player:1","enemy:1");
        Debug.Log("5.5 appendNoGc_String_Int:" + sbAppend.ToString());
        sbAppend.AppendStringBuilderNoGC(new StringBuilder(" 阿巴阿巴"));
        Debug.Log("5.6 AppendStringBuilderNoGC:" + sbAppend.ToString());
        string fmt = "last 20 param:";
        int[] arrParam = new int[20];
        for (int i = 0; i < 20; ++i)
        {
            fmt += "{" + (i).ToString() + "}/";
            arrParam[i] = i + 1;
        }
        sbAppend.AppendFormatNoGC(fmt, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20);
        Debug.Log(" 5.7 AppendFormat:"+ sbAppend.ToString());
        */
    }



相关文章
|
1月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
42 4
|
1月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
101 3
|
3月前
|
存储 API Apache
【zookeeper 第三篇章】客户端 API
本文介绍了Apache ZooKeeper客户端的一些常用命令及其用法。首先,`create`命令用于创建不同类型的节点并为其赋值,如持久化节点、有序节点及临时节点等。通过示例展示了如何创建这些节点,并演示了创建过程中的输出结果。其次,`ls`命令用于列出指定路径下的所有子节点。接着,`set`命令用于更新节点中的数据,可以指定版本号实现乐观锁机制。
33 0
|
8天前
|
API PHP 数据库
PHP中哪个框架最适合做API?
在数字化时代,API作为软件应用间通信的桥梁至关重要。本文探讨了PHP中适合API开发的主流框架,包括Laravel、Symfony、Lumen、Slim、Yii和Phalcon,分析了它们的特点和优势,帮助开发者选择合适的框架,提高开发效率、保证接口稳定性和安全性。
26 3
|
15天前
|
JavaScript 中间件 API
Node.js进阶:Koa框架下的RESTful API设计与实现
【10月更文挑战第28天】本文介绍了如何在Koa框架下设计与实现RESTful API。首先概述了Koa框架的特点,接着讲解了RESTful API的设计原则,包括无状态和统一接口。最后,通过一个简单的博客系统示例,详细展示了如何使用Koa和koa-router实现常见的CRUD操作,包括获取、创建、更新和删除文章。
35 4
|
8天前
|
安全 API 网络架构
Python中哪个框架最适合做API?
本文介绍了Python生态系统中几个流行的API框架,包括Flask、FastAPI、Django Rest Framework(DRF)、Falcon和Tornado。每个框架都有其独特的优势和适用场景。Flask轻量灵活,适合小型项目;FastAPI高性能且自动生成文档,适合需要高吞吐量的API;DRF功能强大,适合复杂应用;Falcon高性能低延迟,适合快速API开发;Tornado异步非阻塞,适合高并发场景。文章通过示例代码和优缺点分析,帮助开发者根据项目需求选择合适的框架。
27 0
|
1月前
|
数据采集 人工智能 自然语言处理
Python实时查询股票API的FinanceAgent框架构建股票(美股/A股/港股)AI Agent
金融领域Finance AI Agents方面的工作,发现很多行业需求和用户输入的 query都是和查询股价/行情/指数/财报汇总/金融理财建议相关。如果需要准确的 金融实时数据就不能只依赖LLM 来生成了。常规的方案包括 RAG (包括调用API )再把对应数据和prompt 一起拼接送给大模型来做文本生成。稳定的一些商业机构的金融数据API基本都是收费的,如果是以科研和demo性质有一些开放爬虫API可以使用。这里主要介绍一下 FinanceAgent,github地址 https://github.com/AI-Hub-Admin/FinanceAgent
|
2月前
|
JSON Go API
使用Go语言和Gin框架构建RESTful API:GET与POST请求示例
使用Go语言和Gin框架构建RESTful API:GET与POST请求示例
|
2月前
|
开发框架 JSON 缓存
震撼发布!Python Web开发框架下的RESTful API设计全攻略,让数据交互更自由!
在数字化浪潮推动下,RESTful API成为Web开发中不可或缺的部分。本文详细介绍了在Python环境下如何设计并实现高效、可扩展的RESTful API,涵盖框架选择、资源定义、HTTP方法应用及响应格式设计等内容,并提供了基于Flask的示例代码。此外,还讨论了版本控制、文档化、安全性和性能优化等最佳实践,帮助开发者实现更流畅的数据交互体验。
81 1
|
2月前
|
JSON 资源调度 JavaScript
Vue框架中Ajax请求的实现方式:使用axios库或fetch API
选择 `axios`还是 `fetch`取决于项目需求和个人偏好。`axios`提供了更丰富的API和更灵活的错误处理方式,适用于需要复杂请求配置的场景。而 `fetch`作为现代浏览器的原生API,使用起来更为简洁,但在旧浏览器兼容性和某些高级特性上可能略显不足。无论选择哪种方式,它们都能有效地在Vue应用中实现Ajax请求的功能。
42 4