Android ndkのso体積縮小
5806 ワード
零背景
SDKは体積に敏感であり,インタフェースコード以外はすべてNativeで実現し,armeabi下のsoは約800 kあり,40%程度の削減を目標としている.そこで今回はSO体積を縮小する過程があった.ボリュームの最適化には主に4つのレベルがあり、それぞれコード、コンパイルパラメータ、プロジェクト構造、最適化ツールであり、以下ではこの4つの面について説明する.
一.コードレベル
コードレベルは、一部の肥大化したサードパーティライブラリを置き換え、自分の実装コードを簡素化し、stlライブラリの使用をできるだけ減らし、純粋にcで実現することにほかならない.私のこのプロジェクトでは、コードが最適化できる場所は3つあります. jsonライブラリ 以前はJsonCppというオープンソースライブラリを用いていたが,文字列解析から簡単にjson文字列に構築し,オブジェクトをjson,jsonに変換する機能が非常に包括的であったが,2000+コードの3つの実装クラスが我々のso体積に100 k程度増加した.プロジェクトはjsonにこんなに依存しているので,これは大きな最適化点である.ここでは使いやすいオープンソースライブラリを2つお勧めします.
一つはC++実装のJson 11で、彼のコード量は700行程度で、しかも2つのファイルしかなくて、その中で文字列解析を実現して、json文字列を構築して、オブジェクトはjsonを回転して、jsonはオブジェクトの4つの比較的に基本的な機能を回転して、完全にプロジェクトの需要を満たすことができます.
もう1つは純Cで実現したJSMNで、200行程度しかありませんが、インタフェースが簡単すぎて使いづらいので断念しますが、stlライブラリの依存から抜け出したいなら、このライブラリが良い選択です.
使いやすさと体積を総合的に考慮すると,Json 11ライブラリを置き換えた後,soサイズは約70 k(10%程度)減少した. protobufライブラリ プロジェクトはpbプロトコルと外部通信を使用しているため、グーグルのprotobuf解析ライブラリを使用しており、protobuf解析ライブラリやprotobufプロトコルファイルのコンパイルなど、多くのprotobufプロトコルCファイルが付属しており、約200 k程度のスペースを占めている.
GoogleはC++のシナリオしか提供していないが、Cのシナリオprotobuf-cを実現した人もいる.C++のシナリオは使い勝手が悪いが、置き換えて150 k(20%)程度のスペースを節約できる. stlライブラリ依存 究極の案はstlライブラリに依存せず,純粋なCを用いて実現し,stlライブラリが静的リンクを用いてsoに打ち込まれると300 k程度増加するが,どのくらいのstlライブラリが使用されているかによって異なる.コード内のmap,vector,threadなどのstlライブラリ依存性をC実装に置き換えるか,まったく使わない.もちろん,コード全体の修正が多く,多くのライブラリが使用できないため,C++の多くの利点を失うことになり,今後の反復効率に多くの影響を及ぼす.そのため、この案をあきらめざるを得ません.もし後で本当にずるいお客様が200 k以下のso体積が必要なら、この計画をしましょう.
これも実際には経験であり、コードがいくつかのiotデバイス上に走っているなど、動的リンクライブラリのサイズに特に敏感であれば、stlライブラリを早期に回避したり、少なく使用したり、純粋なCを使用して実装したりする必要があります.
この3つの点は私のプロジェクトの3つの最適化点に対してだけで、他の業務論理コードの最適化も特別な考え方をまとめていません.特殊な状況の特殊な分析しかできません.皆さんは自分のプロジェクトの実際の状況に基づいて最適化プロジェクトコードを選択することができます.
二.コンパイルレベル
コンパイル面では比較的多くの点があり、主にAndroidである.MkとApplication.mkの2つのファイルのパラメータ構成は、2つのファイルの公式参照ドキュメントを以下に示し、以下のパラメータはいずれも公式ドキュメントで参照できます.
Application.mk公式説明
Android.mk公式説明 rttiパラメータ exceptionsパラメータ gc-sectionsパラメータ
このパラメータは意外な喜びと言えるが,意外にも約200 k(20%)程度のヒントを減らすことができるので,このパラメータ設定の原理を簡単に検討した.
GCCリンク操作はsectionを最小の処理ユニットとし,1つのsectionにある記号が参照される限り,そのsectionが加わる.
リンクフェーズでは、上記の分割操作により、リンク中にどのfunctionが使用されているのか、どのfunctionが使用されていないのかを容易にマークすることができ、このようにして使用されていないfunctionを最後に生成されたsoライブラリから取り除くことができ、生成されたsoはsectionの分割を保持しないため、上記はsectionによって生成された体積が増加するため消失する、無駄なfunctionが削除され、so全体の体積が大幅に減少します.実は全体の過程はjava proguardのoptimize過程に似ていますが、Javaには反射、ダイナミックロードなどの過程があるので、optimizeは少ないです.C++には似たようなメカニズムはないようです(C/C++に精通していないのでよくわかりませんが)、そうでないと穴もあります. icf=safe余分コード を削除 Osパラメータ このパラメータの詳細については公式の説明を参照してください
Options That Control Optimization
ここで簡単に説明すると、Oは実行効率を最適化し、1~3から異なる最適化レベルを開くことで、コンパイル時間を増やしたり、コンパイル後のライブラリのサイズを増やしたりすることができます.具体的には、上記のサイトを参照して、1~3の最適化レベルがそれぞれどのような最適化方式を開いているかを確認することができます.O 0はデバッグモードであり、コンパイル時間を最小限に抑え、デバッグに予想される結果を生み出す.Osはsizeを最適化することを意味し,このオプションは生成コードの体積を最大限に減らすことができ,すなわち最後に動的ライブラリの体積を最小限に抑えることができる.
armのデフォルトはOsですが、x 86のデフォルトは-O 2です.ここでは設定できます.
これによりso体積を効果的に低減することができるが,効率は少し遅くなる可能性がある.
三.プロジェクト構造レベル
プロジェクトに2つのダイナミックライブラリがある場合は、主にApplocationを変更するstlライブラリを共有する方法でコンパイルできます.mkファイルパラメータをxxx_に変更sharedは、コンパイル後に独自のダイナミックライブラリと
次に、異なるメーカーによって異なるsoが提供され、一般的にarmeabi-v 7 aはマルチ浮動小数点演算に対して比較的大きな最適化があり、nativeコードが多くの浮動小数点データ演算に関連していない場合、armeabi-v 7 aのサポートを直接取り除くことができ、armeabiのみを使用することができる.次はx 86のダイナミックライブラリですが、シミュレータの下でもarmeabiの実行に互換性があるため、x 86は必要ありません.従って、一般的な応用では、armeabiのsoはすべての機械の動作をサポートするのに十分であり、より少ないsoは応用の体積を大幅に減少させることができる.
四.最適化ツール-UPX
最後に別の考え方を提供し、UPXを使用します.
UPX-the Ultimate Packer for eXecutables
UPXは実行可能プログラムファイル圧縮器で、圧縮された実行可能ファイルの体積は50%-70%縮小されている.以前はPC時代によくシェルとして使われていましたが、もちろん修正したことがあります.これを研究する専門家がいますが、使用後、体積を20~40%減らすことができるのは、どれだけの圧縮比を設定するかによって決まります.効果は非常に良いと言えますが、公式サイトで提供されているツールを直接使うとAndroidで互換性の問題がありますが、解決できます.この不便さはもっと明らかにして、興味のある学生は自分で穴を踏んで穴を埋めましょう.
五.まとめ
最後に上記の種々の態様によりso体積が300 k程度に減少する、パッケージ化はおよそ150 k程度の体積しか増えていません(パッケージ化するとzip圧縮が行われるので)、目的は達成されました.稼働効率にはあまり敏感ではないので、ボリュームを減らしてどれだけの性能損失をもたらすかはテストしていませんが、その後はこの方面のテストを行う必要があるでしょう.
SDKは体積に敏感であり,インタフェースコード以外はすべてNativeで実現し,armeabi下のsoは約800 kあり,40%程度の削減を目標としている.そこで今回はSO体積を縮小する過程があった.ボリュームの最適化には主に4つのレベルがあり、それぞれコード、コンパイルパラメータ、プロジェクト構造、最適化ツールであり、以下ではこの4つの面について説明する.
一.コードレベル
コードレベルは、一部の肥大化したサードパーティライブラリを置き換え、自分の実装コードを簡素化し、stlライブラリの使用をできるだけ減らし、純粋にcで実現することにほかならない.私のこのプロジェクトでは、コードが最適化できる場所は3つあります.
一つはC++実装のJson 11で、彼のコード量は700行程度で、しかも2つのファイルしかなくて、その中で文字列解析を実現して、json文字列を構築して、オブジェクトはjsonを回転して、jsonはオブジェクトの4つの比較的に基本的な機能を回転して、完全にプロジェクトの需要を満たすことができます.
もう1つは純Cで実現したJSMNで、200行程度しかありませんが、インタフェースが簡単すぎて使いづらいので断念しますが、stlライブラリの依存から抜け出したいなら、このライブラリが良い選択です.
使いやすさと体積を総合的に考慮すると,Json 11ライブラリを置き換えた後,soサイズは約70 k(10%程度)減少した.
GoogleはC++のシナリオしか提供していないが、Cのシナリオprotobuf-cを実現した人もいる.C++のシナリオは使い勝手が悪いが、置き換えて150 k(20%)程度のスペースを節約できる.
これも実際には経験であり、コードがいくつかのiotデバイス上に走っているなど、動的リンクライブラリのサイズに特に敏感であれば、stlライブラリを早期に回避したり、少なく使用したり、純粋なCを使用して実装したりする必要があります.
この3つの点は私のプロジェクトの3つの最適化点に対してだけで、他の業務論理コードの最適化も特別な考え方をまとめていません.特殊な状況の特殊な分析しかできません.皆さんは自分のプロジェクトの実際の状況に基づいて最適化プロジェクトコードを選択することができます.
二.コンパイルレベル
コンパイル面では比較的多くの点があり、主にAndroidである.MkとApplication.mkの2つのファイルのパラメータ構成は、2つのファイルの公式参照ドキュメントを以下に示し、以下のパラメータはいずれも公式ドキュメントで参照できます.
Application.mk公式説明
Android.mk公式説明
LOCAL_CPP_FEATURES := rtti
またはLOCAL_CPPFLAGS := -frtti
RTTI(すなわちランタイムタイプ情報)は、この依存により40 k(5%)程度の体積が増加し、できるだけ使わなくてもよい.LOCAL_CPP_FEATURES := exceptions
またはLOCAL_CPPFLAGS := -fexceptions
すなわちC++異常は,C++の異常に対する支持が不十分であるため,実際には使わなくてもよい(この依存は60 k(7%)程度の体積を増加させる).Fexceptionとrttiの依存性はndkコンパイルではデフォルトでオフであり、コードでこの2つのc++特性が使用されていない場合は、上記の2つのパラメータは無視されます.LOCAL_CPPFLAGS += -ffunction-sections -fdata-sections
LOCAL_CFLAGS += -ffunction-sections -fdata-sections
LOCAL_LDFLAGS += -Wl,--gc-sections
このパラメータは意外な喜びと言えるが,意外にも約200 k(20%)程度のヒントを減らすことができるので,このパラメータ設定の原理を簡単に検討した.
GCCリンク操作はsectionを最小の処理ユニットとし,1つのsectionにある記号が参照される限り,そのsectionが加わる.
-ffunctipn-sections
と-fdata-sections
を加えることなく全てのfunctionとdataが同一のsectionで生成される.oファイル.2つのパラメータを使用する、生成されたものをコンパイルする.oファイルは複数のsectionに分割され、各sectionには1つのfunctionしか含まれず、そのsectionの名前はfunctionの名前である.複数のsection宣言が挿入するので、自然に2つのパラメータを加えて生成する.oファイルは大きいですが、後のリンク段階では、このような分割が大きな役割を果たします.リンクフェーズでは、上記の分割操作により、リンク中にどのfunctionが使用されているのか、どのfunctionが使用されていないのかを容易にマークすることができ、このようにして使用されていないfunctionを最後に生成されたsoライブラリから取り除くことができ、生成されたsoはsectionの分割を保持しないため、上記はsectionによって生成された体積が増加するため消失する、無駄なfunctionが削除され、so全体の体積が大幅に減少します.実は全体の過程はjava proguardのoptimize過程に似ていますが、Javaには反射、ダイナミックロードなどの過程があるので、optimizeは少ないです.C++には似たようなメカニズムはないようです(C/C++に精通していないのでよくわかりませんが)、そうでないと穴もあります.
LOCAL_LDFLAGS += --icf=safe
--icf=safe
リンクパラメータを追加すると、余分なコードを削除できますが、一定のリスクがあり、インライン宣言を削除し、性能損失をもたらす可能性があります.Options That Control Optimization
ここで簡単に説明すると、Oは実行効率を最適化し、1~3から異なる最適化レベルを開くことで、コンパイル時間を増やしたり、コンパイル後のライブラリのサイズを増やしたりすることができます.具体的には、上記のサイトを参照して、1~3の最適化レベルがそれぞれどのような最適化方式を開いているかを確認することができます.O 0はデバッグモードであり、コンパイル時間を最小限に抑え、デバッグに予想される結果を生み出す.Osはsizeを最適化することを意味し,このオプションは生成コードの体積を最大限に減らすことができ,すなわち最後に動的ライブラリの体積を最小限に抑えることができる.
armのデフォルトはOsですが、x 86のデフォルトは-O 2です.ここでは設定できます.
TARGET_x86_release_CFLAGS := -Os
これによりso体積を効果的に低減することができるが,効率は少し遅くなる可能性がある.
三.プロジェクト構造レベル
プロジェクトに2つのダイナミックライブラリがある場合は、主にApplocationを変更するstlライブラリを共有する方法でコンパイルできます.mkファイルパラメータをxxx_に変更sharedは、コンパイル後に独自のダイナミックライブラリと
libxx_shared.so
stlダイナミックライブラリを生成します.これが参照のダイナミックリンクライブラリです.2つのダイナミックライブラリをコンパイルする必要がある場合は、1つのstlダイナミックライブラリを参照するだけでいいですが、Systemに注意してください.loadの場合はstlダイナミックライブラリを優先的にロードする必要があります.次に、異なるメーカーによって異なるsoが提供され、一般的にarmeabi-v 7 aはマルチ浮動小数点演算に対して比較的大きな最適化があり、nativeコードが多くの浮動小数点データ演算に関連していない場合、armeabi-v 7 aのサポートを直接取り除くことができ、armeabiのみを使用することができる.次はx 86のダイナミックライブラリですが、シミュレータの下でもarmeabiの実行に互換性があるため、x 86は必要ありません.従って、一般的な応用では、armeabiのsoはすべての機械の動作をサポートするのに十分であり、より少ないsoは応用の体積を大幅に減少させることができる.
四.最適化ツール-UPX
最後に別の考え方を提供し、UPXを使用します.
UPX-the Ultimate Packer for eXecutables
UPXは実行可能プログラムファイル圧縮器で、圧縮された実行可能ファイルの体積は50%-70%縮小されている.以前はPC時代によくシェルとして使われていましたが、もちろん修正したことがあります.これを研究する専門家がいますが、使用後、体積を20~40%減らすことができるのは、どれだけの圧縮比を設定するかによって決まります.効果は非常に良いと言えますが、公式サイトで提供されているツールを直接使うとAndroidで互換性の問題がありますが、解決できます.この不便さはもっと明らかにして、興味のある学生は自分で穴を踏んで穴を埋めましょう.
五.まとめ
最後に上記の種々の態様によりso体積が300 k程度に減少する、パッケージ化はおよそ150 k程度の体積しか増えていません(パッケージ化するとzip圧縮が行われるので)、目的は達成されました.稼働効率にはあまり敏感ではないので、ボリュームを減らしてどれだけの性能損失をもたらすかはテストしていませんが、その後はこの方面のテストを行う必要があるでしょう.