将0、1字节数组转为0、1字符串:unsafe/fixed性能优化方案咨询
优化字节数组转0/1字符串的性能(含unsafe/fixed实现)
首先得点明你原代码性能拉胯的核心原因:每次循环里的_str += b.ToString()是噩梦根源——C#的字符串是不可变的,每一次拼接都会在堆上创建一个全新的字符串对象,处理1000万条数据时,会产生海量临时对象和GC压力,性能自然差到离谱。
下面给你两种优化方案,包括你问的unsafe/fixed实现:
方案一:不用unsafe的高效实现(推荐优先使用)
既然你的字节数组里只有0和1,我们可以直接预分配一个char[](长度和字节数组一致),逐个把字节转成对应的字符,最后一次性生成字符串——完全避免了中间字符串的分配:
private string Convert(byte[] bytes) { // 预分配足够长度的char数组,避免动态扩容开销 char[] chars = new char[bytes.Length]; for (int i = 0; i < bytes.Length; i++) { // 直接映射0/1到字符,比ToString()快得多 chars[i] = bytes[i] == 0 ? '0' : '1'; } return new string(chars); }
如果用StringBuilder的话,虽然比直接拼接好,但预分配char数组的方式性能更高——因为StringBuilder内部也是用char数组,但多了一层封装开销。
方案二:使用unsafe/fixed的内存直接操作
如果你确实想用unsafe和fixed来直接操作内存(性能和上面的方案差距不大,但能满足你的需求),可以这样做:通过fixed关键字固定字节数组和char数组的内存,防止GC移动它们,然后用指针直接复制转换,避免托管代码的边界检查开销:
private unsafe string ConvertUnsafe(byte[] bytes) { char[] chars = new char[bytes.Length]; // 固定字节数组和char数组的内存地址 fixed (byte* bPtr = bytes) fixed (char* cPtr = chars) { byte* bytePtr = bPtr; char* charPtr = cPtr; int length = bytes.Length; for (int i = 0; i < length; i++) { // 直接通过指针赋值,利用ASCII码映射('0'的ASCII值是48) *charPtr++ = (char)(*bytePtr++ + '0'); } } return new string(chars); }
注意事项:
- 使用
unsafe代码需要在项目设置里开启"允许不安全代码"选项(右键项目→属性→生成→勾选"允许不安全代码")。 - 这个方案的性能和预分配char数组的托管方案非常接近,优势仅在于极端场景下略少的边界检查开销,但代码可读性稍差,所以如果不是特别必要,优先用方案一。
最后再强调一句:不管用哪种方案,都绝对不要用原代码的字符串拼接方式,那是处理大量字符串场景的大忌。
内容的提问来源于stack exchange,提问作者Milad Hatami
相关产品推荐
相关产品推荐

