| Dahua's profile笑对人生,傲立寰宇PhotosBlogLists | Help |
|
January 20 继往开来首先报告一个消息:ECCV'06 结果正式公布了。这次取得了很好的成绩: both of my two papers are accepted for oral presentation.
对于有兴趣想着程序架构方面继续提高的朋友,推荐两本经典的书:
Refactoring (重构):介绍如何对现有的设计进行改造。
STL (Standard Template Library):这是被无数人研究过的库,确实很经典。学习generic programming的都要研读这个library的。读懂这个了,就可以研究一下下面两个了。 C++ Boost Library (这个东西据说差点被编入C++ Standard了,现在在C++ community中也被视为准标准库。它里面由多个子库构成,很多设计做的非常精巧) Loki Library (就是Modern C++ Design的作者所写的库,提供了很多令人叹为观止的meta programming的C++实现。比如我在前面的blog中提到的编译期计算,就是这本书的入门级设计) January 11 今天,再度发生今天凌晨,回去躺了两小时,没睡着,于是回来实验室继续工作。这个现象是距离上一次发生只有4天。
我不知道我最近为什么对于手上的工作有如此大的热情,以至到了废寝忘食的地步了,呵呵。
和做research一样,programming是一种艺术。当你发现你为艺术献身,而不是被任务所驱使时,也许才能开始激发一点潜力。
顺便提到,昨天(就是凌晨前的若干小时),了解到了ECCV 2006的录取结果了(正式结果还没有officially公布,不过这个内部渠道相信是非常可靠了)
我在今年ECCV投的两篇第一作者文章,其中一篇确定是oral了,另外一篇也确定录取了,是oral还是poster尚需最后确定。这是一个可喜可贺的结果。于是,被实验室集体敲诈请客。
好了,今天继续讨论一个C++ Design的paradigm。今天这个比较有趣,就是让编译器计算最大公约数。对于一个程序来说,有两个阶段:编译期(Compile phase)和运行期(Run-time phase)。在编译期,由编译器对源代码进行编译,产生二进制代码。在运行期,由系统执行生成的二进制代码。 下面,先列出一个小程序:使用辗转相除法计算最大公约数(greatest common divisor)。
unsigned int gcd(unsigned int a, unsigned int b)
{ if (a == 0 || b == 0) return 0; //原理:
// 如果 b 整除 a, 那么 b 就是最大公约数;返回b // 否则 a := b; b := r (余数) // 这个过程迭代到整除为止。 for(unsigned int r = a % b; r != 0; a = b, b = r); return b;
} 这就是经典的在运行期求最大公约数的过程。 上面的过程可以简化为递归的形式,思路就更为清晰了:
unsigned int gcd(unsigned int a, unsigned int b)
{ if (a == 0 || b == 0) return 0; unsigned int r = a % b;
return r == 0 ? b : gcd(b, r); } 这还是在运行期进行计算的。下面设计一个有趣的程序,在编译器,编译器就能把gcd算出来。
template<int a, int b> struct gcd_solver
{ enum{ gcd = (a % b == 0) ? b : gcd_solver<b, (a % b)>::gcd }; } #define GCD(a, b) gcd_solver<12, 8>::gcd
这样编译器就会自动把 GCD(12, 8) 替换为 它们的最大公约数 4,etc。这个最大公约数,不是在运行期通过执行程序算出来的,而是编译器在编译时就算好的!
编译器的这种计算,利用了两个重要的特点:
1)编译器进行常数的基本运算; 2) C++编译器的模板(template)生成机制。 编译器遇到gcd_solver<12, 8>的时候,会自动仅进行基本常量计算,产生出
enum{ gcd = gcd_solver<8, 4>::gcd }; 接下来会产生gcd_solver<8, 4>这个类,这时候就会产生 enum{ gcd = 4 }; 然后编译器会把这个回代。 实质上,编译器通过它的模板形成机制,自动做了递归。
January 07 凌晨冲回实验室今天开始参与项目了,凌晨4点回去睡觉。可是睡在床上满脑子的program design。越想越兴奋,干脆爬起来,冒着寒风,一路小跑冲回实验室去了。人,最可贵的就是这点责任和热情。
讨论几个C++ design的问题,和大家分享一下: (1)关于对象的拷贝和引用计数(reference counting) 考虑一个图像类,就叫CImage吧。要实现下面的图像处理函数: CImage myProcess(const CImage& imgsrc)
{ ... some processing codes ... return imgdst; //imgdst is the resultant image of class CImage
} 如果调用语句是
CImage img2 = myProcess(img1);
那么在返回过程中,会经历两次拷贝构造的过程: 首先,程序会从函数体的局部变量imgdst,拷贝构造一个临时的CImage变量,记为temp_img吧; 然后,它会从temp_img拷贝构造img2。 如果按照通常教科书的方式,我们会给CImage写一个深拷贝(deep copy), 大概类似下面的代码: CImage(const CImage& imgsrc) { m_width = imgsrc.m_width; m_height = imgsrc.m_height; m_pdata = new pixel[width * height]; memcpy(m_pdata, imgsrc.m_pdata, sizeof(pixel) * width * height); } 上面的代码只是体现了大概的思路,当然,正式写程序时还有一些细节要考虑。这里我们仅讨论效率问题。对于一个大图像来说,深拷贝需要耗费大量的时间和内存资源,是非常低效的。 现在的C++设计上,更为推崇reference counting的设计方式。就是多个object share一个实体拷贝。通过一定的方式,记录对实体拷贝引用的数目。当引用这个拷贝的数目降为0的时候,销毁实体。下面的代码大致体现了这个基本思路: class ImageBody { protected: int m_width; int m_height; byte* m_pdata; public:
... ImageBody(const ImageBody& imgsrc) \\ a deep copy
...
} template<typename Ty>
class SmartPtr { public: typedef Ty element_type; public:
SmartPtr(element_type* pnew) : m_ptr(0), m_pnum(0) { if (pnew != 0) { m_ptr = pnew; m_pnum = new int(1); } } SmartPtr(SmartPtr& psrc)
{ m_ptr = psrc.m_ptr; //share the same copy m_pnum = psrc.m_pnum; ++(*m_pnum); //add reference counting
} SmartPtr& operator = (SmartPtr& psrc)
{ if (this.m_ptr == psrc.m_ptr) // already pointing to the same body return *this; Detach(); //detach from current body
m_ptr = psrc.m_ptr; //share the same copy with source object
m_pnum = psrc.m_pnum; ++(*m_pnum); //add reference counting
} virtual ~SmartPtr()
{ Detach(); } void Detach() // detach from current body
{ if (m_ptr) // not a null object { if (--(*m_pnum) == 0) // release a unreferred body { delete m_pnum; delete m_ptr; } m_ptr = 0; m_pnum = 0; } } protected:
element_type* m_ptr; unsigned int* m_pnum; }
class CImage : public SmartPtr<ImageBody>
{ ... } 这里CImage继承SmartPtr指针,而不是包含它作为member,其考虑在于更好地实现继承性。事实上,使用这样的方式,将使得建立CImage的继承类更为方便。 (2)关于内存安全 假设有一个Array类,我们需要访问其基地址。这个时候,如果直接返回基地址,会有一些问题的。
class Array
{ public: ... element* PtrBase()
{ return m_pdata; } void Resize(n)
{ ... some code release m_pdata ... ... some code allocate new memory with n elements ... } private: ... element* m_pdata; } 假如调用函数写成:
Array arr = new Array(10);
...
element* p1 = arr.PtrBase();
arr.Resize(20); // the base address may be changed
p1[0] = something; // dangerous action! It may write a memory area used by others
当然,也许我们可以去掉某个成员函数来保证安全,但是这会以严重损害类的功能为代价。这里我们可以通过对内存区域进行固化的方式来提供安全机制。
class SafeArray
{ public: ... element* Fix()
{ ++ m_num_users; return m_pdata; } void Unfix()
{ -- m_num_users; } void Resize(n)
{ if (m_num_users > 0) throw Attempt_to_modify_fixed_memory(); ... some code release m_pdata ...
... some code allocate new memory with n elements ... } private:
... element* m_pdata; int m_num_users; } 这里,使用对user计数的方法,允许多个user同时存在和不同时释放使用权。如果只是使用一个bool变量记录是否fixed,则无法实现multi-users了。 内存的安全使用,除了涉及fix问题之外,还有涉及多线程共享的安全。这个可以通过lock和unlock实现,而且往往在系统API就会提供相应的支持。比如在win32,就提供了诸如HeapLock和HeapUnlock的方法,可以通过适当途径封装到内存类里面。
程序设计还有很多问题,以后有时间再写吧。 January 01 盘点2005在辞旧迎新的时刻,各大媒体都热闹纷呈地盘点着过去一年所发生的大事。我也利用这个机会盘点一下我在2005年所经历的人生。 不同于2004年的风雨跌宕,我在2005年的感情生活,平淡中透出几分温馨。和朋友们相处和谐而愉快。 在2005年,第一次拨打国际长途电话,这源于某人宣称要脱离msn。从此一发不可收拾,现在已经成为精神生活中不可缺少的部分了。这些电话见证了我这一年生活之旅的点点滴滴。 在2005年,认识了一批新的朋友。主要是新来的同学:小呆,欢子,振国,世峰和李云。感谢他们,他们的到来给我的生活增添了无数的欢乐。还有一位朋友:晓燕,和她的认识真是蛮有传奇色彩的。在她的引领下,我第一次踏入了传说中的intel。 在2005年,我第一次以校友,而不是在校学生的身份回访我的母校——中国科学技术大学。我看到了我当年的实验室,老师和同学,还有三教的课室,芳花园的草木,一切都让我觉得无比亲切。 在2005年,我第一次做公开的demo,对象是香港的中学生。Raymond Yeung的这个任务,使我的生活从此现在demo的漩涡之中,到现在还不能脱身,sigh。后来,我还到会展中心面向全港做demo,我的玉照因此出现在多家媒体。 在2005年,我的第一篇正式paper发表。它发表在CVPR 2005——计算机视觉领域的一个顶尖国际会议。就在同一年,我写出并成功发表了我的第一篇第一作者的paper。就这一年里,一共成功发表了9篇paper,另外投出了9篇paper,在review阶段。 在2005年,我第一次成为独立reviewer。以前曾经替老板review文章,这一年得到汤老师推荐,成为IEEE Transaction on PAMI的正式reviewer。全年先后为CVPR 2005, ICCV 2005, ACCV 2006, ECCV 2006 和 PAMI 审稿。 在2005年,我有生以来第一次真正意义踏出国门。我到了传说中的美国加州为CVPR的paper做oral presentation。这也是我第一次面对来自全世界的优秀学者做演讲,介绍我们的工作。以前,我坐过无数次飞机,可是十几个小时的航程还是第一次经历了,呵呵。 在2005年,我来香港后第一次走进电影院。这一年,我在电影院看了三部电影:《七剑》,《无极》,《如果•爱》。都是一个人看的,都在九龙塘。 在2005年,我买了ipod photo,IBM Thinkpad X41, 换了新的眼镜。 现在, 我满怀感激地告别2005年,感谢它给我的创造的精彩生活; 我满怀期待地迎接2006年,期待着在新的一年创造新的惊喜。 |
|
|