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)中的一块. 我们知 ...
随机推荐
- LeetCode算法题-Construct Quad Tree(Java实现)
这是悦乐书的第224次更新,第237篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第91题(顺位题号是427).我们想使用四叉树来存储N×N布尔网格.网格中的每个单元格只 ...
- SpringCloud之初识Feign ----- 分布式负载自动拼接请求的URL
在前面的学习中,我们使用了Ribbon的负载均衡功能,大大简化了远程调用时的代码: String baseUrl = "http://user-service/user/"; Us ...
- 轮播图插件swiper 的使用
引入文件(注:目前版本号为Swiper3.x) <link rel="stylesheet" type="text/css" href="//s ...
- JavaScript的基本包装类型概述与基本包装类型_Number类型
JavaScript的基本包装类型示例 为了便于操作基本类型值,javaScript 提供了 3 个特殊的引用类型:Boolean.Number和 String. 这些类型与其他引用类型相似,但同时也 ...
- Spring国际化模块
1.Spring3.1.0实现原理分析(二).国际化(i18n) https://blog.csdn.net/roberts939299/article/details/69666291
- vue与jquey
vue与jquey jQuery是使用选择器($)选取DOM对象,对其进行赋值.取值.事件绑定等操作,其实和原生的HTML的区别只在于可以更方便的选取和操作DOM对象,而数据和界面是在一起的.比如需要 ...
- 最简单的C#UDP通讯示例
参考:https://www.cnblogs.com/zxyc2000/articles/2846662.html 和 https://www.cnblogs.com/mq0036/p/7302605 ...
- UVA11491-Erasing ans Winning(贪心)
Problem UVA11491-Erasing ans Winning Accept: 799 Submit: 5753Time Limit: 3000 mSec Problem Descript ...
- 转://Oracle中User和Schema的区别和联系
今天在阅读Oracle官方文档的时候,读到schema的基本概念,这就让我产生了一个疑问:user和schema两者之间到底有什么区别?为了更深层次的理解二者之间的区别和联系,以下是官方文档中关于us ...
- 小a的子序列 (线性dp)
思路:设dp[i][j]表示最大数为j,i为第i的位置的萌值.那么推导过程就是两种情况:1.第i位数不放数字,则结果就是dp[i-1][j]; 2.第i位放数字,则结果就是前面的萌值sum+dp[i- ...