C#里面滥用String造成的性能问题
前两天给我们的json
写一个解析函数, 之前用的正宗的json parser
, 支持完整的json
特性. 但是实际上我们用到特性, 只有key-value
的映射, value的类型只有数字
和字符串
两种类型. 由于parse的速度比较慢, 所以我打算自己用字符串解析一遍. 第一个能工作的原型出来的时候, 速度和json解析差不多. 做了profile之后发现, 绝大部分时间都浪费在构造String
和检索IndexOf
上面.
下了coreclr的源码研究了一下, 发现String.Split
在实现的时候, 先扫描一遍split
, 计算有多少个元素, 然后分配一个Array
, 然后再去做Split
操作. Split操作里面还会再new一个新的String出来, 顺便做一下拷贝. 看到这里我就惊呆了, 本来String在C#和Jawa这两个托管语言里面都是不可变的, 那么为什么他们不用一个Slice
去构造一个SubString
呢?
网上搜了一下, 也没发现有人写的StringSlice
或者类似的东西, 我就顺手撸了一个StringView
, 一个只读的StringSlice
.
- using System.Collections.Generic;
- public unsafe struct StringView
- {
- public static readonly StringView Empty = new StringView("");
- public StringView(string str) : this(str, , str.Length) { }
- public StringView(string str, int begin, int length)
- {
- this.str = str;
- this.begin = begin;
- this.length = length;
- if (str.Length <= ) return;
- if (this.begin < ||
- this.begin >= this.str.Length ||
- this.begin + this.length > this.str.Length)
- {
- throw new System.Exception("StringView's Constructor OutOfBound");
- }
- }
- public int IndexOf(char c, int start = )
- {
- fixed (char* p = this.str)
- {
- for (int i = start; i < length; ++i)
- {
- if (p[this.begin + i] == c) return i;
- }
- }
- return -;
- }
- private static bool ArrayContains(char[] array, char c)
- {
- int length = array.Length;
- fixed (char* p = array)
- {
- for (int i = ; i < length; ++i)
- if (p[i] == c) return true;
- }
- return false;
- }
- public int IndexOf(char[] array, int start = )
- {
- if (array.Length == ) return this.IndexOf(array[], start);
- fixed (char* p = this.str)
- {
- for (int i = start; i < length; ++i)
- {
- if (ArrayContains(array, p[this.begin + i])) return i;
- }
- }
- return -;
- }
- public int IndexOf(string s, int start = )
- {
- int s1_length = this.str.Length;
- int s2_length = s.Length;
- fixed (char* p1 = this.str)
- {
- fixed (char* p2 = s)
- {
- int index = this.IndexOf(p2[], start);
- while (index >= )
- {
- if (s2_length > s1_length - this.begin - index)
- return -;
- bool match = true;
- for (int i = ; i < s2_length; ++i)
- {
- if (p1[this.begin + index + i] != p2[i]) { match = false; break; }
- }
- if (match) return index;
- index = this.IndexOf(p2[], index + );
- }
- return -;
- }
- }
- }
- public unsafe char this[int index]
- {
- get
- {
- if (index < || index >= this.length)
- {
- throw new System.Exception("StringView's Index OutOfBound");
- }
- fixed (char* p = this.str)
- {
- return p[this.begin + index];
- }
- }
- }
- public StringView SubString(int begin)
- {
- return this.SubString(begin, this.length - begin);
- }
- public StringView SubString(int begin, int length)
- {
- return new StringView(this.str, this.begin + begin, length);
- }
- public List<StringView> Split(char split, List<StringView> array)
- {
- array.Clear();
- int index = ;
- int pos1 = , pos2 = ;
- pos2 = this.IndexOf(split);
- while (pos2 > && pos2 < this.length)
- {
- array.Add(new StringView(str, this.begin + pos1, pos2 - pos1));
- pos1 = pos2 + ;
- pos2 = this.IndexOf(split, pos1);
- ++index;
- }
- if (pos1 != this.length) array.Add(new StringView(str, this.begin + pos1, this.length - pos1));
- return array;
- }
- public override bool Equals(object obj)
- {
- if (obj is StringView)
- {
- StringView v = (StringView)obj;
- return this.Equals(v);
- }
- return false;
- }
- public bool Equals(StringView v)
- {
- if (v.Length != this.Length) return false;
- for (int i = ; i < this.Length; ++i)
- {
- if (this[i] != v[i]) return false;
- }
- return true;
- }
- internal static int CombineHashCodes(int h1, int h2)
- {
- return (((h1 << ) + h1) ^ h2);
- }
- public override int GetHashCode()
- {
- int hash_code = ;
- for (int i = ; i < this.length; ++i)
- {
- hash_code = CombineHashCodes(hash_code, this[i].GetHashCode());
- }
- return hash_code;
- }
- public int Length { get { return this.length; } }
- public override string ToString()
- {
- return this.str.Substring(begin, length);
- }
- public string GetRawString() { return this.str; }
- public int GetBegin() { return this.begin; }
- private string str;
- private int begin;
- private int length;
- }
为了方便替换String, 很多接口都保持了一致. 目前这个版本只是满足我自己的需求, 以后可以考虑继续完善添加String的函数进来.
之前说的IndexOf
也比较耗, 因为String
的索引器
会带有边界检测, 而IndexOf
一直在用索引器, 所以个人感觉是不太合适的, 所以我的StringView
一直在用指针….
PS: 修改之后的纯text parse, 速度比json parse的速度快一倍以上, 性能还不错, 实际上还有提升的空间
PS: 现在比较完整的StringView已经上传至github, https://github.com/egmkang/StringView 添加了ToInt64
, StringBuilder.Append
支持
C#里面滥用String造成的性能问题的更多相关文章
- 从.Net版本演变看String和StringBuild性能之争
在C#中string关键字的映射实际上指向.NET基类System.String.System.String是一个功能非常强大且用途非常广泛的基类,所以我们在用C#string的时候实际就是在用.NE ...
- 从.Net版本演变看String和StringBuilder性能之争
在C#中string关键字的映射实际上指向.NET基类System.String.System.String是一个功能非常强大且用途非常广泛的基类,所以我们在用C#string的时候实际就是在用.NE ...
- JVM系列之:String.intern的性能
目录 简介 String.intern和G1字符串去重的区别 String.intern的性能 举个例子 简介 String对象有个特殊的StringTable字符串常量池,为了减少Heap中生成的字 ...
- string insert 的性能分析
有这样一个网络传输包. 前端有个固定的包头,包含了后面传输body的长度信息. 在有拷贝的前提下,我们选用什么性能比较高呢? 方案一 复用data_buffer str ...
- Java中String连接性能的分析【转】
[转]http://www.blogjava.net/javagrass/archive/2010/01/24/310650.html 总结:如果String的数量小于4(不含4),使用String. ...
- Java中String连接性能的分析
总结:如果String的数量小于4(不含4),使用String.concat()来连接String,否则首先计算最终结果的长度,再用该长度来创建一个StringBuilder,最后使用这个String ...
- 也谈string.Join和StringBuilder的性能比较
前几天在园子里面看到一篇讲StringBuilder性能的文章.文章里面给出了一个测试用例,比较StringBuilder.AppendJoin和String.Join的性能.根据该测试结果,&quo ...
- java中String的相等比较
首先贴出测试用例: package test; import org.junit.Test; /** * Created by Administrator on 2015/9/16. * */ pub ...
- java中的String设计原理
首先,必须强调一点:String Pool不是在堆区,也不是在栈区,而是存在于方法区(Method Area) 解析: String Pool是常量池(Constant Pool)中的一块. 我们知 ...
随机推荐
- Kotlin的参考资料
参考资料和站点 http://kotlinlang.org/ 官方网站 https://github.com/JetBrains/kotlin/releases/tag/v1.0.6 下载compil ...
- Django 自带认证功能auth模块和User对象的基本操作
一.auth模块 from django.contrib import auth django.contrib.auth中提供了许多方法,这里主要介绍其中的三个: authenticate() ...
- 4.9Python数据处理篇之Matplotlib系列(九)---子图分布
目录 目录 前言 (一)subplot()方法 ==1.语法说明== ==2.源代码== ==3.输出效果== (二)subplot2grid方法 ==1.语法说明== ==2.源代码== ==3.展 ...
- spring的基于XML方式的属性注入
1.掌握spring的属性注入的方法: 1.1构造方法注入普通值---------<constructor-arg>标签的使用 首先新建一个类 package spring.day1.de ...
- 【夯实PHP基础】PHP多进程-- pcntl_fork实现
本文地址 参考文档 分享提纲: 1. 概述 2.安装(只支持Linux) 3. 代码实验多进程pcntl_fork 4. 具体解释 1. 概述 PHP有个pcntl_fork的函数可以实现多进程,但要 ...
- 《Java大学教程》—第24章 Java的背景
本章主要介绍的是Java的背景知识,通过了解历史知道Java与其他语言的区别,以便更好选择在什么场景下使用Java. 24.2 语言的尺寸Java语言短小.紧凑,以C++为基础,放弃了一些特定的 ...
- Announcing the Updated NGINX and NGINX Plus Plug‑In for New Relic (Version 2)
In March, 2013 we released the first version of the “nginx web server” plug‑in for New Relic monitor ...
- router-link 自定义点击事件
<li v-for="(item, index) in menuList"> <router-link class="classify" ta ...
- Scrapy 框架 中间件 代理IP 提高效率
中间件 拦截请求跟响应 进行ua(User-Agent ) 伪装 代理 IP 中间件位置: 引擎 和下载器 中间 的中间件 ( 下载中间件) 引擎 跟 spider 中间 的中间件 ( 爬虫中间件)( ...
- 关于发邮件报错535 Error:authentication failed解决方法
写在最前面 相信看到535报错的同学代码编写方面都没有问题,只是不明白为什么填写了帐号密码后还是报535错误,这里我以163邮箱为例,并使用Python讲解怎么解决535问题 代码如下: import ...