【永恒的瞬間】
          ?Give me hapy ?

          前言

          網上關于編譯優化的文章很多,但大多零零散散,不成體系,本文試圖給出一個完整和清晰的優化思路,同時提供在實踐中如何進行優化的詳盡參考。但是,在介紹所有優化知識之前首先引用LFS-Book中的一句忠告:“使用編譯器優化得到的小幅度性能提升,與它帶來的風險相比微不足道”。你還要進行優化嗎?

          %@&#=^%~*# ...
          OK, crazy guy! Let's Go!!

          在繼續之前,作者還是奉勸各位:如果追求極致的優化,那么它將是一件既耗時又麻煩的事情,你會陷入無止盡的測試、測試、再測試……另外 Gentoo wiki 上有這么一句話:"GCC has well over a hundred individual optimization flags and it would be insane to try and describe them all."所以本文不會涉及全部GCC優化選項。最后作者還是再羅唆一句:優化應當適可而止為好,將精力留出來做一些其它事情會更有意義!

          先決條件

          本文的主要讀者是 LFS/Gentoo 的玩家,基本上比較 crazy 的玩家都接觸過,如果你之前從未使用過 LFS/Gentoo ,請先按照《Linux From Scratch 6.2 中文版》做一遍 LFS ,然后再來閱讀此文將會更有意義。另外,本文是建立在《深入理解軟件包的配置、編譯與安裝》一文基礎之上的,在開始閱讀本文之前,請先閱讀它。

          基本原理

          我們首先從三個方面來看與優化相關的內容:

          1. 從運行時的依賴關系來看,對性能有較大影響的組件有 kernel 和 glibc ,雖然這嚴格說來這不屬于本文的話題,但是經過精心選擇、精心配置、精心編譯的內核與C庫將對提高系統的運行速度起著基礎性的作用。
          2. 從被編譯的軟件包來看,每個軟件包的 configure 腳本都提供了許多配置選項,其中有許多選項是與性能息息相關的。比如,對于 Apache-2.2.3 而言,你可以使用 --enable-MODULE=static 將模塊靜態編譯進核心,使用 --disable-MODULE 禁用不需要的模塊,使用 --with-mpm=MPM 選擇一個高效的多路處理模塊,在不需要IPv6的情況下使用 --disable-ipv6 禁用IPv6支持,在不使用線程化的MPM時使用 --disable-threads 禁用線程支持,等等……這部分內容顯然不可能在本文中進行完整的講述,本文只能講述與優化相關的通用選項。針對特定的軟件包,請在編譯前使用 configure --help 查看所有選項,并精心選擇。
          3. 從編譯過程自身來看,將源代碼編譯為二進制文件是在 Makefile 文件的指導下,由 make 程序調用一條條編譯命令完成的。而將源代碼編譯為二進制文件又需要經過以下四個步驟:預處理(cpp) → 編譯(gcc或g++) → 匯編(as) → 連接(ld) ;括號中表示每個階段所使用的程序,它們分別屬于 GCC 和 Binutils 軟件包。顯然的,優化應當從編譯工具自身的選擇以及控制編譯工具的行為入手。

          大體上編譯優化就這"三板斧"(其實是"三腳貓")了,本文接下來的內容將討論這只貓的后兩只腳。

          編譯工具的選擇

          對于編譯工具自身的選擇,在假定使用 Binutils 和 GCC 以及 Make 的前提下,沒什么好說的,基本上新版本都能帶來性能提升,同時比老版本對新硬件的支持更好,所以應當盡量選用新版本。不過追新也可能帶來系統的不穩定,這就要針對實際情況進行權衡了。本文以 Binutils-2.17 和 GCC-4.1.1 以及 Make-3.81 為例進行說明。

          configure 選項

          這里我們只講解通用的"體系結構選項",由于"特性選項"在每個軟件包之間千差萬別,所以不可能在此處進行講解。

          這部分內容很簡單,并且其含義也是不言而喻的,下面只列出常用的值:

          • i586-pc-linux-gnu
          • i686-pc-linux-gnu
          • x86_64-pc-linux-gnu
          • powerpc-unknown-linux-gnu
          • powerpc64-unknown-linux-gnu

          如果你實在不知道應當使用哪一個,那么就干脆不使用這幾個選項,讓 config.guess 腳本自己去猜吧,反正也挺準的。

          編譯選項

          讓我們先看看 Makefile 規則中的編譯命令通常是怎么寫的。

          大多數軟件包遵守如下約定俗成的規范:

          #1,首先從源代碼生成目標文件(預處理,編譯,匯編),"-c"選項表示不執行鏈接步驟。
          $(CC) $(CPPFLAGS) $(CFLAGS) example.c   -c   -o example.o
          #2,然后將目標文件連接為最終的結果(連接),"-o"選項用于指定輸出文件的名字。
          $(CC) $(LDFLAGS) example.o   -o example
          #有一些軟件包一次完成四個步驟:
          $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) example.c   -o example
          

          當然也有少數軟件包不遵守這些約定俗成的規范,比如:

          #1,有些在命令行中漏掉應有的Makefile變量(注意:有些遺漏是故意的)
          $(CC) $(CFLAGS) example.c    -c   -o example.o
          $(CC) $(CPPFLAGS) example.c  -c   -o example.o
          $(CC) example.o   -o example
          $(CC) example.c   -o example
          #2,有些在命令行中增加了不必要的Makefile變量
          $(CC) $(CFLAGS) $(LDFLAGS) example.o   -o example
          $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) example.c   -c   -o example.o
          

          當然還有極個別軟件包完全是"胡來":亂用變量(增加不必要的又漏掉了應有的)者有之,不用$(CC)者有之,不一而足.....

          盡管將源代碼編譯為二進制文件的四個步驟由不同的程序(cpp,gcc/g++,as,ld)完成,但是事實上 cpp, as, ld 都是由 gcc/g++ 進行間接調用的。換句話說,控制了 gcc/g++ 就等于控制了所有四個步驟。從 Makefile 規則中的編譯命令可以看出,編譯工具的行為全靠 CC/CXX CPPFLAGS CFLAGS/CXXFLAGS LDFLAGS 這幾個變量在控制。當然理論上控制編譯工具行為的還應當有 AS ASFLAGS ARFLAGS 等變量,但是實踐中基本上沒有軟件包使用它們。

          那么我們如何控制這些變量呢?一種簡易的做法是首先設置與這些 Makefile 變量同名的環境變量并將它們 export 為全局,然后運行 configure 腳本,大多數 configure 腳本會使用這同名的環境變量代替 Makefile 中的值。但是少數 configure 腳本并不這樣做,你必須手動編輯生成的 Makefile 文件,在其中尋找這些變量并修改它們的值,一些源碼包在每個子文件夾中都有 Makefile 文件,真是一件很累人的事!

          CC 與 CXX

          這是 C 與 C++ 編譯器命令。默認值一般是 "gcc" 與 "g++"。這個變量本來與優化沒有關系,但是有些人因為擔心軟件包不遵守那些約定俗成的規范,害怕自己苦心設置的 CFLAGS/CXXFLAGS/LDFLAGS 之類的變量被忽略了,而索性將原本應當放置在其它變量中的選項一股老兒塞到 CC 或 CXX 中,比如:CC="gcc -march=k8 -O2 -s"。這是一種怪異的用法,本文不提倡這種做法,而是提倡按照變量本來的含義使用變量。

          CPPFLAGS

          這是用于預處理階段的選項。不過能夠用于此變量的選項,看不出有哪個與優化相關。如果你實在想設一個,那就使用下面這個吧:

          -DNDEBUG
          "NDEBUG"是一個標準的 ANSI 宏,表示不進行調試編譯。

          CFLAGS 與 CXXFLAGS

          CFLAGS 表示用于 C 編譯器的選項,CXXFLAGS 表示用于 C++ 編譯器的選項。這兩個變量實際上涵蓋了編譯和匯編兩個步驟。大多數程序和庫在編譯時默認的優化級別是"2"(使用"-O2"選項)并且在Intel/AMD平臺上默認按照i386處理器來編譯,也就是 CFLAGS="-O2 -march=i386", CXXFLAGS=$CFLAGS 。事實上,"-O2"已經啟用絕大多數安全的優化選項了。另一方面,由于大部分選項可以同時用于這兩個變量,所以僅在最后講述只能用于其中一個變量的選項。[提醒]下面所列選項皆為非默認選項,你只要按需添加即可。

          先說說"-O3"在"-O2"基礎上增加的幾項:

          -finline-functions
          允許編譯器選擇某些簡單的函數在其被調用處展開,比較安全的選項,特別是在CPU二級緩存較大時建議使用。
          -funswitch-loops
          將循環體中不改變值的變量移動到循環體之外。
          -fgcse-after-reload
          為了清除多余的溢出,在重載之后執行一個額外的載入消除步驟。

          另外:

          -fomit-frame-pointer
          對于不需要棧指針的函數就不在寄存器中保存指針,因此可以忽略存儲和檢索地址的代碼,同時對許多函數提供一個額外的寄存器。所有"-O"級別都打開它,但僅在調試器可以不依靠棧指針運行時才有效。在AMD64平臺上此選項默認打開,但是在x86平臺上則默認關閉。建議顯式的設置它(已知在x86上會造成glibc-2.3.4出現nptl測試錯誤)。
          -falign-functions=N
          -falign-jumps=N
          -falign-loops=N
          -falign-labels=N
          這四個對齊選項在"-O2"中打開,但是其中的N使用的是默認值。如果你想指定不同于默認值的N,也可以單獨指定。比如,對于L2-cache>=1M的cpu而言,指定 -falign-functions=64 可能會獲得更好的性能。

          調試選項:

          -fpretend-float
          交叉編譯的時候,假定目標機和宿主機使用同樣的浮點格式。它導致輸出錯誤的浮點常數,但是在目標機上運行的時候,真實的指令序列有可能和GNU CC希望的一樣。
          -fprofile-arcs
          在使用這一選項編譯程序并運行它以創建包含每個代碼塊的執行次數的文件后,程序可以再次使用 -fbranch-probabilities 編譯,文件中的信息可以用來優化那些經常選取的分支。如果沒有這些信息,gcc將猜測哪個分支將被經常運行以進行優化。這類優化信息將會存放在一個以源文件為名字的并以".da"為后綴的文件中。

          全局選項:

          -pipe
          在編譯過程的不同階段之間使用管道而非臨時文件進行通信,可以加快編譯速度。建議使用。

          目錄選項:

          --sysroot=dir
          將dir作為邏輯根目錄。比如編譯器通常會在 /usr/include 和 /usr/lib 中搜索頭文件和庫,使用這個選項后將在 dir/usr/include 和 dir/usr/lib 目錄中搜索。如果使用這個選項的同時又使用了 -isysroot 選項,則此選項僅作用于庫文件的搜索路徑,而 -isysroot 選項將作用于頭文件的搜索路徑。這個選項與優化無關,但是在 CLFS 中有著神奇的作用。

          代碼生成選項:

          -fno-bounds-check
          關閉所有對數組訪問的邊界檢查。該選項將提高數組索引的性能,但當超出數組邊界時,可能會造成不可接受的行為。
          -freg-struct-return
          如果struct和union足夠小就通過寄存器返回,這將提高較小結構的效率。如果不夠小,無法容納在一個寄存器中,將使用內存返回。這是一個比較安全的選項。
          -fpic
          生成可用于共享庫的位置獨立代碼。所有的內部尋址均通過全局偏移表完成。要確定一個地址,需要將代碼自身的內存位置作為表中一項插入。該選項產生可以在共享庫中存放并從中加載的目標模塊。
          -fstack-check
          為防止程序棧溢出而進行必要的檢測,僅在多線程環境中運行時才可能需要它。
          -fvisibility=hidden
          設置默認的ELF鏡像中符號的可見性為隱藏。使用這個特性可以非常充分的提高連接和加載共享庫的性能,生成更加優化的代碼,提供近乎完美的API輸出和防止符號碰撞。我們強烈建議你在編譯任何共享庫的時候使用該選項。參見 -fvisibility-inlines-hidden 選項。

          硬件體系結構相關選項[僅僅針對x86與x86_64]:

          -march=cpu-type
          為特定的cpu-type編譯二進制代碼(不能在更低級別的cpu上運行)。Intel可以用:pentium2, pentium3(=pentium3m), pentium4(=pentium4m), pentium-m, prescott, nocona 。AMD可以用:k6-2(=k6-3), athlon(=athlon-tbird), athlon-xp(=athlon-mp), k8(=opteron=athlon64=athlon-fx)
          -mfpmath=sse
          P3和athlon-xp級別及以上的cpu支持"sse"浮點指令。不過由于glibc頭文件的限制,"sse"常常使編譯出的二進制文件運行更慢,建議不要使用該選項。
          -malign-double
          將double, long double, long long對齊于雙字節邊界上;有助于生成更高速的代碼,但是程序的尺寸會變大,并且可能不能與未使用該選項編譯的程序一起工作。
          -m128bit-long-double
          指定long double為128位,pentium以上的cpu更喜歡這種標準,并且符合x86-64的ABI標準,但是卻不附合i386的ABI標準。
          -mregparm=N
          指定用于傳遞整數參數的寄存器數目(默認不使用寄存器)。0<=N<=3 ;注意:當N>0時你必須使用同一參數重新構建所有的模塊,包括所有的庫。
          -msseregparm
          使用SSE寄存器傳遞float和double參數和返回值。注意:當你使用了這個選項以后,你必須使用同一參數重新構建所有的模塊,包括所有的庫。
          -mmmx
          -mno-mmx
          -msse
          -mno-sse
          -msse2
          -mno-sse2
          -msse3
          -mno-sse3
          -m3dnow
          -mno-3dnow
          是否使用相應的擴展指令集以及內置函數,按照自己的cpu選擇吧!
          -maccumulate-outgoing-args
          指定在函數引導段中計算輸出參數所需最大空間,這在大部分現代cpu中是較快的方法;缺點是會增加代碼尺寸。
          -mthreads
          支持Mingw32的線程安全異常處理。對于依賴于線程安全異常處理的程序,必須啟用這個選項。使用這個選項時會定義"-D_MT",它將包含使用選項"-lmingwthrd"連接的一個特殊的線程輔助庫,用于為每個線程清理異常處理數據。
          -minline-all-stringops
          內聯所有的字符串操作??梢蕴岣咦址僮鞯男阅?,但是會增加代碼尺寸。
          -momit-leaf-frame-pointer
          不為葉子函數在寄存器中保存棧指針,這樣可以節省寄存器,但是將會使調試變的困難。注意:不要與 -fomit-frame-pointer 同時使用,因為會造成代碼效率低下。
          -m64
          生成專門運行于64位環境的代碼,不能運行于32位環境,僅用于x86_64[含EMT64]環境。
          -mcmodel=small
          [默認值]程序和它的符號必須位于2GB以下的地址空間。指針仍然是64位。程序可以靜態連接也可以動態連接。僅用于x86_64[含EMT64]環境。
          -mcmodel=kernel
          內核運行于2GB地址空間之外。在編譯linux內核時必須使用該選項!僅用于x86_64[含EMT64]環境。
          -mcmodel=medium
          程序必須位于2GB以下的地址空間,但是它的符號可以位于任何地址空間。程序可以靜態連接也可以動態連接。注意:共享庫不能使用這個選項編譯!僅用于x86_64[含EMT64]環境。

          其它優化選項:

          -fforce-addr
          必須將地址復制到寄存器中才能對他們進行運算。由于所需地址通常在前面已經加載到寄存器中了,所以這個選項可以改進代碼。
          -finline-limit=n
          對偽指令數超過n的函數,編譯程序將不進行內聯展開,默認為600。增大此值將增加編譯時間和編譯內存用量,但是可以得到更優化的結果。
          -fmerge-all-constants
          試圖將跨編譯單元的所有常量值和數組合并在一個副本中。但是標準C/C++要求每個變量都必須有不同的存儲位置。
          -fgcse-sm
          在全局公共子表達式消除之后運行存儲移動,以試圖將存儲移出循環。gcc-3.4中曾屬于"-O2"級別的選項。
          -fgcse-las
          在全局公共子表達式消除之后消除多余的在存儲到同一存儲區域之后的加載操作。gcc-3.4中曾屬于"-O2"級別的選項。
          -floop-optimize2
          使用 -floop-optimize 循環優化(包含在"-O1"中)的改進版本。
          -funsafe-loop-optimizations
          假定循環不會溢出,并且循環的退出條件不是無窮。這將可以在一個比較廣的范圍內進行循環優化,即使優化器自己也不能斷定這樣做是否正確。
          -fsched-spec-load
          允許一些裝載指令執行一些投機性的動作。
          -ftree-loop-linear
          在trees上進行線型循環轉換。它能夠改進緩沖性能并且允許進行更進一步的循環優化。
          -fivopts
          在trees上執行歸納變量優化。
          -ftree-vectorize
          在trees上執行循環向量化。
          -ftracer
          執行尾部復制以擴大超級塊的尺寸,它簡化了函數控制流,從而允許其它的優化措施做的更好。據說挺有效。
          -funroll-loops
          僅對循環次數能夠在編譯時或運行時確定的循環進行展開,生成的代碼尺寸將變大,執行速度可能變快也可能變慢。
          -fprefetch-loop-arrays
          生成數組預讀取指令,對于使用巨大數組的程序可以加快代碼執行速度,適合數據庫相關的大型軟件等。具體效果如何取決于代碼。
          -fweb
          為每個web結構體分配一個偽寄存器,提供更佳的緩存器使用率。gcc-3.4中曾屬于"-O3"級別的選項。
          -ffast-math
          違反IEEE/ANSI標準以提高浮點數計算速度,是個危險的選項,僅在編譯不需要嚴格遵守IEEE規范且浮點計算密集的程序考慮采用。
          -fsingle-precision-constant
          將浮點常量作為單精度常量對待,而不是隱式地將其轉換為雙精度。
          -fbranch-probabilities
          在使用 -fprofile-arcs 選項編譯程序并執行它來創建包含每個代碼塊執行次數的文件之后,程序可以利用這一選項再次編譯,文件中所產生的信息將被用來優化那些經常發生的分支代碼。如果沒有這些信息,gcc將猜測那一分支可能經常發生并進行優化。這類優化信息將會存放在一個以源文件為名字的并以".da"為后綴的文件中。
          -frename-registers
          試圖驅除代碼中的假依賴關系,這個選項對具有大量寄存器的機器很有效。gcc-3.4中曾屬于"-O3"級別的選項。
          -fbranch-target-load-optimize
          -fbranch-target-load-optimize2
          在執行序啟動以及結尾之前執行分支目標緩存器加載最佳化。
          -fstack-protector
          在關鍵函數的堆棧中設置保護值。在返回地址和返回值之前,都將驗證這個保護值。如果出現了緩沖區溢出,保護值不再匹配,程序就會退出。程序每次運行,保護值都是隨機的,因此不會被遠程猜出。
          -fstack-protector-all
          同上,但是在所有函數的堆棧中設置保護值。
          --param max-gcse-memory=xxM
          執行GCSE優化使用的最大內存量(xxM),太小將使該優化無法進行,默認為50M。
          --param max-gcse-passes=n
          執行GCSE優化的最大迭代次數,默認為 1。

          傳遞給匯編器的選項:

          -Wa,options
          options是一個或多個由逗號分隔的可以傳遞給匯編器的選項列表。其中的每一個均可作為命令行選項傳遞給匯編器。
          -Wa,--strip-local-absolute
          從輸出符號表中移除局部絕對符號。
          -Wa,-R
          合并數據段和正文段,因為不必在數據段和代碼段之間轉移,所以它可能會產生更短的地址移動。
          -Wa,--64
          設置字長為64。僅用于x86_64,并且僅對ELF格式的目標文件有效。

          僅可用于 CFLAGS 的選項:

          -fhosted
          按宿主環境編譯,其中需要有完整的標準庫,入口必須是main()函數且具有int型的返回值。內核以外幾乎所有的程序都是如此。該選項隱含設置了 -fbuiltin,且與 -fno-freestanding 等價。
          -ffreestanding
          按獨立環境編譯,該環境可以沒有標準庫,且對main()函數沒有要求。最典型的例子就是操作系統內核。該選項隱含設置了 -fno-builtin,且與 -fno-hosted 等價。

          僅可用于 CXXFLAGS 的選項:

          -fno-enforce-eh-specs
          C++標準要求強制檢查異常違例,但是該選項可以關閉違例檢查,從而減小生成代碼的體積。該選項類似于定義了"NDEBUG"宏。
          -fno-rtti
          如果沒有使用'dynamic_cast'和'typeid',可以使用這個選項禁止為包含虛方法的類生成運行時表示代碼,從而節約空間。此選項對于異常處理無效(仍然按需生成rtti代碼)。
          -ftemplate-depth-n
          將最大模版實例化深度設為'n',符合標準的程序不能超過17,默認值為500。
          -fno-optional-diags
          禁止輸出診斷消息,C++標準并不需要這些消息。
          -fno-threadsafe-statics
          GCC自動在訪問C++局部靜態變量的代碼上加鎖,以保證線程安全。如果你不需要線程安全,可以使用這個選項。
          -fvisibility-inlines-hidden
          默認隱藏所有內聯函數,從而減小導出符號表的大小,既能縮減文件的大小,還能提高運行性能,我們強烈建議你在編譯任何共享庫的時候使用該選項。參見 -fvisibility=hidden 選項。

          LDFLAGS

          LDFLAGS 是傳遞給連接器的選項。這是一個常被忽視的變量,事實上它對優化的影響也是很明顯的。

          -s
          刪除可執行程序中的所有符號表和所有重定位信息。其結果與運行命令 strip 所達到的效果相同。
          -static
          連接器將忽略動態連接庫,同時通過將靜態目標文件直接包含到結果目標文件完成對所有引用的解析。
          -shared
          鏈接器將生成共享目標代碼,該共享庫可在運行時動態鏈接到程序形成完整可執行體。而且,如果使用 gcc 命令創建共享庫作為其輸出,該選項可以防止鏈接器將缺失 main() 方法視為錯誤。為了可以正確工作,應該一致地使用選項 -fpic 和 -fPIC 以及目標平臺選項編譯構成同一庫的所有共享目標模塊。特別是,該選項可能需要生成特殊代碼來讓構造函數正常工作。由于不正確的選項設定而產生的錯誤可能很不明顯,而且也沒有警告消息。
          -shared-libgcc
          鏈接共享版本的libgcc 。當應用程序需要從一個共享庫代碼拋出另一個共享庫捕獲的異常時,需要使用共享版本的libgcc 。
          -static-libgcc
          鏈接靜態版本的libgcc 。該選項可能會引起 C++ 和 Java 中的異常處理問題。
          -Wl,options
          options是由一個或多個逗號分隔的傳遞給鏈接器的選項列表。其中的每一個選項均會作為命令行選項提供給鏈接器。
          -Wl,-On
          當n>0時將會優化輸出,但是會明顯增加連接操作的時間,這個選項是比較安全的。
          -Wl,--sort-common
          把全局公共符號按照大小排序后放到適當的輸出節,以防止符號間因為排布限制而出現間隙。
          -Wl,--no-keep-memory
          按需讀取符號表而不是將它們緩存在內存中,這樣可以減少內存用量,但是會降低運行速度。僅在連接大型可執行文件時有些意義。
          -Wl,-s
          剝離輸出文件中所有的符號信息,這個選項是比較安全的。
          -Wl,-znow
          默認僅在需要時才動態加載共享代碼,這樣減少了內存用量,但是可能降低運行速度。而使用這個選項可以強制在啟動時就加載共享代碼,這可能導致啟動速度減慢,并且占用更多的內存,但是運行速度時速度可能加快。
          -Wl,--enable-new-dtags
          在ELF中創建新式的"dynamic tags",但在老式的ELF系統上無法識別。
          -Wl,--as-needed
          移除不必要的符號引用,僅在實際需要的時候才連接,可以生成更高效的代碼。

          最后說兩個與優化無關的系統環境變量,因為會影響GCC編譯程序的方式,下面兩個是咱中國人比較關心的:

          LANG
          指定編譯程序使用的字符集,可用于創建寬字符文件、串文字、注釋;默認為英文。[目前只支持日文"C-JIS,C-SJIS,C-EUCJP",不支持中文]
          LC_ALL
          指定多字節字符的字符分類,主要用于確定字符串的字符邊界以及編譯程序使用何種語言發出診斷消息;默認設置與LANG相同。中文相關的幾項:"zh_CN.GB2312 , zh_CN.GB18030 , zh_CN.GBK , zh_CN.UTF-8 , zh_TW.BIG5"。
          posted on 2007-05-10 20:27 ???MengChuChen 閱讀(432) 評論(0)  編輯  收藏 所屬分類: Sun_Solaris
          主站蜘蛛池模板: 吐鲁番市| 集安市| 财经| 揭东县| 隆德县| 定西市| 雷波县| 扎囊县| 凉城县| 蕲春县| 敦煌市| 云和县| 育儿| 永新县| 大冶市| 西安市| 香港| 峨眉山市| 扎鲁特旗| 如皋市| 海宁市| 平邑县| 安福县| 林口县| 河间市| 辽宁省| 忻州市| 城固县| 常宁市| 磐石市| 临夏县| 九江县| 中宁县| 饶阳县| 达州市| 深水埗区| 峡江县| 德庆县| 辛集市| 邳州市| 漯河市|