編譯器優化if
1. shader中用for,if等條件語句為什麼會使得幀率降低很多
1. For和If不一定意味著動態分支
在GPU上的分支語句(for,if-else,while),可以分為三類。
Branch的Condition僅依賴編譯期常數
此時編譯器可以直接攤平分支,或者展開(unloop)。對於For來說,會有個權衡,如果For的次數特別多,或者body內的代碼特別長,可能就不展開了,因為會指令裝載也是有限或者有耗費的
額外成本可以忽略不計
Branch的Condition僅依賴編譯期常數和Uniform變數
一個運行期固定的跳轉語句,可預測
同一個Warp內所有micro thread均執行相同分支
額外成本很低
Branch的Condition是動態的表達式
這才是真正的「動態分支」
會存在一個Warp的Micro Thread之間各自需要走不同分支的問題
2. 跳轉本身的成本非常低
隨著IP/EP(Instruction Pointer/Execution Pointer)的引入,現代GPU在執行指令上的行為,和CPU沒什麼兩樣。跳轉僅僅是重新設置一個寄存器。
3.Micro Thread走不同分支時的處理
GPU本身的執行速度快,是因為它一條指令可以處理多個Micro Thread的數據(SIMD)。但是這需要多個Micro Thread同一時刻的指令是相同的。
如果不同,現代GPU通常的處理方法是,按照每個Micro Thread的不同需求多次執行分支。
x = tex.Load();
if(x == 5)
{
// Thread 1 & 2 使用這個路徑
out.Color = float4(1, 1, 1, 1);
}
else
{
// Thread 3 & 4 使用這個路徑
out.Color = float4(0, 0, 0, 0);
}
比如在上例中,兩個分支的語句Shader Unit都會執行,只是不同的是如果在執行if分支,那麼計算結果將不會寫入到thread 3 和 4的存儲中(無副作用)。
這樣做就相當於運算量增加了不少,這是動態分支的主要成本。
但是如果所有的線程,都走的是同一分支,那麼另外一個分支就不用走了。這個時候Shader Unit也不會去傻逼一樣的執行另外一個根本不需要執行的分支。此時性能的損失也不多。並且,在實際的Shader中,除非特殊情況,大部分Warp內的線程,即便在動態分支的情況下,也多半走的是同一分支。
4. 動態分支和代碼優化難度有相關性
這一點經常被忽視,就是有動態分支的代碼,因為沒准你要讀寫點什麼,前後還可能有依賴,往往也難以被優化。比如說你非要鬧這樣的語句出來:
if(x == 1)
{
color = tex1.Load(coord);
}
else if(x == 2)
{
color = tex2.Load(coord);
}
...
你說編譯器怎麼給你優化。
說句題外話,為啥要有TextureArray呢?也是為了這個場合。TextureArray除了紋理不一樣,無論格式、大小、坐標、LoD、偏移,都可以是相同的。這樣甚至可以預見不同Texture Surface上取數據的內存延遲也是非常接近的。這樣有很多的操作都可以合並成SIMD,就比多個Texture分別來取快得多了。這就是一個通過增加了約束(紋理格式、大小、定址坐標)把SISD優化成SIMD的例子。
2. 為什麼說用if(true==flag){}比if(flag==true){}更好
因為當你錯把「if(true==flag)」寫成「if(true=flag)」時你的程序編譯是通不過的!而當你把「if(flag==true)」錯寫成「if(flag=true)」時你的編譯器對程序的編譯是可以通過的,所以一旦出了類似的錯誤則將很可能完全改變了你的程序的行為或目的.總之要記住:在c語言里是禁止對一個常量賦值的.這是培養自己成為一個優秀c程序員的基礎.一旦有一天你開始在為win內核編程時,你就會發現這種編程習慣的重要性...
3. C語言中的條件賦值語句和if——else語句執行效率比較,哪一個效率高些,坐等高手解惑
一、條件語句和if else的效率一樣
二、x++的效率比x=x+1要高,只是理論上,實際現在的機器性能很好,這個差異一般無法體現
綜上所述,你上面的語句,用if else的效率要高。
4. 現代C/C++編譯器有多智能
最近在搞C/C++代碼的性能優化,發現很多時候自以為的優化其實編譯器早就優化過了,得結合反匯編才能看出到底要做什麼樣的優化。
請熟悉編譯器的同學結合操作系統和硬體談一談現代c/c++編譯器到底有多智能吧。哪些書本上的優化方法其實早就過時了?
以及程序員做什麼會讓編譯器能更好的自動優化代碼?
舉個栗子:
1,循環展開,大部分編譯器設置flag後會自動展開;
2,順序SIMD優化,大部分編譯器設置flag後也會自動優化成SIMD指令;
3,減少中間變數,大部分編譯器會自動優化掉中間變數;
etc.
查看代碼對應的匯編:
Compiler Explorer
【以下解答】
舉個之前看過的例子:
int calc_hash(signed char *s){ static const int N = 100003; int ret = 1; while (*s) { ret = ret * 131 + *s; ++ s; } ret %= N; if (ret < 0) ret += N; //注意這句 return ret;}
【以下解答】
舉個簡單例子,一到一百求和
#include int sum() { int ret= 0; int i; for(i = 1; i <= 100; i++) ret+=i; return ret;}int main() { printf("%d\n", sum()); return 0;}
【以下解答】
話題太大,碼字花時間…
先放傳送門好了。
請看Google的C++編譯器組老大Chandler Carruth的演講。這個演講是從編譯器研發工程師的角度出發,以Clang/LLVM編譯C++為例,向一般C++程序員介紹理解編譯器優化的思維模型。它講解了C++編譯器會做的一些常見優化,而不會深入到LLVM具體是如何實現這些優化的,所以即使不懂編譯原理的C++程序員看這個演講也不會有壓力。
Understanding Compiler Optimization - Chandler Carruth - Opening Keynote Meeting C++ 2015
演示稿:https://meetingcpp.com/tl_files/mcpp/2015/talks/meetingcxx_2015-understanding_compiler_optimization_themed_.pdf
錄像:https://www.youtube.com/watch?v=FnGCDLhaxKU(打不開請自備工具…)
Agner Fog寫的優化手冊也永遠是值得參考的文檔。其中的C++優化手冊:
Optimizing software in C++ - An optimization guide for Windows, linux and Mac platforms - Agner Fog
要稍微深入一點的話,GCC和LLVM的文檔其實都對各自的內部實現有不錯的介紹。
GCC:GNU Compiler Collection (GCC) Internals
LLVM:LLVM』s Analysis and Transform Passes
========================================
反模式(anti-patterns)
1. 為了「優化」而減少源碼中局部變數的個數
這可能是最沒用的手工「優化」了。特別是遇到在高級語言中「不用臨時變數來交換兩個變數」這種場景的時候。
看另一個問題有感:有什麼像a=a+b;b=a-b;a=a-b;這樣的演算法或者知識? - 編程
2. 為了「優化」而把應該傳值的參數改為傳引用
(待續…)
【以下解答】
推薦讀一讀這里的幾個文檔:
Software optimization resources. C++ and assembly. Windows, Linux, BSD, Mac OS X
其中第一篇:http://www.agner.org/optimize/optimizing_cpp.pdf
講解了C++不同領域的優化思路和問題,還有編譯器做了哪些優化,以及如何代碼配合編譯器優化。還有優化多線程、使用向量指令等的介紹,推薦看看。
感覺比較符合你的部分需求。
【以下解答】
一份比較老的slides:
http://www.fefe.de/source-code-optimization.pdf
【以下解答】
利用C++11的range-based for loop語法可以實現類似python里的range生成器,也就是實現一個range對象,使得
for(auto i : range(start, stop, step))
【以下解答】
我覺得都不用現代。。。。寄存器分配和指令調度最智能了
【以下解答】
每次編譯poco庫的時候我都覺得很為難GCC
【以下解答】
有些智能並不能保證代碼變換前後語義是等價的
【以下解答】
誒誒,我錯了各位,GCC是可以藉助 SSE 的 xmm 寄存器進行優化的,經 @RednaxelaFX 才知道應該添加 -march=native 選項。我以前不了解 -march 選項,去研究下再來補充為什麼加和不加區別這么大。
十分抱歉黑錯了。。。以後再找別的點來黑。
誤導大家了,實在抱歉。(??ˇ?ˇ??)
/*********以下是並不正確的原答案*********/
我是來黑 GCC的。
最近在搞編譯器相關的活,編譯OpenSSL的時候有一段這樣的代碼:
BN_ULONG a0,a1,a2,a3; // EmmetZC 註:BN_ULONG 其實就是 unsigned longa0=B[0]; a1=B[1]; a2=B[2]; a3=B[3];A[0]=a0; A[1]=a1; A[2]=a2; A[3]=a3;
【以下解答】
提示:找不到對象
【以下解答】
忍不住抖個機靈。
私以為正常寫代碼情況下編譯器就能優化,才叫智能編譯器。要程序員絞盡腦汁去考慮怎麼寫代碼能讓編譯器更好優化,甚至降低了可讀性,那就沒有起到透明屏蔽的作用。
智能編譯器應該是程序猿要較勁腦汁才能讓編譯器不優化。
理論上是這樣的。折疊我吧。
【以下解答】
編譯器智能到每次我都覺得自己很智障。
【以下解答】
雖然題主內容里是想問編譯器代碼性能優化方面的內容,但題目里既然說到編譯器的的智能,我就偏一下方向來說吧。
有什麼更能展示編譯器的強大和智能?
自然是c++的模版元編程
template meta programming
簡單解釋的話就是寫代碼的代碼,寫的還是c++,但能讓編譯器在編譯期間生成正常的c++代碼。
沒接觸過的話,是不是聽上去感覺就是宏替換的加強版?感覺不到它的強大呢?
只是簡單用的話,效果上這樣理解也沒什麼
但是一旦深入下去,尤其翻看大神寫的東西,這明明看著就是c++的代碼,但TM怎麼完全看不懂他在干什麼?後來才知道這其實完全是另外一個世界,可是明明是另外一個世界的東西但它又可以用來做很多正常c++能做的事....
什麼?你說它好像不能做這個,不能做那個,好像做不了太多東西,錯了,大錯特錯。就像你和高手考試都考了100分的故事一樣,雖然分數一樣,但你是努力努力再努力才得了滿分,而高手只是因為卷面分只有100分.....在元編程面前,只有想不到,沒有做不到。
再回頭看看其他答案,編譯器順手幫你求個和,丟棄下無用代碼,就已經被驚呼強大了,那模板元編程這種幾乎能在編譯期直接幫你「生成」包含復雜邏輯的c++代碼,甚至還能間接「執行」一些復雜邏輯,這樣的編譯器是不是算怪獸級的強大?
一個編譯器同時支持編譯語法相似但結果不同卻又關聯的兩種依賴語言,這個編譯器有多強大多智能?
寫的人思維都要轉換幾次,編譯器轉著圈嵌著套翻著番兒地編譯代碼的代碼也肯定是無比蛋疼的,你說它有多強大多智能?
一個代碼創造另外一個代碼,自己能按照相似的規則生成自己,是不是聽上去已經有人工智慧的發展趨勢了?
上帝說,要有光,於是有了光。
老子曰,一生二,二生三,三生萬物。
信c++,得永生!
===
FBI WARNING:模板元編程雖然很強大,但也有不少缺點,尤其對於大型項目,為了你以及身邊同事的身心健康,請務必適度且謹慎的使用。勿亂入坑,回頭是岸。
【以下解答】
c++11的auto自動類型推斷算么....
【以下解答】
智能到開不同級別的優化,程序行為會不同 2333
【以下解答】
這個取決於你的水平
5. c語言中純粹的if語句耗時嗎
要算機器周期的話,還取決於實際的軟硬體環境的,
給你一個程序實驗一下:
#include <stdio.h>
__forceinline unsigned _int64 My_clock(void)
{
_asm _emit 0x0F
_asm _emit 0x31
}
unsigned _int64 Start(void) { return My_clock();} // 開始時間
unsigned _int64 Stop(unsigned _int64 m_start, unsigned _int64 m_overhead)
{return My_clock()-m_start - m_overhead; } // 停時間
void main()
{
unsigned _int64 m_start=0, m_overhead=0;
unsigned int N;
int a;
m_start = My_clock();
m_overhead = My_clock() - m_start - m_overhead;
m_start = My_clock(); // 正式開始
//測試程序段 Start
//這里寫你要計算耗時的程序段!!!!!!!!!!!!!!!!!!!!!!!!
//測試程序段 End
N = Stop(m_start,m_overhead); // 獲取耗時.
printf("Time:%I64d us\n", N);
}
我VC6+win7環境測試了一下:
#include <stdio.h>
__forceinline unsigned _int64 My_clock(void)
{
_asm _emit 0x0F
_asm _emit 0x31
}
unsigned _int64 Start(void) { return My_clock();} // 開始時間
unsigned _int64 Stop(unsigned _int64 m_start, unsigned _int64 m_overhead)
{return My_clock()-m_start - m_overhead; } // 停時間
void main()
{
unsigned _int64 m_start=0, m_overhead=0;
unsigned int N;
int a;
m_start = My_clock();
m_overhead = My_clock() - m_start - m_overhead;
m_start = My_clock(); // 正式開始
//測試程序段 Start
if(a>7) a=1;
//測試程序段 End
N = Stop(m_start,m_overhead); // 獲取耗時.
printf("Time:%I64d us\n", N);
}
耗時10us左右
下面這句呢?
if(a>7)
{
if(a>10)
{
if(a>15)
{
a=1;
}
}
}
#include <stdio.h>
__forceinline unsigned _int64 My_clock(void)
{
_asm _emit 0x0F
_asm _emit 0x31
}
unsigned _int64 Start(void) { return My_clock();} // 開始時間
unsigned _int64 Stop(unsigned _int64 m_start, unsigned _int64 m_overhead)
{return My_clock()-m_start - m_overhead; } // 停時間
void main()
{
unsigned _int64 m_start=0, m_overhead=0;
unsigned int N;
int a;
m_start = My_clock();
m_overhead = My_clock() - m_start - m_overhead;
m_start = My_clock(); // 正式開始
//測試程序段 Start
if(a>7)
{
if(a>10)
{
if(a>15)
{
a=1;
}
}
}
//測試程序段 End
N = Stop(m_start,m_overhead); // 獲取耗時.
printf("Time:%I64d us\n", N);
}
耗時30us左右。
6. java 編譯優化問題
java編譯的結果是位元組碼而不是二進制,所以在運行時vm的優化才是重要的,包括VM的回收策略、分配給VM內存的大小都能在一定程度上影響性能。Sun的VM支持熱點編譯,對高頻執行的代碼段翻譯的2進制會進行緩存,這也是VM的一種優化。
IBM JVM處理數學運算速度最快,BEA JVM處理大量線程和網路socket性能最好,而Sun JVM處理通常的商業邏輯性能最好。不過Hotspot的Server mode被報告有穩定性的問題。
Java 的最大優勢不是體現在執行速度上,所以對Compiler的要求並不如c++那樣高,代碼級的優化還需要程序員本身的功底。
貼個java的運行參數:
Usage: java [-options] class [args...]
(to execute a class)
or java [-options] -jar jarfile [args...]
(to execute a jar file)
where options include:
-client to select the "client" VM
-server to select the "server" VM
-hotspot is a synonym for the "client" VM [deprecated]
The default VM is client.
-cp <class search path of directories and zip/jar files>
-classpath <class search path of directories and zip/jar files>
A ; separated list of directories, JAR archives,
and ZIP archives to search for class files.
-D<name>=<value>
set a system property
-verbose[:class|gc|jni]
enable verbose output
-version print proct version and exit
-version:<value>
require the specified version to run
-showversion print proct version and continue
-jre-restrict-search | -jre-no-restrict-search
include/exclude user private JREs in the version search
-? -help print this help message
-X print help on non-standard options
-ea[:<packagename>...|:<classname>]
-enableassertions[:<packagename>...|:<classname>]
enable assertions
-da[:<packagename>...|:<classname>]
-disableassertions[:<packagename>...|:<classname>]
disable assertions
-esa | -enablesystemassertions
enable system assertions
-dsa | -disablesystemassertions
disable system assertions
-agentlib:<libname>[=<options>]
load native agent library <libname>, e.g. -agentlib:hprof
see also, -agentlib:jdwp=help and -agentlib:hprof=help
-agentpath:<pathname>[=<options>]
load native agent library by full pathname
-javaagent:<jarpath>[=<options>]
load Java programming language agent, see
java.lang.instrument
-Xmixed mixed mode execution (default)
-Xint interpreted mode execution only
-Xbootclasspath:<directories and zip/jar files separated by ;>
set search path for bootstrap classes and resources
-Xbootclasspath/a:<directories and zip/jar files separated by ;>
append to end of bootstrap class path
-Xbootclasspath/p:<directories and zip/jar files separated by ;>
prepend in front of bootstrap class path
-Xnoclassgc disable class garbage collection
-Xincgc enable incremental garbage collection
-Xloggc:<file> log GC status to a file with time stamps
-Xbatch disable background compilation
-Xms<size> set initial Java heap size
-Xmx<size> set maximum Java heap size
-Xss<size> set java thread stack size
-Xprof output cpu profiling data
-Xfuture enable strictest checks, anticipating future default
-Xrs rece use of OS signals by Java/VM (see
documentation)
-Xcheck:jni perform additional checks for JNI functions
-Xshare:off do not attempt to use shared class data
-Xshare:auto use shared class data if possible (default)
-Xshare:on require using shared class data, otherwise fail.
Java虛擬機(JVM)參數配置說明
在Java、J2EE大型應用中,JVM非標准參數的配置直接關繫到整個系統的性能。
JVM非標准參數指的是JVM底層的一些配置參數,這些參數在一般開發中默認即可,不需
要任何配置。但是在生產環境中,為了提高性能,往往需要調整這些參數,以求系統達
到最佳新能。
另外這些參數的配置也是影響系統穩定性的一個重要因素,相信大多數Java開發人員都
見過「OutOfMemory」類型的錯誤。呵呵,這其中很可能就是JVM參數配置不當或者就沒
有配置沒意識到配置引起的。
為了說明這些參數,還需要說說JDK中的命令行工具一些知識做鋪墊。
首先看如何獲取這些命令配置信息說明:
假設你是windows平台,你安裝了J2SDK,那麼現在你從cmd控制台窗口進入J2SDK安裝目
錄下的bin目錄,然後運行java命令,出現如下結果,這些就是包括java.exe工具的和
JVM的所有命令都在裡面。
-----------------------------------------------------------------------
D:\j2sdk15\bin>java
Usage: java [-options] class [args...]
(to execute a class)
or java [-options] -jar jarfile [args...]
(to execute a jar file)
where options include:
-client to select the "client" VM
-server to select the "server" VM
-hotspot is a synonym for the "client" VM [deprecated]
The default VM is client.
-cp <class search path of directories and zip/jar files>
-classpath <class search path of directories and zip/jar files>
A ; separated list of directories, JAR archives,
and ZIP archives to search for class files.
-D<name>=<value>
set a system property
-verbose[:class|gc|jni]
enable verbose output
-version print proct version and exit
-version:<value>
require the specified version to run
-showversion print proct version and continue
-jre-restrict-search | -jre-no-restrict-search
include/exclude user private JREs in the version search
-? -help print this help message
-X print help on non-standard options
-ea[:<packagename>...|:<classname>]
-enableassertions[:<packagename>...|:<classname>]
enable assertions
-da[:<packagename>...|:<classname>]
-disableassertions[:<packagename>...|:<classname>]
disable assertions
-esa | -enablesystemassertions
enable system assertions
-dsa | -disablesystemassertions
disable system assertions
-agentlib:<libname>[=<options>]
load native agent library <libname>, e.g. -agentlib:hprof
see also, -agentlib:jdwp=help and -agentlib:hprof=help
-agentpath:<pathname>[=<options>]
load native agent library by full pathname
-javaagent:<jarpath>[=<options>]
load Java programming language agent, see
java.lang.instrument
-----------------------------------------------------------------------
在控制台輸出信息中,有個-X(注意是大寫)的命令,這個正是查看JVM配置參數的命
令。
其次,用java -X 命令查看JVM的配置說明:
運行後如下結果,這些就是配置JVM參數的秘密武器,這些信息都是英文的,為了方便
閱讀,我根據自己的理解翻譯成中文了(不準確的地方還請各位博友斧正)
-----------------------------------------------------------------------
D:\j2sdk15\bin>java -X
-Xmixed mixed mode execution (default)
-Xint interpreted mode execution only
-Xbootclasspath:<directories and zip/jar files separated by ;>
set search path for bootstrap classes and resources
-Xbootclasspath/a:<directories and zip/jar files separated by ;>
append to end of bootstrap class path
-Xbootclasspath/p:<directories and zip/jar files separated by ;>
prepend in front of bootstrap class path
-Xnoclassgc disable class garbage collection
-Xincgc enable incremental garbage collection
-Xloggc:<file> log GC status to a file with time stamps
-Xbatch disable background compilation
-Xms<size> set initial Java heap size
-Xmx<size> set maximum Java heap size
-Xss<size> set java thread stack size
-Xprof output cpu profiling data
-Xfuture enable strictest checks, anticipating future default
-Xrs rece use of OS signals by Java/VM (see
documentation)
-Xcheck:jni perform additional checks for JNI functions
-Xshare:off do not attempt to use shared class data
-Xshare:auto use shared class data if possible (default)
-Xshare:on require using shared class data, otherwise fail.
The -X options are non-standard and subject to change without notice.
-----------------------------------------------------------------------
JVM配置參數中文說明:
-----------------------------------------------------------------------
1、-Xmixed mixed mode execution (default)
混合模式執行
2、-Xint interpreted mode execution only
解釋模式執行
3、-Xbootclasspath:<directories and zip/jar files separated by ;>
set search path for bootstrap classes and resources
設置zip/jar資源或者類(.class文件)存放目錄路徑
3、-Xbootclasspath/a:<directories and zip/jar files separated by ;>
append to end of bootstrap class path
追加zip/jar資源或者類(.class文件)存放目錄路徑
4、-Xbootclasspath/p:<directories and zip/jar files separated by ;>
prepend in front of bootstrap class path
預先載入zip/jar資源或者類(.class文件)存放目錄路徑
5、-Xnoclassgc disable class garbage collection
關閉類垃圾回收功能
6、-Xincgc enable incremental garbage collection
開啟類的垃圾回收功能
7、-Xloggc:<file> log GC status to a file with time stamps
記錄垃圾回日誌到一個文件。
8、-Xbatch disable background compilation
關閉後台編譯
9、-Xms<size> set initial Java heap size
設置JVM初始化堆內存大小
10、-Xmx<size> set maximum Java heap size
設置JVM最大的堆內存大小
11、-Xss<size> set java thread stack size
設置JVM棧內存大小
12、-Xprof output cpu profiling data
輸入CPU概要表數據
13、-Xfuture enable strictest checks, anticipating future default
執行嚴格的代碼檢查,預測可能出現的情況
14、-Xrs rece use of OS signals by Java/VM (see
documentation)
通過JVM還原操作系統信號
15、-Xcheck:jni perform additional checks for JNI functions
對JNI函數執行檢查
16、-Xshare:off do not attempt to use shared class data
盡可能不去使用共享類的數據
17、-Xshare:auto use shared class data if possible (default)
盡可能的使用共享類的數據
18、-Xshare:on require using shared class data, otherwise fail.
盡可能的使用共享類的數據,否則運行失敗
The -X options are non-standard and subject to change without notice.
7. 在不考慮編譯器優化等因素下,下面那個運算比較快
下面是在編譯器不優化的情況下的代碼反匯編後的結果
14: if (a > b)//我們看到,只有3條指令,
0040104D mov edx,dword ptr [ebp-4] //將a放入寄存器EDX
00401050 cmp edx,dword ptr [ebp-8] //比較EDX和b的大小
00401053 jle main+3Ch (0040105c) //如果a<b跳轉到0040105c
15: {
16: return 1;
00401055 mov eax,1
0040105A jmp main+4Fh (0040106f)
17: }
18: if (a - b > 0)//我們看到有4條指令
0040105C mov eax,dword ptr [ebp-4]//將a放入EAX
0040105F sub eax,dword ptr [ebp-8]//a-b的差放入EAX
00401062 test eax,eax//比較EAX是否為0
00401064 jle main+4Dh (0040106d)//條件跳轉
19: {
20: return 2;
00401066 mov eax,2
0040106B jmp main+4Fh (0040106f)
21: }
所以我認為if (a > b) {....}
效率更高
8. 編譯器在編譯階段,究竟做哪些事情
1. 預處理首先源代碼文件(.c/.cpp)和相關頭文件(.h/.hpp)被預處理器cpp預編譯成.i文件(C++為.ii)。預處理命令為:gcc –E hello.c –o hello.i預編譯過程主要處理那些源代碼中以#開始的預編譯指令,主要處理規則如下:u 將所有的#define刪除,並且展開所有的宏定義;u 處理所有條件編譯指令,如#if,#ifdef等;u 處理#include預編譯指令,將被包含的文件插入到該預編譯指令的位置。該過程遞歸進行,及被包含的文件可能還包含其他文件。u 刪除所有的注釋//和 /**/;u 添加行號和文件標識,如#2 「hello.c」 2,以便於編譯時編譯器產生調試用的行號信息及用於編譯時產生編譯錯誤或警告時能夠顯示行號信息;u 保留所有的#pragma編譯器指令,因為編譯器須要使用它們。2. 編譯編譯過程就是把預處理完的文件進行一系列詞法分析,語法分析,語義分析及優化後生成相應的匯編代碼文件(.s)。編譯的命令為:gcc –S hello.i –o hello.s或者從源文件直接輸出匯編代碼文件:gcc –S hello.c –o hello.s現在版本的GCC把預編譯和編譯兩個步驟合並成一個步驟,由程序cc1來完成(C++為cc1plus)。3. 匯編匯編就是將匯編代碼轉變成機器可以執行的命令,生成目標文件(.o),匯編器as根據匯編指令和機器指令的對照表一一翻譯即可完成。匯編的命令為:gcc –c hello.s –o hello.o或者從源文件直接輸出目標文件:gcc –c hello.c –o hello.o4. 鏈接鏈接就是鏈接器ld將各個目標文件組裝在一起,解決符號依賴,庫依賴關系,並生成可執行文件。鏈接的命令為:ld –static crt1.o crti.o crtbeginT.o hello.o –start-group –lgcc –lgcc_eh –lc-end-group crtend.o crtn.o一般我們使用一條命令就可以完成上述4個步驟:gcc hello.c實際上gcc只是一些其它程序的包裝,它會根據不同參數去調用預編譯編譯程序cc1、匯編器as、鏈接器ld。
9. 為什麼說三目條件運算符:操作符能使編譯器產生比if-then-else更優化的代碼
所謂優化問題1.內存優化,就是所寫代碼的長度要盡可能的短小但又不是功能,佔用內存盡可能的少; 2.計算機cpu執行編譯好的代碼所用的時間盡可能的少,因為計算機只有一個cpu,同一時間只能執行一個程序,這就要求我們寫代碼盡可能少的佔用cpu的時間 根據以上的介紹我們再來談三目運算符的?:與if -then-else優化的問題,三目運算符執行的起來使用最多是三個機械周期,而if -then-else至少是三個機械周期,這就是說它至少在佔用cpu上是得到了優化,其次我們再來看看代碼,很明顯if -then-else的代碼要比?:的代碼寫得要長一些,綜合以上三目運算符?:是要比if -then-else更為的優化。在這里多說一點,三目運算符?:沒有if -then-else使用的靈活,這也是他們的機構所決定的 越集中的東西越不靈活,無所謂誰更好,只有使用誰更適合
10. linux共享內存會不會被編譯器優化
利用共享內存(Share Memory)可以讓我們在任意兩個進程之間傳遞數據,而且也是相對容易實現的一種方法,在正常情
況下,一個進程所使用的內存是不允許其它進程訪問的,但是通過共享內存可以實現數據的共享。
使用共享內存用到的API函數有:
# include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
key_t ftok(const char *pathname, int proj_id)
其中pathname為當前進程的程序名,第二個參數為進程所開辟的第幾個共享內存,返回一個key_t值,用於共享內存的獲取
使用。
int shmget(key_t shmkey, int shmsiz, int flag)
void *shmat(int shmid, char *shmaddr, int shmflag)
int shmdt(char *shmaddr)
shmget是用來創建或者指向一塊共享內存的函數,其中shmkey是共享內存的標識符,如果父子關系的進程間通信的話,這
個標識用IPC_PRIVATE替代,如果沒有任何關系,可以使用ftok計算出來;Shmsiz是這塊內存的大小;flag是這塊內存的模
式和許可權標識(IPC_CREAT, IPC_ALLOC, IPC_EXCL,0666許可權)。函數成功返回共享內存的shmid,否則返回-1表示失敗。
Shmat用來允許進程訪問共享內存的函數,其中shmid是共享內存的ID,shmaddr是共享內存起始位置,shmflag是本進程對
內存操作模式,SHM_RDONLY是只讀模式,成功返回共享內存的起始位置。
Shmdt與shmat相反,是用來禁止進程訪問一塊共享內存的函數。
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
其中shmid是共享內存的ID,cmd是控制命令(IPC_STAT, IPC_SET, IPC_RMID),struct shmid_ds *buf是一個結構體指針
,如果要改變共享內存的狀態,使用它來指定,獲得狀態就存放在這個結構體中。
上述API函數的詳細說明請使用man(男人)查看:
# man shmget | shmat | shmdt | shmctl
下面使用一個簡單的例子說明如何使用共享內存。
// sharememcut.c 拷貝用戶輸入的字元串到共享內存中
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
int main()
{
key_t shmkey;
int shmid, in_tmp;
char *head, *pos, in_data[4096], *in_ptr;
// Create share memory KEY
shmkey = ftok("sharememcut", 1);
// Get the share memory ID
shmid = shmget(shmkey, sizeof(in_data), IPC_CREAT | 0666);
// Allow the process to access share memory, and get the address
head = pos = shmat(shmid, 0, 0);
in_ptr = in_data;
// Receive the character from stdin, 'q' to quit
while ((in_tmp = getchar()) != 'q') {
*in_ptr = in_tmp;
in_ptr++;
}
*in_ptr = '\0';
in_ptr = in_data;
// Cut the data into share memory
while (*in_ptr != '\0') {
*pos++ = *in_ptr++;
}
// Prohabit the process to access share memory
shmdt(head);
return 0;
}
# gcc -o sharememcut sharememcut.c 編譯sharememcut.c
# ./sharememcut 執行sharememcut創建共享內存
…
# ipcs -m | grep 4096 查看創建的共享內存
0x01068288 348848145 root 666 4096 0
// sharemempaste.c 將共享內存的內容顯示在屏幕上並且刪除共享內存
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
int main()
{
key_t shmkey;
int shmid;
char *head, *pos, out_data[4096], *out_ptr;
// Create share memory KEY
shmkey = ftok("sharememcut", 1);
// Get the share memory ID
shmid = shmget(shmkey, sizeof(out_data), 0666);
if (shmid == -1)
return -1;
// Allow the process to access share memory, and get the address
head = pos = shmat(shmid, 0, 0);
out_ptr = out_data;
// Get the data from share memory
while (*pos != '\0') {
*out_ptr++ = *pos++;
}
*out_ptr = '\0';
// Output the data into stdout
printf("%s\n", out_data);
fflush(stdout);
// Prohabit the process to access share memory
shmdt(head);
// Delete the share memory
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
# gcc -o sharemempaste sharemempaste.c 編譯sharemempaste.c
# ./sharemempaste 執行程序顯示共享內存的內容