当前位置: 首页 > news >正文

重庆忠县网站建设公司黑马培训是正规学校吗

重庆忠县网站建设公司,黑马培训是正规学校吗,北京建设执业网站,遵义网站建设gzyhg#x1f387;C学习历程#xff1a;入门 博客主页#xff1a;一起去看日落吗持续分享博主的C学习历程博主的能力有限#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话#xff1a; 也许你现在做的事情#xff0c;暂时看不到成果#xff0c;但不要忘记… C学习历程入门 博客主页一起去看日落吗持续分享博主的C学习历程博主的能力有限出现错误希望大家不吝赐教分享给大家一句我很喜欢的话 也许你现在做的事情暂时看不到成果但不要忘记树成长之前也要扎根也要在漫长的时光中沉淀养分。静下来想一想哪有这么多的天赋异禀那些让你羡慕的优秀的人也都曾默默地翻山越岭。 目录1. 性能瓶颈分析2. 针对性能瓶颈使用基数树进行优化3. 使用基数树进行优化代码实现1. 性能瓶颈分析 经过前面的测试可以看到我们的代码此时与malloc之间还是有差距的此时我们就应该分析分析我们当前项目的瓶颈在哪里但这不能简单的凭感觉我们应该用性能分析的工具来进行分析。 VS编译器下性能分析的操作步骤 VS编译器中就带有性能分析的工具的我们可以依次点击“调试→性能和诊断”进行性能分析注意该操作要在Debug模式下进行。 同时我们将代码中n的值由10000调成了1000否则该分析过程可能会花费较多时间并且将malloc的测试代码进行了屏蔽因为我们要分析的是我们实现的高并发内存池。 在点击了“调试→性能和诊断”后会弹出一个提示框我们直接点击“开始”进行了。 然后会弹出一个选项框这里我们选择的是第二个因为我们要分析的是各个函数的用时时间然后点击下一步。 出现以下选项框继续点击下一步。 最后点击完成就可以等待分析结果了。 分析性能瓶颈 通过分析结果可以看到光是Deallocate和MapObjectToSpan这两个函数就占用了一半多的时间。 而在Deallocate函数中调用ListTooLong函数时消耗的时间是最多的。 在ListTooLong函数中调用ReleaseListToSpans函数时消耗的时间是最多的。 在ReleaseListToSpans函数中调用MapObjectToSpan函数时消耗的时间是最多的。 也就是说最终消耗时间最多的实际就是MapObjectToSpan函数我们这时再来看看为什么调用MapObjectToSpan函数会消耗这么多时间。通过观察我们最终发现调用该函数时会消耗这么多时间就是因为锁的原因。 因此当前项目的瓶颈点就在锁竞争上面需要解决调用MapObjectToSpan函数访问映射关系时的加锁问题。tcmalloc当中针对这一点使用了基数树进行优化使得在读取这个映射关系时可以做到不加锁。 2. 针对性能瓶颈使用基数树进行优化 基数树实际上就是一个分层的哈希表根据所分层数不同可分为单层基数树、二层基数树、三层基数树等。 单层基数树 单层基数树实际采用的就是直接定址法每一个页号对应span的地址就存储数组中在以该页号为下标的位置。 最坏的情况下我们需要建立所有页号与其span之间的映射关系因此这个数组中元素个数应该与页号的数目相同数组中每个位置存储的就是对应span的指针。 //单层基数树 template int BITS class TCMalloc_PageMap1 { public:typedef uintptr_t Number;explicit TCMalloc_PageMap1(){size_t size sizeof(void*) BITS; //需要开辟数组的大小size_t alignSize SizeClass::_RoundUp(size, 1 PAGE_SHIFT); //按页对齐后的大小array_ (void**)SystemAlloc(alignSize PAGE_SHIFT); //向堆申请空间memset(array_, 0, size); //对申请到的内存进行清理}void* get(Number k) const{if ((k BITS) 0) //k的范围不在[0, 2^BITS-1]{return NULL;}return array_[k]; //返回该页号对应的span}void set(Number k, void* v){assert((k BITS) 0); //k的范围必须在[0, 2^BITS-1]array_[k] v; //建立映射} private:void** array_; //存储映射关系的数组static const int LENGTH 1 BITS; //页的数目 };此时当我们需要建立映射时就调用set函数需要读取映射关系时就调用get函数就行了。 代码中的非类型模板参数BITS表示存储页号最多需要比特位的个数。在32位下我们传入的是32-PAGE_SHIFT在64位下传入的是64-PAGE_SHIFT。而其中的LENGTH成员代表的就是页号的数目即 22BITS 比如32位平台下以一页大小为8K为例此时页的数目就是232 / 213 2 19 , 因此存储页号最多需要19个比特位此时传入非类型模板参数的值就是 32 − 13 19。由于32位平台下指针的大小是4字节因此该数组的大小就是219 x 4 2M ,内存消耗不大是可行的。但如果是在64位平台下此时该数组的大小是251 x 8 224G,这显然是不可行的实际上对于64位的平台我们需要使用三层基数树。 二层基数树 这里还是以32位平台下一页的大小为8K为例来说明此时存储页号最多需要19个比特位。而二层基数树实际上就是把这19个比特位分为两次进行映射。 比如用前5个比特位在基数树的第一层进行映射映射后得到对应的第二层然后用剩下的比特位在基数树的第二层进行映射映射后最终得到该页号对应的span指针。 在二层基数树中第一层的数组占用25 x 27 Byte 空间第二层的数组最多占用25 x 214 x 4 221 2M。二层基数树相比一层基数树的好处就是一层基数树必须一开始就把 2M 的数组开辟出来而二层基数树一开始时只需将第一层的数组开辟出来当需要进行某一页号映射时再开辟对应的第二层的数组就行了。 //二层基数树 template int BITS class TCMalloc_PageMap2 { private:static const int ROOT_BITS 5; //第一层对应页号的前5个比特位static const int ROOT_LENGTH 1 ROOT_BITS; //第一层存储元素的个数static const int LEAF_BITS BITS - ROOT_BITS; //第二层对应页号的其余比特位static const int LEAF_LENGTH 1 LEAF_BITS; //第二层存储元素的个数//第一层数组中存储的元素类型struct Leaf{void* values[LEAF_LENGTH];};Leaf* root_[ROOT_LENGTH]; //第一层数组 public:typedef uintptr_t Number;explicit TCMalloc_PageMap2(){memset(root_, 0, sizeof(root_)); //将第一层的空间进行清理PreallocateMoreMemory(); //直接将第二层全部开辟}void* get(Number k) const{const Number i1 k LEAF_BITS; //第一层对应的下标const Number i2 k (LEAF_LENGTH - 1); //第二层对应的下标if ((k BITS) 0 || root_[i1] NULL) //页号值不在范围或没有建立过映射{return NULL;}return root_[i1]-values[i2]; //返回该页号对应span的指针}void set(Number k, void* v){const Number i1 k LEAF_BITS; //第一层对应的下标const Number i2 k (LEAF_LENGTH - 1); //第二层对应的下标assert(i1 ROOT_LENGTH);root_[i1]-values[i2] v; //建立该页号与对应span的映射}//确保映射[start,start_n-1]页号的空间是开辟好了的bool Ensure(Number start, size_t n){for (Number key start; key start n - 1;){const Number i1 key LEAF_BITS;if (i1 ROOT_LENGTH) //页号超出范围return false;if (root_[i1] NULL) //第一层i1下标指向的空间未开辟{//开辟对应空间static ObjectPoolLeaf leafPool;Leaf* leaf (Leaf*)leafPool.New();memset(leaf, 0, sizeof(*leaf));root_[i1] leaf;}key ((key LEAF_BITS) 1) LEAF_BITS; //继续后续检查}return true;}void PreallocateMoreMemory(){Ensure(0, 1 BITS); //将第二层的空间全部开辟好} };因此在二层基数树中有一个Ensure函数当需要建立某一页号与其span之间的映射关系时需要先调用该Ensure函数确保用于映射该页号的空间是开辟了的如果没有开辟则会立即开辟。 而在32位平台下就算将二层基数树第二层的数组全部开辟出来也就消耗了 2 M 2M 2M的空间内存消耗也不算太多因此我们可以在构造二层基数树时就把第二层的数组全部开辟出来。 三层基数树 上面一层基数树和二层基数树都适用于32位平台而对于64位的平台就需要用三层基数树了。三层基数树与二层基数树类似三层基数树实际上就是把存储页号的若干比特位分为三次进行映射。 此时只有当要建立某一页号的映射关系时再开辟对应的数组空间而没有建立映射的页号就可以不用开辟其对应的数组空间此时就能在一定程度上节省内存空间。 //三层基数树 template int BITS class TCMalloc_PageMap3 { private:static const int INTERIOR_BITS (BITS 2) / 3; //第一、二层对应页号的比特位个数static const int INTERIOR_LENGTH 1 INTERIOR_BITS; //第一、二层存储元素的个数static const int LEAF_BITS BITS - 2 * INTERIOR_BITS; //第三层对应页号的比特位个数static const int LEAF_LENGTH 1 LEAF_BITS; //第三层存储元素的个数struct Node{Node* ptrs[INTERIOR_LENGTH];};struct Leaf{void* values[LEAF_LENGTH];};Node* NewNode(){static ObjectPoolNode nodePool;Node* result nodePool.New();if (result ! NULL){memset(result, 0, sizeof(*result));}return result;}Node* root_; public:typedef uintptr_t Number;explicit TCMalloc_PageMap3(){root_ NewNode();}void* get(Number k) const{const Number i1 k (LEAF_BITS INTERIOR_BITS); //第一层对应的下标const Number i2 (k LEAF_BITS) (INTERIOR_LENGTH - 1); //第二层对应的下标const Number i3 k (LEAF_LENGTH - 1); //第三层对应的下标//页号超出范围或映射该页号的空间未开辟if ((k BITS) 0 || root_-ptrs[i1] NULL || root_-ptrs[i1]-ptrs[i2] NULL){return NULL;}return reinterpret_castLeaf*(root_-ptrs[i1]-ptrs[i2])-values[i3]; //返回该页号对应span的指针}void set(Number k, void* v){assert(k BITS 0);const Number i1 k (LEAF_BITS INTERIOR_BITS); //第一层对应的下标const Number i2 (k LEAF_BITS) (INTERIOR_LENGTH - 1); //第二层对应的下标const Number i3 k (LEAF_LENGTH - 1); //第三层对应的下标Ensure(k, 1); //确保映射第k页页号的空间是开辟好了的reinterpret_castLeaf*(root_-ptrs[i1]-ptrs[i2])-values[i3] v; //建立该页号与对应span的映射}//确保映射[start,startn-1]页号的空间是开辟好了的bool Ensure(Number start, size_t n){for (Number key start; key start n - 1;){const Number i1 key (LEAF_BITS INTERIOR_BITS); //第一层对应的下标const Number i2 (key LEAF_BITS) (INTERIOR_LENGTH - 1); //第二层对应的下标if (i1 INTERIOR_LENGTH || i2 INTERIOR_LENGTH) //下标值超出范围return false;if (root_-ptrs[i1] NULL) //第一层i1下标指向的空间未开辟{//开辟对应空间Node* n NewNode();if (n NULL) return false;root_-ptrs[i1] n;}if (root_-ptrs[i1]-ptrs[i2] NULL) //第二层i2下标指向的空间未开辟{//开辟对应空间static ObjectPoolLeaf leafPool;Leaf* leaf leafPool.New();if (leaf NULL) return false;memset(leaf, 0, sizeof(*leaf));root_-ptrs[i1]-ptrs[i2] reinterpret_castNode*(leaf);}key ((key LEAF_BITS) 1) LEAF_BITS; //继续后续检查}return true;}void PreallocateMoreMemory(){} };因此当我们要建立某一页号的映射关系时需要先确保存储该页映射的数组空间是开辟好了的也就是调用代码中的Ensure函数如果对应数组空间未开辟则会立马开辟对应的空间。 3. 使用基数树进行优化代码实现 代码更改 现在我们用基数树对代码进行优化此时将PageCache类当中的unorder_map用基数树进行替换即可由于当前是32位平台因此这里随便用几层基数树都可以。 //单例模式 class PageCache { public://... private://std::unordered_mapPAGE_ID, Span* _idSpanMap;TCMalloc_PageMap132 - PAGE_SHIFT _idSpanMap; };此时当我们需要建立页号与span的映射时就调用基数树当中的set函数。 _idSpanMap.set(span-_pageId, span);而当我们需要读取某一页号对应的span时就调用基数树当中的get函数。 Span* ret (Span*)_idSpanMap.get(id);并且现在PageCache类向外提供的用于读取映射关系的MapObjectToSpan函数内部就不需要加锁了。 //获取从对象到span的映射 Span* PageCache::MapObjectToSpan(void* obj) {PAGE_ID id (PAGE_ID)obj PAGE_SHIFT; //页号Span* ret (Span*)_idSpanMap.get(id);assert(ret ! nullptr);return ret; }为什么读取基数树映射关系时不需要加锁 当某个线程在读取映射关系时可能另外一个线程正在建立其他页号的映射关系而此时无论我们用的是C当中的map还是unordered_map在读取映射关系时都是需要加锁的。 因为C中map的底层数据结构是红黑树unordered_map的底层数据结构是哈希表而无论是红黑树还是哈希表当我们在插入数据时其底层的结构都有可能会发生变化。比如红黑树在插入数据时可能会引起树的旋转而哈希表在插入数据时可能会引起哈希表扩容。此时要避免出现数据不一致的问题就不能让插入操作和读取操作同时进行因此我们在读取映射关系的时候是需要加锁的。 而对于基数树来说就不一样了基数树的空间一旦开辟好了就不会发生变化因此无论什么时候去读取某个页的映射都是对应在一个固定的位置进行读取的。并且我们不会同时对同一个页进行读取映射和建立映射的操作因为我们只有在释放对象时才需要读取映射而建立映射的操作都是在page cache进行的。也就是说读取映射时读取的都是对应span的_useCount不等于0的页而建立映射时建立的都是对应span的_useCount等于0的页所以说我们不会同时对同一个页进行读取映射和建立映射的操作。 再次对比malloc进行测试 还是同样的代码只不过我们用基数树对代码进行了优化这时测试固定大小内存的申请和释放的结果如下 可以看到这时就算申请释放的是固定大小的对象其效率都是malloc的两倍。下面在申请释放不同大小的对象时由于central cache的桶锁起作用了其效率更是变成了malloc的好几倍。
http://www.hkea.cn/news/14372384/

相关文章:

  • 无锡 网站建设公司制作网页第一件事就是选定一种
  • 青岛网站建设康之迅中国建设银行信用卡黑名单网站
  • 江阴网站建设多少钱论述网站建设的主要内容
  • 企业建设营销网站的目的商城小程序多少钱
  • 阿里云服务器上如何做网站沈阳哪家网站做的好
  • 虹口高端网站建设苏州高端网站建设企业
  • 网站方案模板seo搜索引擎优化到底是什么
  • 凡科做的网站可以在百度搜到吗网站中的文章可以做排名吗
  • 企业门户网站的安全性石家庄企业建站
  • 自己建设网站平台步骤推广软文范文
  • 石家庄做外贸的网站建设简历制作免费模板网站
  • 深圳中小企业网站制作wordpress健身预定主题
  • 怎么看一个网站是不是外包做的基于 wordpress
  • 同服务器网站查询工具常州市城市建设局网站
  • 网站开发与制作工资分类信息发布 wordpress
  • 安徽省校园网站建设icp ip 网站备案查询
  • 黑龙江做网站的公司哪个网站可以做编程题
  • 门户手机网站模板写作网站可以签约未成年吗
  • 手机网站推荐一个互联网设计公司网站
  • 网站关键词做多了是不是影响权重重庆食品公司
  • 枣庄建设局网站上海装修公司排名前30
  • 网站开发安全有哪些做特卖的网站有哪些
  • 兰山网站建设2017网站建设前景
  • 电影网站建设视频教程购物网站建设规划书
  • 怎么上传网站模板电子商务seo优化
  • 建站企业网站惠阳惠州网站建设
  • 做网站排在前十名要多少钱推广产品的渠道有哪些
  • 网站和数字界面设计师wordpress模板位置
  • dede网站mip成都微信小程序分类信息开发
  • asp.net网站制作实例开发新客户的十大渠道