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)中的一块. 我们知 ...
随机推荐
- x3D 下载以及如何使用原版NetBeans IDE 来搭建x3d编辑环境
安装前: Overview X3D-Edit version 3.3 standalone application and Netbeans plugin are available and read ...
- jQuery实现画面的展开、收起和停止
主要用到动画效果中的三个操作 ("#id").slideDown(3000): // 后面的数字表示效果的时长 ("#id").stop(); ("# ...
- mssql sql server 其它系统函数 parsename 点语法字符串分割函数应用简介
转自:http://www.maomao365.com/?p=4534 一. parsename函数功能简介 parsename函数的主要功能是:可以快速的使用”.”关键字分解字符串,并返回.分解后指 ...
- mysql性能排查思路
mysql性能瓶颈排查 top/free/vmstat/sar/mpstat 查看mysqld进程的cpu消耗占比 确认mysql进程的cpu消耗是%user, 还是sys%高 确认是否是物理内存 ...
- c/c++叉树的创建与遍历(非递归遍历左右中,不破坏树结构)
二叉树的创建与遍历(非递归遍历左右中,不破坏树结构) 创建 二叉树的递归3种遍历方式: 1,先中心,再左树,再右树 2,先左树,再中心,再右树 3,先左树,再右树,再中心 二叉树的非递归4种遍历方式: ...
- php快速定位当前调用的类的位置
php快速定位当前调用的类的位置 $func = new ReflectionMethod('类名', '方法名'); $start = $func->getStartLine() - 1; $ ...
- [Hive_10] Hive 的分析函数
0. 说明 Hive 的分析函数 窗口函数 | 排名函数 | 最大值 | 分层次 | lead && lag 统计活跃用户 | cume_dist 1. 窗口函数(开窗函数) ove ...
- 讲解wpe抓包,封包
相信大多数朋友都是会使用WPE的,因为这里也有不少好的教程,大家都辛苦了!先说说接触WPE的情况.当时好像是2011年,我本来不知道WPE对游戏竟有如此大的辅助作用的.起先找WPE软件的时候,只是因为 ...
- Apache中httpd.conf文件的详解
PHP中,Apache的配置至关重要,特别是httpd.conf这个文件,它是Apache中的核心文件.好了,废话不说,今天将这个文件中的一些内容讲解一番. ServerRoot "d:/w ...
- ubuntu16.04如何安装多个版本的CUDA
我的机器是CUDA16.04的,之前装过CUDA10.0,因为一些原因,现在需要安转CUDA9.0. 1.首先https://developer.nvidia.com/cuda-90-download ...