结构体转换成对应的byte数组

最近看到一个帖子,问的是怎么把自己定义的结构体转换成对应的byte数组,一般来说,都会想到用Marshal类来完成这个功能,其实还有一个方法也可以,那就是利用unsafe代码。

  先定义假想的一个值类型:

Code

  然后,定义一个公用方法签名:Action<MyStruct, Stream>,这个是为了方便之后的几种不同方式做性能测试。

  先来看看Marshal类是怎么做到的:

Code

  然后看看unsafe代码是如何做到的:

Code

  比较两者,可以发现,使用Marshal类出现了装箱,新建数组(因为签名定成了输出到流),分配堆空间,和释放对空间等,当然,实战中可以避免掉新建数组的代价,但是总体代价依然高于unsafe方法,当然,用Marshal可以很轻松的做到把任意值类型copy到数组里面,这一点上unsafe方法就吃亏了,因为unsafe方法必须要非常明确的支出结构体的具体类型。

  先来看一个性能对比:

Code

  在我机器(型号比较老。。。)上的运行结果是:135,718

可以发现unsafe代码的性能优势非常明显,但是,如果不解决类型问题,unsafe的方式显然实用意义仍然小与Marshal方式,可能有人会想到用泛型,但是不幸的是泛型无法作用于unsafe代码,也就是说,T*是无法通过编译的,难道真的走投无路了吗?

  别忘了,.net还有一个杀手锏,LCG——轻量级代码生成,就是凭借LCG,IronPython的性能才能如此出众。我们无法在运用泛型指针,但是我们可以用泛型方法来生成对应类型的非泛型unsafe代码,来实现一个:

Code

  有点长,但是性能如何哪?来改造一下测试代码:

Code

  看看结果如何:136,713,142

  真的这么强大吗?LCG本身的代价哪?可以把count修改成1,再次运行,得到的结果是:0,0,14

  也可以看到在循环次数太少的情况下,获得的性能优势不足以弥补LCG本身的消耗,那么多少是临界点哪?

  我的机器上,经过多次试验,发现21000时,Marshal方法和LCG方法的时间消耗相等,也就是说,当次数小于21000次是,Marshal占了上风,单是如果,超过21000次时,LCG带来的性能优势,就足以弥补代码生成的损失了。(当然,前提是生成的代码,以委托的方式被缓存下来)

  如果,无论次数多少,都要求最高性能的话,那就只有用unsafe的方法,把每一个需要用到的结构体都写一遍了,就是用人力换速度。。。

posted @ 2010-05-25 17:03  一浩瀚星空一  阅读(520)  评论(0编辑  收藏  举报