前两天给我们的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. java----八种排序算法

    1.直接插入排序 经常碰到这样一类排序问题:把新的数据插入到已经排好的数据列中. 将第一个数和第二个数排序,然后构成一个有序序列 将第三个数插入进去,构成一个新的有序序列. 对第四个数.第五个数……直 ...

  2. Failed to decrypt protected XML node "DTS:Password" with error 0x8009000B "Key not valid for use in specified state.". You may not be authorized to access this information. This error occurs when t

    Question SSIS包从A服务器搬迁到B服务器,运行报错 Description: Failed to decrypt protected XML node "DTS:Password ...

  3. mysql 数据库 命令行的操作——对表和字段的操作

    一.对表的操作 1.查看所有表 show tables: 2.创建表 create table 表名(字段1 类型1 约束1 ,字段2 类型2 约束2): 3.修改表的名字 rename table ...

  4. ugui中toggle.isOn的属性笔记

    准备知识 toggle:指unity3d引擎中UGUI的 toggle组件 (单选框) 本文使用lua语言描述 事件触发 使用unity的ugui,你如果细心观察会发现toggle在界面被关闭/隐藏( ...

  5. celery 定时任务

    用celery定时任务,定时删除文件夹 tasks.py from celery import Celery import os import shutil app = Celery('demo') ...

  6. 基于centOS7:新手篇→tomcat的部署方式

    一.自动部署 将项目直接拷贝到webapps目录下,通过项目名直接访问 二.在server.xml中指定项目 打开Tomcat/conf/server.xml文件,在host标签中加入以下参数并重启T ...

  7. 使用google的pprof工具以及在gin中集成pprof

    首先我们得先安装这两个工具: google的pprof工具链 go get -u github.com/google/pprof gin的pprof工具 go get github.com/DeanT ...

  8. nuxt拦截IE浏览器

    需求场景 判断浏览器类型,让譬如IE的低版本浏览器跳转到指定提示浏览器升级页面. 难点分析 使用过的都知道,nuxt没有暴露主入口页面也就是index.html啊,我们以前常用的IE条件判断没地方写. ...

  9. HashMap源码调试——认识"put"操作

    前言:通常大家都知道HashMap的底层数据结构为数组加链表的形式,但其put操作具体是怎样执行的呢,本文通过调试HashMap的源码来阐述这一问题. 注:jdk版本:jdk1.7.0_51 1.pu ...

  10. 【JSOI2018】潜入行动

    [JSOI2018]潜入行动 树形\(DP\).设\(f_{i,j,0/1,0/1}\)表示以\(i\)为根的子树中,用了\(j\)个监听器,是否放置了监听器,是否被监听的方案数.转移就多讨论几种情况 ...