提高C++性能的编程技术笔记:引用计数+测试代码
引用计数(reference counting):基本思想是将销毁对象的职责从客户端代码转移到对象本身。对象跟踪记录自身当前被引用的数目,在引用计数达到零时自行销毁。换句话说,对象不再被使用时自行销毁。
引用计数和执行速度之间的关系是与上下文紧密关联的。该关系取决于以下几个因素:
(1). 目标对象的资源消耗量集中于哪些方面?如果目标对象使用过多内存,比如未保护内存,将使可用内存受限,并导致显著的性能损失,表现为缓存命不中和页面错误。
(2). 分配(释放)目标对象所使用资源的代价有多高?即便从堆中分配1字节空间也需要执行数百条指令。释放时亦然。
(3). 可能有多少对象共享目标对象的单个实例?通过使用赋值操作符和拷贝构造函数可以提升共享率。
(4). 创建(销毁)第一个(最后一个)目标对象引用的额度有多高?使用构造函数而不是拷贝构造函数创建新的引用计数对象时,会产生对目标对象的初次引用。这种操作代价高昂。与创建一个新的目标对象相比,这种方法包含更多开销。移除最后一次引用时也是如此。
对于预先存在且无法修改的类,引用计数依然可以实现,只不过,这种情况需要对设计进行一些修改。也可以在多线程执行环境下处理引用计数。在这种情况下,多个线程可能会并发访问同一个引用计数对象。因此必须对维持引用计数的变量进行保护,使其对进行的更新是原子操作。原子更新操作要求有一个锁定机制。
引用计数在性能上并非无往不胜。引用计数、执行时间和资源维护会产生微妙的相互作用,如果对性能方面的考虑很重要,就必须对这几个方面仔细进行评估。引用计数是提升还是损害性能取决于其使用方式。下面的任意一种情况都可以使引用计数变得更为有效:目标对象是很大的资源消费者;资源分配和释放的代价很高;高度共享,由于使用赋值操作符和拷贝构造函数,引用计数的性能可能会很高;创建和销毁引用的代价低廉。反之,则应跳出引用计数而转为使用更加有效的简单非计数对象。
以下是测试代码(reference_counting.cpp):
#include "reference_counting.hpp"
#include <iostream>
#include <chrono>
#include <string>
#include <string.h>namespace reference_counting_ {// reference: 《提高C++性能的编程技术》:第十二章:引用计数
namespace {// 为使引用计数更加方便,我们需要为每个BigInt对象关联一个引用计数。
// 实现这一操作的方式有两种,为BigInt直接添加refCount成员或使其继承自基类RCObject
// RCObject是引用计数对象的基类,并且封装了所有引用计数变量以及针对这些变量的操作
class RCObject {
public:void addReference() { ++refCount; }void removeReference() { if (--refCount == 0) delete this; }void markUnshareable() { shareable = false; }bool isShareable() const { return shareable; }bool isShared() const { return refCount > 1; }protected:RCObject() : refCount(0), shareable(true) {}RCObject(const RCObject& rhs) : refCount(0), shareable(true) {}RCObject& operator=(const RCObject& rhs) { return *this; }virtual ~RCObject() {}private:int refCount;bool shareable;
};// BigInt类的功能是将正整数表示成用二进制编码的十进制形式
class BigInt : public RCObject {
friend BigInt operator+ (const BigInt&, const BigInt&);public:BigInt(const char*);BigInt(unsigned = 0);BigInt(const BigInt&);BigInt& operator= (const BigInt&);BigInt& operator+= (const BigInt&);~BigInt();char* getDigits() const { return digits; }unsigned getNdigits() const { return ndigits; }void print() { fprintf(stdout, "digits: %s\n", digits); }private:char* digits;unsigned ndigits;unsigned size; // 分配的字符串的大小BigInt(const BigInt&, const BigInt&);char fetch(unsigned i) const;
};BigInt::BigInt(unsigned u)
{unsigned v = u;for (ndigits = 1; (v/=10) > 0; ++ndigits) {;}digits = new char[size=ndigits];for (unsigned i = 0; i < ndigits; ++i) {digits[i] = u%10;u /= 10;}
}BigInt::~BigInt()
{delete [] digits;
}BigInt& BigInt::operator=(const BigInt& rhs)
{if (this == &rhs) return *this;ndigits = rhs.ndigits;if (ndigits > size) {delete [] digits;digits = new char[size = ndigits];}for (unsigned i = 0; i < ndigits; ++i) {digits[i] = rhs.digits[i];}return *this;
}BigInt::BigInt(const char* s)
{if (s[0] == '\0') {s = "0";}size = ndigits = strlen(s);digits = new char[size];for (unsigned i = 0; i < ndigits; ++i) {digits[i] = s[ndigits-1-i] - '0';}
}BigInt::BigInt(const BigInt& copyFrom)
{size = ndigits = copyFrom.ndigits;digits = new char[size];for (unsigned i = 0; i < ndigits; ++i) {digits[i] = copyFrom.digits[i];}
}// BigInt = left + right
BigInt::BigInt(const BigInt& left, const BigInt& right)
{size = 1 + (left.ndigits > right.ndigits ? left.ndigits : right.ndigits);digits = new char[size];ndigits = left.ndigits;for (unsigned i = 0; i < ndigits; ++i) {digits[i] = left.digits[i];}*this += right;
}inline char BigInt::fetch(unsigned i) const
{return i < ndigits ? digits[i] : 0;
}BigInt& BigInt::operator+=(const BigInt& rhs)
{unsigned max = 1 + (rhs.ndigits > ndigits ? rhs.ndigits : ndigits);if (size < max) {char* d = new char[size=max];for (unsigned i = 0; i < ndigits; ++i) {d[i] = digits[i];}delete [] digits;digits = d;}while (ndigits < max) {digits[ndigits++] = 0;}for (unsigned i = 0; i < ndigits; ++i) {digits[i] += rhs.fetch(i);if (digits[i] >= 10) {digits[i] -= 10;digits[i+1] = 1;}}if (digits[ndigits-1] == 0) {--ndigits;}return *this;
}std::ostream& operator<<(std::ostream& os, BigInt& bi)
{char c;const char* d = bi.getDigits();for (int i = bi.getNdigits() - 1; i >= 0; i--) {c = d[i] + '0';os << c;}os << std::endl;return os;
}inline BigInt operator+(const BigInt& left, const BigInt& right)
{return BigInt(left, right);
}// 智能指针:这种特殊指针的任务是对引用计数进行登记
template<class T>
class RCPtr {
public:RCPtr(T* realPtr = 0) : pointee(realPtr) { init(); }RCPtr(const RCPtr& rhs) : pointee(rhs.pointee) { init(); }~RCPtr() { if (pointee) pointee->removeReference(); }RCPtr& operator=(const RCPtr& rhs);T* operator->() const { return pointee; }T& operator*() const { return *pointee; }private:T* pointee;void init();
};template<class T>
void RCPtr<T>::init()
{if (0 == pointee) return;if (false == pointee->isShareable()) {pointee = new T(*pointee);}pointee->addReference();
}template<class T>
RCPtr<T>& RCPtr<T>::operator=(const RCPtr& rhs)
{if (pointee != rhs.pointee) {if (pointee) pointee->removeReference();pointee = rhs.pointee;init();}return *this;
}// RCBigInt:构造BigInt的引用计数实现,RCBigInt需要指向一个真正的BigInt对象
// 如果是频繁赋值和复制RCBigInt对象的工作,RCBigInt会大显身手。另外,与直接使用简单BigInt相比,
// 由于创建了对新BigInt对象的初次引用,使用新RCBigInt对象的代价要更高一些。每当RCBigInt产生了
// 对BigInt的初次引用时,就会在堆内存中创建一个BigInt对象并指向它。对于基于堆栈(局部变量)的简单
// BigInt对象而言,则不必付出这种代价。此类的情况在移除最后一次BigInt引用时也会发生。这是因为
// 底层的BigInt对象被释放并还给堆内存。
class RCBigInt {friend RCBigInt operator+(const RCBigInt&, const RCBigInt&);public:RCBigInt(const char* p) : value(new BigInt(p)) {}RCBigInt(unsigned u = 0) : value(new BigInt(u)) {}RCBigInt(const BigInt& bi) : value(new BigInt(bi)) {}void print() const { value->print(); }private:RCPtr<BigInt> value;
};inline RCBigInt operator+(const RCBigInt& left, const RCBigInt& right)
{return RCBigInt(*(left.value) + *(right.value));
}} // namespaceint test_reference_counting_1()
{std::chrono::high_resolution_clock::time_point time_start, time_end;const int count{10000000};{ // test BigInt createtime_start = std::chrono::high_resolution_clock::now();for (int i = 0; i < count; ++i) {BigInt a = i;BigInt b = i + 1;BigInt c = i + 2;BigInt d = i + 3;}time_end = std::chrono::high_resolution_clock::now(); fprintf(stdout, "BigInt create time spend: %f seconds\n",(std::chrono::duration_cast<std::chrono::duration<double>>(time_end-time_start)).count());
}{ // test RCBigInt create// RCBigInt测试会更多地忙于初次引用的创建及之后将其销毁的工作time_start = std::chrono::high_resolution_clock::now();for (int i = 0; i < count; ++i) {RCBigInt a = i;RCBigInt b = i + 1;RCBigInt c = i + 2;RCBigInt d = i + 3;}time_end = std::chrono::high_resolution_clock::now(); fprintf(stdout, "RCBigInt create time spend: %f seconds\n",(std::chrono::duration_cast<std::chrono::duration<double>>(time_end-time_start)).count());
}{ // test BigInt assignBigInt a, b, c;BigInt d = 1;time_start = std::chrono::high_resolution_clock::now();for (int i = 0; i < count; ++i) {a = b = c = d;}time_end = std::chrono::high_resolution_clock::now(); fprintf(stdout, "BigInt assign time spend: %f seconds\n",(std::chrono::duration_cast<std::chrono::duration<double>>(time_end-time_start)).count());
}{ // test RCBigInt assign// 对RCBigInt对象的赋值操作效率高于BigInt对象,但是创建和销毁对象时却相反RCBigInt a, b, c;RCBigInt d = 1;time_start = std::chrono::high_resolution_clock::now();for (int i = 0; i < count; ++i) {a = b = c = d;}time_end = std::chrono::high_resolution_clock::now(); fprintf(stdout, "RCBigInt assign time spend: %f seconds\n",(std::chrono::duration_cast<std::chrono::duration<double>>(time_end-time_start)).count());
}return 0;
}} // namespace reference_counting_
执行结果如下:
GitHub: https://github.com/fengbingchun/Messy_Test
相关文章:

如何提升 CSS 选择器的性能?
CSS选择器对性能的影响源于浏览器匹配选择器和文档元素时所消耗的时间,所以优化选择器的原则是应尽量避免使用消耗更多匹配时间的选择器。而在这之前我们需要了解CSS选择器匹配的机制, 如子选择器规则: #header > a {font-weight:blod;} 我…

百度AI攻坚战:PaddlePaddle中国突围
作者 | 阿司匹林出品 | AI科技大本营(ID:rgznai100)2013年,百度开始研发深度学习框架PaddlePaddle,搜索、凤巢CTR预估上线DNN模型。2016年,在百度世界大会上,百度宣布PaddlePaddle开源ÿ…

提高C++性能的编程技术笔记:编码优化+测试代码
缓存:在现代处理器中,缓存经常与处理器中的数据缓存和指令缓存联系在一起。缓存主要用来存储使用频繁而且代价高昂的计算结果,这样就可以避免对这些结果的重复计算。如,循环内对常量表达式求值是一种常见的低性能问题。 预先计算…

Swift 中使用 SQLite——打开数据库
关于Swift中使用SQLite,接下来可能会分别从打开、增、删、改、查,几个方面来介绍SQLite的具体使用,这一篇重点介绍一下如何打开。 定义全局数据库访问句柄 /// 全局数据库访问句柄 private var db: COpaquePointer nil实现打开数据库函数 …

MVC中获取模型属性的Range和StringLength验证特性设置
MVC中的客户端及服务端模型验证信息都以ModelMetadata类型作为承载,在获得属性的ModelMetadata之后(还不知道怎么获取ModelMetadata的童鞋请自行恶补),我们可以轻松得到一些我们在模型中定义的特性,比如显示名称、是否…

以安装PyTorch为例说明Anaconda在Windows/Linux上的使用
在Windows10上配置完MXNet 1.3.0后,再配置PyTorch 1.0时,发现两者需要依赖的NumPy版本不一致,之前是通过pip安装NumPy,根据pip的版本不同,会安装不同版本的NumPy,使用起来很不方便,而且MXNet和P…

常用 SQL介绍
创建表 /*创建数据表CREATE TABLE 表名 (字段名 类型(INTEGER, REAL, TEXT, BLOB)NOT NULL 不允许为空PRIMARY KEY 主键AUTOINCREMENT 自增长,字段名2 类型,...)注意:在开发中,如果是从 Navicat 粘贴的 SQL,需要自己添加一个指令IF NO…

AttoNets,一种新型的更快、更高效边缘计算神经网络
作者 | Alexander Wong, Zhong Qiu Lin, and Brendan Chwyl 译者 | Rachel 出品 | AI科技大本营(ID:rgznai100)尽管机器学习已经在很多复杂的任务中取得了进展,但现有模型仍然面临许多边缘计算实践的困难,这些边缘计算场景包括移…

Appro DM8127 IPNC 挂载NFS遇到的问题及解决
对于Appro DM8127 IPNC,默认的启动方式是NAND is used for booting kernel and NAND is used as root filesystem 为了调试应用程序方便,通常使用挂载NFS作为 root filesystem 但是如果直接采用ti文档中所给的方法修改文件系统挂载方式(将启动…

提高C++性能的编程技术笔记:设计优化/可扩展性/系统体系结构相关+测试代码
1. 设计优化 我们可以粗略地将性能优化分为两种类型:编码优化和设计优化。编码优化定义为不需要完整理解要解决的问题或者应用程序的执行流程就能实施的优化。通过定义看出,编码优化用于局部代码,同时该过程不牵涉周围的代码。除了这些容易实…

ICLR 2020被爆半数审稿人无相关领域经验,同行评审制度在垮塌?
作者 | 若名出品 | AI科技大本营(ID:rgznai100)根据维基百科,同行评议(peer review),是指由一个或多个具有与作品生产者具有相似能力的人员(同行)对作品进行的评估活动。同行评审方法用于维持质…

Swift 中使用 SQLite——批量更新(事务处理)
本文是Swift 中使用 SQLite系列的收官之作,介绍一下在数据库中的批量更新。 事务 在准备做大规模数据操作前,首先开启一个事务,保存操作前的数据库的状态开始数据操作如果数据操作成功,提交事务,让数据库更新到数据操…

网络管理常用命令之二 - Ipconfig 命令详解(图文)
2、Ipconfig 命令...不带参数.../all 参数.../release 和 /realease6 参数.../Renew 和 /Renew6 参数.../flushdns 参数.../displaydns 参数2、Ipconfig 命令 ipconfig命令也是使用率非常高的一个命令,可用于显示系统的TCP/IP网络配置值,并刷新动态主…

Swift 中使用 SQLite——查询数据
本文主要介绍如何查询 SQLite 结果集,以及封装 SQLite 的操作方法。 准备测试代码 /// 从数据库中加载 person 数组 class func persons() -> [Person]? {// 1. 准备 SQLlet sql "SELECT id, name, age, height FROM T_Person;"// 2. 访问数据库// …

提高C++性能的编程技术笔记:总结
《提高C性能的编程技术》这本书是2011年出版的,书中有些内容的介绍可能已经过时,已不再适用于现在的C编程中,但大部分内容还是很有参考意义的。 这里是基于之前所有笔记的简单总结,笔记列表如下: 跟踪实例࿱…

13岁小孩都跟我抢Python了,完了!
以下来自一位程序员母亲和工作人员的对话。程序员妈妈:您好,可以帮我推荐一本适合我家小孩看的编程书籍吗?兔子:可以的呀,《Scratch从入门到精通》,这本书适合小孩学习,您可以先看一下哦~程序员…

Windows Mobile 6.0 SDK和中文模拟器下载
【转】 Windows Mobile 6.0 SDK和中文模拟器下载 Windows Mobile 6.5 模拟器2010年12月06日 星期一 07:48转载自 zhangyanle86终于编辑 zhangyanle86Windows Mobile 6.0 SDK和中文模拟器下载 SDK 6.0下载页面:http://www.microsoft.com/downloads/details.aspx?fam…

wxPython:Python首选的GUI库 | CSDN博文精选
作者 | 天元浪子来源 | CSDN博客文章目录概述窗口程序的基本框架事件和事件驱动菜单栏/工具栏/状态栏动态布局AUI布局DC绘图定时器和线程后记概述跨平台的GUI工具库,较为有名的当属GTK、Qt 和 wxWidgets 了。GTK是C实现的,由于C语言本身不支持OOP&#x…

Swift 中使用 SQLite——修改和删除数据
本文主要介绍在SQLite中修改数据、删除数据: 更新记录 /// 将当前对象信息更新到数据库 /// /// - returns: 是否成功 func updatePerson() -> Bool {guard let name name else {print("姓名不能为空")return false}if id < 0 {print("id 不…

用python3实现指定目录下文件sha256及文件大小统计
有时会统计某个目录下有哪些文件,每个文件的sha256及文件大小等相关信息,这里用python3写了个脚本用来实现此功能,此脚本可跨平台,同时支持windows和linux,脚本(get_dir_file_info.py)内容如下: import os…

Swift 中使用 SQLite——新增数据
本文重点介绍两个方面,1、新增数据,2、获取自动增长 ID。 建立 Person.swift 数据模型 /// 个人模型 class Person: NSObject {// MARK: - 模型属性/// 代号var id: Int64 0/// 姓名var name: String?/// 年龄var age 0/// 身高var height: Double …

投稿2877篇,EMNLP 2019公布4篇最佳论文
整理 | AI科技大本营(ID:rgznai100)近日,自然语言处理领域的顶级会议之一EMNLP 2019公布了年度最佳论文。EMNLP是由国际语言学会(ACL)下属的SIGDAT小组主办的自然语言处理领域的顶级国际会议,是自然语言算法…

对象检测工具包mmdetection简介、安装及测试代码
mmdetection是商汤和港中文大学联合开源的基于PyTorch的对象检测工具包,属于香港中文大学多媒体实验室open-mmlab项目的一部分。该工具包提供了已公开发表的多种流行的检测组件,通过这些组件的组合可以迅速搭建出各种检测框架。 mmdetection主要特性&am…

(转)eclipse 代码自动补全
转自:http://blog.csdn.net/yushuwai2010/article/details/11856129 一般默认情况下,Eclipse的代码提示功能是比MicrosoftVisualStudio的差很多的,主要是Eclipse本身有很多选项是默认关闭的,要开发者自己去手动配置。如果开发者不…

swift 多线程GCD和延时调用
GCD 是一种非常方便的使用多线程的方式。通过使用 GCD,我们可以在确保尽量简单的语法的前提下进行灵活的多线程编程。在 “复杂必死” 的多线程编程中,保持简单就是避免错误的金科玉律。好消息是在 Swift 中是可以无缝使用 GCD 的 API 的,而且…

目标检测算法Faster R-CNN简介
在博文https://blog.csdn.net/fengbingchun/article/details/87091740 中对Fast R-CNN进行了简单介绍,这里在Fast R-CNN的基础上简单介绍下Faster R-CNN。 目标检测领域从R-CNN开始,通过引入卷积神经网络取得了很多突破性的进展,但是始终未能…

ICCV 2019 | 加一个任务路由让数百个任务同时跑起来,怎么做到?
作者 | Gjorgji Strezoski, Nanne van Noord, Marcel Worring 译者 | 中国海洋大学李杰 出品 | AI科技大本营(ID:rgznai100)摘要传统的多任务(MTL)学习方法依赖于架构调整和大型可训练参数集来联合优化多个任务。但是,…

DEV开发之控件NavBarControl
右键点击RunDesigner弹出如下界面鼠标先点击3或4,1,,然后点击1或2进行相应的新增或删除操作,3是分组,4是项目,4可以直接拖动到相应的分组3.属性caption:显示的名称4.NavBarControl 属性 PaintStyleName绘画风格&…

swift支持多线程操作数据库类库-CoreDataManager
类库方法 获取数据 executeFetchRequest(request:) 同步获取数据 var request: NSFetchRequest NSFetchRequest(entityName: "MonkeyEntity")var myMonkeys:NSArray? CoreDataManager.shared.executeFetchRequest(request)异步获取数据 executeFetchRequest(re…

目标检测(或分隔)算法Mask R-CNN简介
在博文https://blog.csdn.net/fengbingchun/article/details/87195597 中对Faster R-CNN进行了简单介绍,这里在Faster R-CNN的基础上简单介绍下Mask R-CNN。 Mask R-CNN是faster R-CNN的扩展形式,能够有效地检测图像中的目标,并且Mask R-CNN…