String.ToCharArray()方法中的内存优化技巧

简介:
((原文发表于CSDN我的Blog: http://blog.csdn.net/happyhippy/archive/2006/10/29/1356088.aspx
先看下Reflector.exe反汇编.net framework 2.0中Mscorlid.dll,得到String.ToCharArray ()源码:
// .net framework 2.0
public   unsafe   char [] ToCharArray()
{
      
int num1 = this.Length;
      
char[] chArray1 = new char[num1];
      
if (num1 > 0)
      
{
            
fixed (char* chRef1 = &this.m_firstChar)
            
{
               
//使用fixed,防止gc执行垃圾回收后移动堆上的对象而造成无效指针
                  fixed (char* chRef2 = chArray1)
                  
{
                        
string.wstrcpyPtrAligned(chRef2, chRef1, num1);
                        chRef1 
= null;
                  }

            }

      }

      
return chArray1;
}


private   static   unsafe   void  wstrcpyPtrAligned( char *  dmem,  char *  smem,  int  charCount)
{
      
while (charCount >= 8)//四重展开
      {
            
*((int*) dmem) = *((uint*) smem); //拷贝32bit
            *((int*) (dmem + 2)) = *((uint*) (smem + 2));
            
*((int*) (dmem + 4)) = *((uint*) (smem + 4));
            
*((int*) (dmem + 6)) = *((uint*) (smem + 6));
            dmem 
+= 8;
            smem 
+= 8;
            charCount 
-= 8;
      }

      
if ((charCount & 4!= 0)
      
{
            
*((int*) dmem) = *((uint*) smem);
            
*((int*) (dmem + 2)) = *((uint*) (smem + 2));
            dmem 
+= 4;
            smem 
+= 4;
      }

      
if ((charCount & 2!= 0)
      
{
            
*((int*) dmem) = *((uint*) smem);
            dmem 
+= 2;
            smem 
+= 2;
      }

      
if ((charCount & 1!= 0)
      
{
            dmem[
0= smem[0];
      }

}

      ToCharArray中使用了fixed来防止gc执行垃圾回收后移动托管堆上的对象。托管堆的工作方式类似于栈,在某种程度上,连续的对象会在内存紧挨的位置放置,这样很容易使用指向下一个空闲存储单元的堆指针,来确定下一个对象的位置。在gc回收不再引用的对象所占用的内存后,现存对象在内存的位置可能不再连续,此时gc会把所有对象移动到托管堆的端部,在次形成一个连续的块。当然,在移动对象时,这些对象的所有引用都需要用正确的新地址来更新。gc的这个压缩操作时托管的堆与非托管堆的区别所在。使用托管堆,就只需要读取堆指针的值即可,而不是搜索链接地址列表,来查找一个地方放置新对象。因此在.net下实例化对象要快得多。
      使用了fixed后,在fixed块语句内指针所指向的对象将不会在托管堆上移动,从而保证wstrcpyPtrAligned方法中的指正都指向正确的内存地址。

 

wstrcpyPtrAligned方法中也用几点内存优化技巧(《代码优化规则》摘录了更多的技巧):
1.   *((int*) targetAddress) = *((uint*) originalAddress);
        将执行char的指针转换成指向int/uint的指针,从而实现每次读取32bit的数据。


2.   while (charCount >= 8){}
        按四重展开循环。流水线型CPU对分支语句表现出过度敏感,从而降低了分支语句的执行速度(循环语句也是一种分支语句)。可以把CPU比作赛跑选手,把代码比作跑道,选手会在每个弯道(分支语句)处减速。循环展开有助于减少分支,从而减少运行时间。一般按4/8/16重展开,再继续展开的话,优化效果可以忽略,而书写的代码量大,CPU的Cache也容不下这个庞然大物。--《代码优化:有效使用内存》


3.   *((int*) dmem) = *((uint*) smem); 
       *((int*) (dmem + 2)) = *((uint*) (smem + 2));
       *((int*) (dmem + 4)) = *((uint*) (smem + 4));
       *((int*) (dmem + 6)) = *((uint*) (smem + 6));
       dmem += 8;
       smem += 8;
前面四条语句都是根据dmen/smen+X来定位地址,消除了语句之间的数据相关性。如果写成下面这样:
*((int*) dmem) = *((uint*) smem); 
dmem += 2; smem += 2;
*((int*) dmem) = *((uint*) smem); 
dmem += 2; smem += 2;
*((int*) dmem) = *((uint*) smem); 
dmem += 2; smem += 2;
*((int*) dmem) = *((uint*) smem); 
dmem += 2; smem += 2;
这种写法不但增加了代码量,而且还降低了指令执行的并发度。

 

另外,.net framework 1.1中的ToCharArray()的实现与2.0版本中的有所不同,下面是ToCharArray方法的源码:

// .net framework 1.1
public   char [] ToCharArray()
{
      
return this.ToCharArray(0this.Length);
}


public   char [] ToCharArray( int  startIndex,  int  length)
{
      
if (((startIndex < 0|| (startIndex > this.Length)) || (startIndex > (this.Length - length)))
      
{
            
throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
      }

      
if (length < 0)
      
{
            
throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_Index"));
      }

      
char[] chArray1 = new char[length];
      
this.InternalCopyTo(startIndex, chArray1, 0, length);
      
return chArray1;
}


[MethodImpl(MethodImplOptions.InternalCall)]
internal   extern   void  InternalCopyTo( int  sourceIndex,  char [] destination,  int  destinationIndex,  int  count);

本文转自Silent Void博客园博客,原文链接:http://www.cnblogs.com/happyhippy/archive/2006/12/23/601211.html ,如需转载请自行联系原作者
相关文章
|
2月前
|
存储 安全 iOS开发
内存卡怎么格式化?6个格式化方法供你选
随着使用时间的增加,内存卡可能会因为数据积累、兼容性或是文件系统损坏等原因需要进行格式化。那么怎样正确格式化内存卡呢?格式化内存卡的时候需要注意什么呢?本文会给大家提供详细的步骤,帮助大家轻松完成格式化内存卡的操作。
|
4月前
|
监控 JavaScript Java
Node.js中内存泄漏的检测方法
检测内存泄漏需要综合运用多种方法,并结合实际的应用场景和代码特点进行分析。及时发现和解决内存泄漏问题,可以提高应用的稳定性和性能,避免潜在的风险和故障。同时,不断学习和掌握内存管理的知识,也是有效预防内存泄漏的重要途径。
349 62
|
7月前
for循环和String类下方法的一个练习题
for循环和String类下方法的一个练习题
69 1
|
4月前
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
103 6
|
5月前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
105 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
|
5月前
|
机器学习/深度学习 算法 物联网
大模型进阶微调篇(一):以定制化3B模型为例,各种微调方法对比-选LoRA还是PPO,所需显存内存资源为多少?
本文介绍了两种大模型微调方法——LoRA(低秩适应)和PPO(近端策略优化)。LoRA通过引入低秩矩阵微调部分权重,适合资源受限环境,具有资源节省和训练速度快的优势,适用于监督学习和简单交互场景。PPO基于策略优化,适合需要用户交互反馈的场景,能够适应复杂反馈并动态调整策略,适用于强化学习和复杂用户交互。文章还对比了两者的资源消耗和适用数据规模,帮助读者根据具体需求选择最合适的微调策略。
1685 5
|
5月前
|
缓存 监控 Java
在使用 Glide 加载 Gif 动画时避免内存泄漏的方法
【10月更文挑战第20天】在使用 Glide 加载 Gif 动画时,避免内存泄漏是非常重要的。通过及时取消加载请求、正确处理生命周期、使用弱引用、清理缓存和避免重复加载等方法,可以有效地避免内存泄漏问题。同时,定期进行监控和检测,确保应用的性能和稳定性。需要在实际开发中不断积累经验,根据具体情况灵活运用这些方法,以保障应用的良好运行。
|
4月前
|
JavaScript 前端开发 开发者
|
7月前
|
JavaScript 算法 前端开发
JS算法必备之String常用操作方法
这篇文章详细介绍了JavaScript中字符串的基本操作,包括创建字符串、访问特定字符、字符串的拼接、位置查找、大小写转换、模式匹配、以及字符串的迭代和格式化等方法。
JS算法必备之String常用操作方法
|
7月前
|
XML Java API
List与String相互转化方法汇总
本文汇总了List与String相互转化的多种方法,包括使用`String.join()`、`StringBuilder`、Java 8的Stream API、Apache Commons Lang3的`StringUtils.join()`以及Guava的`Joiner.on()`方法实现List转String;同时介绍了使用`split()`方法、正则表达式、Apache Commons Lang3的`StringUtils.split()`及Guava的`Splitter.on()`方法实现String转List。
282 1
List与String相互转化方法汇总