C++矩阵优化算法
寫在前面的話:此矩陣優化算法并非原創,筆者只是轉述了一種智慧。?
在C++中,定義一個矩陣通常是這樣的: ??
class ? MyMatrix ??
{ ??
? ? ? ? ........ ??
? ? ? ? public: ??
? ? ? ? ........ ??
? ? ? ? float ? data[50000]; ??
}?
? ? ? ? ? ? ??
這里為了方便說明問題,使用固定大小的數組,實際使用中更多的是動態分配。 ? 在對MyMatrix重載operator ? +和-后,我們就可以進行如下計算了: ??
MyMatrix ? m1,m2,m3,m4; ??
..... ??
m4=m1+m2-m3;//表達式 ??
...... ??
C++編譯器將表達式解釋為:先把m1+m2計算好賦給一個臨時MyMatrix類變量(tmp1),tmp1-m3后生成新的臨時變量tmp2,然后才把tmp2賦值給m4。程序執行過程中,會產生臨時變量tmp1,tmp2(有些C++編譯器可以優化掉其中一個或全部),由于data一般較大,分配內存會占用時間和空間。這就是為什么在數值計算方面強大的C++比Fortran慢的重要原因之一。但如果放棄MyMatrix的operator ? +和-重載,添加operator ? []取data[]的重載后,寫如下代碼: ??
struct ? plus; ? struct ? minus; ??
template ? <class ? L, ? class ? OpTag, ? class ? R> ??
struct ? Expression ??
{ ??
? ? ? ? Expression(L ? const& ? l, ? R ? const& ? r) ??
? ? ? ? ? ? ? ? ? ? : ? l(l), ? r(r) ? {}?
? ? ? ? float ? operator[](unsigned ? index) ? const; ??
? ? ? ? L ? const& ? l; ??
? ? ? ? R ? const& ? r; ??
}; ??
template ? <class ? L, ? class ? R> ??
Expression <L,plus,R> ? operator+(L ? const& ? l, ? R ? const& ? r) ??
{ ??
? ? ? ? return ? Expression <L,plus,R> (l, ? r); ??
} ??
template ? <class ? L, ? class ? R> ??
Expression <L,minus,R> ? operator-(L ? const& ? l, ? R ? const& ? r) ??
{ ??
? ? ? ? return ? Expression <L,minus,R> (l, ? r); ??
} ??
struct ? plus ??
{ ??
? ? ? ? static ? float ? apply(float ? a, ? float ? b) ??
? ? ? ? { ? return ? a ? + ? b; ? } ??
}; ??
struct ? minus ??
{ ??
? ? ? ? static ? float ? apply(float ? a, ? float ? b) ??
? ? ? ? { ? return ? a ? - ? b; ? } ??
}; ??
對MyMatrix添加=重載: ??
template ? <class ? Expr> ??
MyMatrix ? &MyMatrix::operator=(Expr ? const& ? x) ??
{ ??
? ? ? ? for ? (unsigned ? i ? = ? 0; ? i ? < ? 50000; ? i++) ??
? ? ? ? ? ? ? ? (*this) ? = ? x; ??
? ? ? ? return ? *this; ??
} ??
然后計算: ??
...... ??
m4=m1+m2-m3; ??
這時生成的臨時變量類型是Expression <L,plus,R> ,和Expression <Expression <L,plus,R> ? ,minus,R> , ? 它們占用的內存遠比MyMatrix小,Expression直到operator=MyMatrix ? 的時候才展開計算,實際上是把矩陣運算變成了加法,所以大大加快了速度。這正是數值計算所需要的。 ??
這種方法叫Expression ? templates優化。更多細節參考 < <C++ ? template> > 、Blitz++庫、boost::ublas庫和MTL庫等。?
后話:編程語言之爭一直是熱門話題。因為筆者所接觸的有限元計算程序涉及大規模的數值計算,所以筆者也曾花了2年時間搞騰Fortran。最后,筆者還是放棄古老的Fortran,轉投C++的陣營了——畢竟連微軟都早放棄Fortran了:)?
Fortran在數值計算方面的確有些優勢,但Fortran在軟件工程方面的表現得實在讓人汗顏。須知,面向對象并不是一個炒作出來的概念,面向對象對改進軟件工程方面起了里程碑式的作用,大大提高了軟件開發效率。?
從本質上看,可執行程序都編譯成二進制代碼了,怎么會存在Fortran比C++快的道理?其實最主要原因還是開發人員對C++的數值計算不熟悉所致。從上面的例子也可以看出,Fortran只是在某些方面做了一些適合數值計算的技術處理,C++并非不能做到,只是一般人平時沒留心罷了。?
Fortran計算速度快還有一個原因是大量使用了全局變量。Fortran有一個公共變量塊的定義,可以在那里設置全局變量。如果你愿意,你也可以在C++程序中大量使用全局變量提高程序運行速度。問題是這樣做的后果是在軟件工程方面帶來無盡的后遺癥,軟件維護變得異常困難。?
C++比Fortran慢有一個不好克服的原因就是使用了面向對象。虛函數的重載要查找虛函數表,必然會降低效率。所以在聽說了Fortran要支持面向對象以后,徹底讓筆者轉投到C++的陣營去了——如果Fortran支持面向對象,他在數值計算方面的優勢將蕩然無存。?
Fortran,過時的工具了,還是放棄吧。?
我的博客: ? http://chenkong0500.blog.163.com/?
歡迎交流
在C++中,定義一個矩陣通常是這樣的: ??
class ? MyMatrix ??
{ ??
? ? ? ? ........ ??
? ? ? ? public: ??
? ? ? ? ........ ??
? ? ? ? float ? data[50000]; ??
}?
? ? ? ? ? ? ??
這里為了方便說明問題,使用固定大小的數組,實際使用中更多的是動態分配。 ? 在對MyMatrix重載operator ? +和-后,我們就可以進行如下計算了: ??
MyMatrix ? m1,m2,m3,m4; ??
..... ??
m4=m1+m2-m3;//表達式 ??
...... ??
C++編譯器將表達式解釋為:先把m1+m2計算好賦給一個臨時MyMatrix類變量(tmp1),tmp1-m3后生成新的臨時變量tmp2,然后才把tmp2賦值給m4。程序執行過程中,會產生臨時變量tmp1,tmp2(有些C++編譯器可以優化掉其中一個或全部),由于data一般較大,分配內存會占用時間和空間。這就是為什么在數值計算方面強大的C++比Fortran慢的重要原因之一。但如果放棄MyMatrix的operator ? +和-重載,添加operator ? []取data[]的重載后,寫如下代碼: ??
struct ? plus; ? struct ? minus; ??
template ? <class ? L, ? class ? OpTag, ? class ? R> ??
struct ? Expression ??
{ ??
? ? ? ? Expression(L ? const& ? l, ? R ? const& ? r) ??
? ? ? ? ? ? ? ? ? ? : ? l(l), ? r(r) ? {}?
? ? ? ? float ? operator[](unsigned ? index) ? const; ??
? ? ? ? L ? const& ? l; ??
? ? ? ? R ? const& ? r; ??
}; ??
template ? <class ? L, ? class ? R> ??
Expression <L,plus,R> ? operator+(L ? const& ? l, ? R ? const& ? r) ??
{ ??
? ? ? ? return ? Expression <L,plus,R> (l, ? r); ??
} ??
template ? <class ? L, ? class ? R> ??
Expression <L,minus,R> ? operator-(L ? const& ? l, ? R ? const& ? r) ??
{ ??
? ? ? ? return ? Expression <L,minus,R> (l, ? r); ??
} ??
struct ? plus ??
{ ??
? ? ? ? static ? float ? apply(float ? a, ? float ? b) ??
? ? ? ? { ? return ? a ? + ? b; ? } ??
}; ??
struct ? minus ??
{ ??
? ? ? ? static ? float ? apply(float ? a, ? float ? b) ??
? ? ? ? { ? return ? a ? - ? b; ? } ??
}; ??
對MyMatrix添加=重載: ??
template ? <class ? Expr> ??
MyMatrix ? &MyMatrix::operator=(Expr ? const& ? x) ??
{ ??
? ? ? ? for ? (unsigned ? i ? = ? 0; ? i ? < ? 50000; ? i++) ??
? ? ? ? ? ? ? ? (*this) ? = ? x; ??
? ? ? ? return ? *this; ??
} ??
然后計算: ??
...... ??
m4=m1+m2-m3; ??
這時生成的臨時變量類型是Expression <L,plus,R> ,和Expression <Expression <L,plus,R> ? ,minus,R> , ? 它們占用的內存遠比MyMatrix小,Expression直到operator=MyMatrix ? 的時候才展開計算,實際上是把矩陣運算變成了加法,所以大大加快了速度。這正是數值計算所需要的。 ??
這種方法叫Expression ? templates優化。更多細節參考 < <C++ ? template> > 、Blitz++庫、boost::ublas庫和MTL庫等。?
后話:編程語言之爭一直是熱門話題。因為筆者所接觸的有限元計算程序涉及大規模的數值計算,所以筆者也曾花了2年時間搞騰Fortran。最后,筆者還是放棄古老的Fortran,轉投C++的陣營了——畢竟連微軟都早放棄Fortran了:)?
Fortran在數值計算方面的確有些優勢,但Fortran在軟件工程方面的表現得實在讓人汗顏。須知,面向對象并不是一個炒作出來的概念,面向對象對改進軟件工程方面起了里程碑式的作用,大大提高了軟件開發效率。?
從本質上看,可執行程序都編譯成二進制代碼了,怎么會存在Fortran比C++快的道理?其實最主要原因還是開發人員對C++的數值計算不熟悉所致。從上面的例子也可以看出,Fortran只是在某些方面做了一些適合數值計算的技術處理,C++并非不能做到,只是一般人平時沒留心罷了。?
Fortran計算速度快還有一個原因是大量使用了全局變量。Fortran有一個公共變量塊的定義,可以在那里設置全局變量。如果你愿意,你也可以在C++程序中大量使用全局變量提高程序運行速度。問題是這樣做的后果是在軟件工程方面帶來無盡的后遺癥,軟件維護變得異常困難。?
C++比Fortran慢有一個不好克服的原因就是使用了面向對象。虛函數的重載要查找虛函數表,必然會降低效率。所以在聽說了Fortran要支持面向對象以后,徹底讓筆者轉投到C++的陣營去了——如果Fortran支持面向對象,他在數值計算方面的優勢將蕩然無存。?
Fortran,過時的工具了,還是放棄吧。?
我的博客: ? http://chenkong0500.blog.163.com/?
歡迎交流
總結
- 上一篇: 除法上取整、下取整
- 下一篇: 从SVN迁移到Git(包括SVN历史纪录