gap_vector - 符合编辑器习惯的快速容器类算法
来源:互联网 发布:电话软件 编辑:程序博客网 时间:2024/06/16 23:08
前言:
STL的确是好东西,可以让程序简洁,同时丰富的算法库可以减少程序出现BUG的几率。但其性能问题却是公认的最大缺点。因此针对一些特殊的应用场景,比如频繁访问的容器,如果要做到性能最大化,则有必要自己重写一些简洁轻量级的算法。
立项:
前一阵子研究的窗口命令行控件需要一个内置的文本编辑器,文本编辑器的特点就是内容会经常变化,会有大量的插入、删除、查找操作。原本的设计是用标准的vector容器来保存文本内容的。在遇到了性能问题以后,用VS2010的性能分析工具发现GDI是影响性能的根本原因,但是vector的频繁操作对影响的性能也不小,因此有必要一起优化一下。
分析:
文本编辑器的行为特征是基于光标的,即先把光标移动到要修改的地方,然后再进行连续的插入或者删除操作,而且多数是在文本的末尾进行操作。因此可以总结出来,编辑器的行为特征主要点:
1. 大部分的修改操作是在文本末尾进行的
2. 连续输入,即在相同位置连续的插入操作
第1点没有问题,因为在容器的最后进行插入和删除并不需要移动数据,不会太影响性能。而第2点则有很大的优化空间,一般情况保存文本最适合是用vector容器,每次插入和删除一个字符,意味着后面的文本都需要往后或往前移动一个字节。(不要说用list链表来存储文本数据,要不然查找会消耗掉所有的性能)。
设计:
既然大部分的修改都是在当前光标处连续修改,那么优化的思想就是在每次插入或删除数据的时候,在当前光标位置处预留出足够的空间,使得在同一个位置进行连续的插入或删除操作的时候不需要再进行数据的移动,加速后续的连续修改操作。这个预留的空间称为Gap。
举个实际的例子,假设vector容器有下面的初始化数据:
如果要在光标处插入一个字符,标准的vector算法是把后面的内容往后移动一个字节,然后在当前位置处插入一个字符:
![](http://my.csdn.net/uploads/201208/22/1345622458_2137.png)
可以预见,假如在当前光标再次输入一个字符,则后面的文本还需要再次被移动,这样性能就在不停地移动数据中消耗掉了。而gap_vector的做法是这样,如果需要在当前光标处插入一个字符,则后面的文本会往后移动一大段,保证当前光标处预留出足够的空间:
如果后续再进行连续的插入操作,则不需要再移动数据,直接在Gap中进行插入即可:
![](http://my.csdn.net/uploads/201208/22/1345622002_2082.png)
在文本最后的插入操作也不需要移动文本,直接在文本最后插入即可:
![](http://my.csdn.net/uploads/201208/22/1345622005_1784.png)
仅仅在Gap空间不足的情况下,比如现在Gap空间只剩下23个字节,而我们需要在当前光标处粘贴一个32字节的文本,这时候需要重新调整Gap的空间:
![](http://my.csdn.net/uploads/201208/22/1345622009_6508.png)
当调整出足够的空间后,再在当前光标处插入文本:
![](http://my.csdn.net/uploads/201208/22/1345622012_7215.png)
从上面的例子可以看出,Gap预留空间的原则是,Gap大小 = 文本最后空闲空间的大小,这样可以同时兼顾当前光标处和文件最末处的文本修改操作。
代码:
#define GAP_VECTOR_GROW_SIZE 1024template <typenameT>class gap_vector{public: gap_vector() : _body(NULL), _body_size(0),_grow_size(GAP_VECTOR_GROW_SIZE) { clear(); } ~gap_vector() { clear(); if(_body) { delete[] _body; _body = NULL; } } // FUNCTIONclear(): clear vector without changing the buffer size voidclear() { _len_before_gap = 0; _len_after_gap = 0; _len_content = 0; _len_gap = 0; _gap_pos = 0; } // FUNCTIONset_grow_size(): modify auto-growth size of the buffer voidset_grow_size(int grow_size) { _grow_size = max(1, grow_size); } // FUNCTIONresize_buffer(): resize the buffer boolresize_buffer(int size) { if(size> _body_size) { T* new_body = new T [size]; if(NULL== new_body) { returnfalse; } memcpy(new_body, _body, _body_size* sizeof(T)); _body_size = size; delete[] _body; _body = new_body; } return true; } // FUNCTION resize():resize the vector boolresize(int size) { // extend atthe end of content gap_at(_len_content, size -_len_content); _len_before_gap += size - _len_content; _len_gap -= size - _len_content; _len_content += size - _len_content; return true; } // FUNCTIONgap_at(): move the gap position boolgap_at(int pos,introom) { // grow sizewhen no enough room while(room+ _len_content > _body_size) { if(!resize_buffer(_body_size+ _grow_size)) { returnfalse; } } // right gapposition and enough room if(_len_before_gap== pos && _len_gap >= room) { return true; } if(_len_gap< room) { // new_gap_pos = _len_before_gap + room + (_body_size - _len_before_gap - room -_len_after_gap) / 2 intnew_gap_pos = (_body_size + _len_before_gap + room - _len_after_gap) / 2; if(new_gap_pos+ _len_after_gap > _body_size) {returnfalse; } // not enough room memmove(_body + new_gap_pos, _body+ _gap_pos, (_len_after_gap) *sizeof(T)); _gap_pos = new_gap_pos; _len_gap = _gap_pos -_len_before_gap; } //BBB*BBB AAAAAA => BBB BBBAAAAAA if(pos< _len_before_gap) { memmove(_body + _gap_pos -_len_before_gap + pos, _body + pos, (_len_before_gap - pos) *sizeof(T)); } //BBBBBB AAA*AAA => BBBBBBAAA AAA else { memmove(_body + _len_before_gap,_body + _gap_pos, (pos - _len_before_gap) *sizeof(T)); } _len_before_gap = pos; _len_after_gap = _len_content - pos; _gap_pos = pos + _len_gap; return true; } // FUNCTIONinsert(): insert content to pos boolinsert(int pos, T* pt,intsize) { // insert atthe end and with enough room if(pos== _len_content && _len_gap + _len_content + size <= _body_size) { memcpy(_body + _len_gap +_len_content, pt, size * sizeof(T)); _len_content += size; _len_after_gap += size; returntrue; } // move gapand ensure enough room for new cells if(!gap_at(pos,size)) { returnfalse; } // insert atpos memcpy(_body + pos, pt, size * sizeof(T)); _len_before_gap += size; _len_content += size; _len_gap -= size; return true; } // FUNCTIONremove(): remove content from pos voidremove(int pos,intlength = 1) { intremove_len = max(0, min(_len_content - pos, length)); if(!gap_at(pos+ remove_len, 0)) { return; } _len_before_gap -= remove_len; _len_content -= remove_len; _len_gap += remove_len; } // OPERATOR =:copy content from source vector void operator = (gap_vector<T> &src) { clear(); set_buffer_size(src.length()); for(int i = 0; i < src.length(); ++i) { insert(i, &src[i], 1); } } inline int buffer_size() { return _body_size; } // get buffersize inline int length() { return _len_content; } // get vectorlength inlineT& back() {return_body[_len_content + _len_gap - 1]; } // get last cell inline bool push_back(T& t) { returninsert(_len_content, &t, 1); } // push cell to the end inlineT& operator [] (intn) { if(n<_len_before_gap)return *(_body+n); elsereturn *(_body+n+_len_gap); } private: T *_body; // head pointof vector buffer int _body_size; //current buffer size int _grow_size; //vector buffer growth size int _len_before_gap; // text isdevided into two parts by the gap, this is the length of first part int _len_after_gap; // text isdevided into two parts by the gap, this is the length of second part int _gap_pos; // gapend position int _len_content; //_len_content = _len_before_gap + _len_after_gap int _len_gap; //_len_gap = _gap_pos - _len_before_gap};
- gap_vector - 符合编辑器习惯的快速容器类算法
- 符合国人习惯的多功能jsp编辑器(eclipse插件)
- 配置符合个人工作习惯的phpMyAdmin
- 建立符合搜索抓取习惯的网站
- CSS 符合习惯的代码规范
- stl 的find_if算法用来查找容器内的符合条件的元素
- 符合Web标准的在线编辑器:widgEditor [
- 自定义一个符合习惯的基于DOS的sqlplus命令行
- 关于magento的名字转化为符合国内的习惯
- 编写统一、符合习惯的CSS的原则
- 自定义一个符合习惯的基于DOS的sql…
- HIBERNATE - 符合Java习惯的关系数据库持久化
- HIBERNATE - 符合Java习惯的关系数据库持久化
- 符合Java习惯的关系数据库持久化
- HIBERNATE - 符合Java习惯的关系数据库持久化
- HIBERNATE - 符合Java习惯的关系数据库持久化
- 平板电脑的中文输入法可以更符合中国人习惯
- HIBERNATE - 符合Java习惯的关系数据库持久化
- ”Undefined reference to“ 的处理
- [Android 笔记] ADT v17+读取工程写 lib 下jar 包出现 java.lang.NoClassDefFoundError 问题
- get方式传递中文参数乱码解决方式
- POJ 1840 Eqs
- loadrunner检查点总结分析
- gap_vector - 符合编辑器习惯的快速容器类算法
- hint提示的相关介绍
- Def和__declspec(dllexport)
- java多线程的几种实现方法
- 各种文件之间的转换
- Android开发环境搭建
- POJ 1375 Intervals(解析几何)
- 黑马程序员_Java基础_IO流(对象序列化和字符编码)
- 《Unix & Linux 大学教程》 - 第十八章 学习笔记