前两天给我们的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造成的性能问题的更多相关文章

  1. 从.Net版本演变看String和StringBuild性能之争

    在C#中string关键字的映射实际上指向.NET基类System.String.System.String是一个功能非常强大且用途非常广泛的基类,所以我们在用C#string的时候实际就是在用.NE ...

  2. 从.Net版本演变看String和StringBuilder性能之争

    在C#中string关键字的映射实际上指向.NET基类System.String.System.String是一个功能非常强大且用途非常广泛的基类,所以我们在用C#string的时候实际就是在用.NE ...

  3. JVM系列之:String.intern的性能

    目录 简介 String.intern和G1字符串去重的区别 String.intern的性能 举个例子 简介 String对象有个特殊的StringTable字符串常量池,为了减少Heap中生成的字 ...

  4. string insert 的性能分析

    有这样一个网络传输包. 前端有个固定的包头,包含了后面传输body的长度信息. 在有拷贝的前提下,我们选用什么性能比较高呢?         方案一         复用data_buffer str ...

  5. Java中String连接性能的分析【转】

    [转]http://www.blogjava.net/javagrass/archive/2010/01/24/310650.html 总结:如果String的数量小于4(不含4),使用String. ...

  6. Java中String连接性能的分析

    总结:如果String的数量小于4(不含4),使用String.concat()来连接String,否则首先计算最终结果的长度,再用该长度来创建一个StringBuilder,最后使用这个String ...

  7. 也谈string.Join和StringBuilder的性能比较

    前几天在园子里面看到一篇讲StringBuilder性能的文章.文章里面给出了一个测试用例,比较StringBuilder.AppendJoin和String.Join的性能.根据该测试结果,&quo ...

  8. java中String的相等比较

    首先贴出测试用例: package test; import org.junit.Test; /** * Created by Administrator on 2015/9/16. * */ pub ...

  9. java中的String设计原理

    首先,必须强调一点:String Pool不是在堆区,也不是在栈区,而是存在于方法区(Method Area) 解析: String Pool是常量池(Constant  Pool)中的一块. 我们知 ...

随机推荐

  1. virtualbbox centos7 NAT模式外网 Host-only Adapter模式联网 双网卡

    1.下载oracle VM virtualbox  centos7 1.1. 下载地址:https://www.virtualbox.org/wiki/Downloads https://www.ce ...

  2. EOS与以太坊有哪些区别?

    以太坊是一个专门为开发和运行去中心化应用(DAPP)搭建的智能合约平台:EOS与以太坊类似,同样是基于智能合约和区块链而搭建.但是,从技术和设计理念等方面来看,这两者之间实际上存在明显的区别. 那么E ...

  3. python爬虫之小说网站--下载小说(正则表达式)

    python爬虫之小说网站--下载小说(正则表达式) 思路: 1.找到要下载的小说首页,打开网页源代码进行分析(例:https://www.kanunu8.com/files/old/2011/244 ...

  4. python编写文件统计脚本

    python编写文件统计脚本 思路:用os模块中的一些函数(os.listdir().os.path.isdir().os.path.join().os.path.abspath()等) 实现功能:显 ...

  5. IPerf——网络测试工具介绍与源码解析(4)

    上篇随笔讲到了TCP模式下的客户端,接下来会讲一下TCP模式普通场景下的服务端,说普通场景则是暂时不考虑双向测试的可能,毕竟了解一项东西还是先从简单的情况下入手会快些. 对于服务端,并不是我们认为的直 ...

  6. Git Extensions 使用小结

    1.查看仓库 2.创建分支 然后会自动创建一个 Commit ,推送到远端分支即可. 3.合并分支 注意1.自动提交需要没有无法自动合并的冲突才行. 注意2.快进线指的是将别人的提交原封不动附加到自己 ...

  7. LeetCode算法题-Rotate Array(Java实现)

    这是悦乐书的第184次更新,第186篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第43题(顺位题号是189).给定一个数组,将数组向右旋转k步,其中k为非负数.例如: ...

  8. formbuild拖拽表单设计器

        formbuild拖拽表单设计器 表单设计器适用于OA系统.问卷调查系统.考试系统等系统,具体使用请前至官网API请点击 formbuild拖拽表单设计器     formbuild迭代几个功 ...

  9. C#多线程编程のSemaphore(信号量,负责协调各个线程)

    Semaphore负责协调线程,可以限制对某一资源访问的线程数量 这里对SemaphoreSlim类的用法做一个简单的例子: namespace WpfApplication6 { /// <s ...

  10. 正则表达式工具RegexBuddy

    1 下载 RegexBuddy 并安装 安装后的界面如下:     2 切换布局 点击右上角的彩色格子图标,选择 Side by Side Layout:     这种布局的好处是,Create 面板 ...