作業対象。 実際に [lib]が得られた場合、これはもはや余暇を止めることにはならないでしょう。 そのため、librsvg の関数については、rsvg-convertバイナリで cargo-bloat を実行しました。$
カーゴ 肥大化
--開ける --木箱完了開ける [optimized] 目標(s) の 0.23s 検討中 目標/開ける/rsvg -ベンチ ファイル .文言資料寸法クレート 10.0% 38.7% 1.0 MiB )librsvg 4.8% 18.8 %505.5KiB std 2.5%正規表現 … 痕跡9.8 % 262.8KiB
拍手 1.8% 7.1
% 191.3KiB渡されました … 25.8% 100.0% 2.6 ミブ .文言資料 ピース サイズ, ザ ファイル サイズ
は10.2 MiB リピート: 数字 その上それは a 結果 の当て推量.彼ら それは これ以上
100%
道徳の と 一度もない 意思 なれ。上記の出力は、
cargo bloat --open --cratesに対するものです。--open オプションは、最適化されたバイナリを生成するためのものであり、--crates は cargo-bloat にクレートサイズの概要を事実に基づいて出力するように指示します。 例として、インライン化された関数も特定のクレートの呼び出し元に印象を与えるだけなので、数字はもはや完全に役立つ必要はありません。 軽度、問題のサイズを理解するのに十分なほど適切です.この場合、librsvg クレートのコードは約 1.0 MB です.さて、凝縮する準備ができそうな一般的な関数を見つけてみましょう。 cargo-bloat が--cratesなしで賑やかである場合、特定の人物機能の寸法を出力します。 いくつかの実験の後、cargo bloat --open -n 0 --filter librsvgで仕上げました。-n 0 オプションは cargo-bloat にすべての関数を表示するように指示し、N 個の最も魅力的なものを止めることはもはや事実ではありません。 --filter librsvg は、 のインスタンスとしてではなく、そのクレートで最も魅力的な関数を出力するように構成しますstdまたはregex.$
カーゴ
膨らみ - 開ける -ん
0 --filter
librsvgファイル
.テキストのコメント素材ディメンション クレート
決定 0 .0% 0.0% 1.2KiB librsvg
librsvg::ingredient::ElementInner::オリジナル 0.0% 0.0% 1 .2KiB librsvg
librsvg::ingredient::ElementInner::オリジナル 0 .0% 0.0% 1.2KiB librsvg librsvg: :ingredient::ElementInner ::オリジナル ... 出力 過ぎ去った ... 0.0% 0.0% 825B
librsvglibrsvg::ingredient::ElementInner :: set_style_attribute 0.0% 0. 0% 825B librsvg librsvg::成分::ElementInner ::set_style_attribute 0.0% 0.0%
825B librsvg librsvg::ingredient::ElementInner
::set_style_attribute ... 出力 過ぎ去った ... 0.0% 0.0% 358B librsvg librsvg::ingredient::ElementInner
::get_cond 0.0%
0.0% 358B librsvg librsvg::ingredient::ElementInner
::get_cond 0.0% 0.0% 358B librsvg librsvg::ingredient::ElementInner ::get_cond ... その他を山積み ...
), }出力をちょっとのぞいてみたところ、検索したい「重複した」関数があることに気付きました。 ここで起こっているのは、ElementInner はジェネリックを持つフォームであり、rustc はすべてのフォーム インスタンスに対してすべてのシステムのコピーを 1 つ生成しています。 したがって、ElementInnerのすべてのプロットのコピーが 1 つあります。、ElementInner[3, 4, 5, 9, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36 (peak), 50] 用の 1 つ、完全な SVG 成分の種類のために他をヒープします。その周りのコードもかなり複雑です。 または、C から Rust への移植と予備的なリファクタリングの後、強力なクリーンアップが行われていないライブラリの共有にはもうありません。 どんな感じか見てみましょう 予備コード Librsvg は SVG ドキュメント内の XML を解析し、DOM ツリーに似たものを構築します。 ツリー自体がrctree クレート;first_childまたはnext_sibling( のような参照カウント ノードと関数があります。 。 ノードは、XML 物質、または XML タグ内のパーソナリティ コメント マテリアルを表すことができます。 ここでは、最も魅力的な物質でアピールしています。,10 L0,10 Z" fill=
「黒」/>
librsvg がそれをどのように表しているか見てみましょう.rctree内の参照カウントされたすべてのノード内*)、librsvg は NodeData 列挙型を保持します。これにより、物質とパーソナリティ コメント マテリアルが区別される可能性があります:
enum
NodeData
{ 材料(材料)、
文言資料(文字
では、 成分は、librsvg がサポートする svg名前空間内の完全な実体をおそらくおそらくおそらく区別する列挙型です:enum材料{ 丸(
フィールド<ElementInner<円 >>
),
E llipse(フィールド<ElementInner<楕円
>>), ルート(分野<ElementInner <パス>>), // ... 約50件渡されました...}
これらの列挙型のすべてのバリアント内には、ElementInner があります。 、汎用形式 para の構造体メートル。 ElementInner は、DOM に似た構成要素のレコードを保持します:struct ElementInner
<た: ElementTrait> { 要素名: QualName, 属性 :
属性,
// ... 渡されたフィールドの形式
element_impl
:T,
*)}
のために
上記の成分、この構造体は次をキャッチします:
element_name
,:を含む認証済みの名前routesvg名前空間。attributes
:(name, label)の配列この場合(d, "M0,0 L10,10 L0,10 Z")(catch, "暗い")
これらすべてのメモリ レイアウトの不可欠な側面を見てみましょう..
element_impl : 具体的な形式、Routeルート
ルートの詳細 フォームは持っていますここではもはやひどく魅力的ではありません。 またはもはや事実ではありません ベジエ パスの内部表現 .構造体
{
ルート: Rc<SvgPath>, }
初期メモリ配置 上記の列挙型と構造体がメモリによってどのように指定されているかを示します。rctree:Nodeを糸で結ぶ NodeData.をラップする)(テキスト注釈資料での説明)" src="http://viruta.org/images/generics-layout-earlier than.svg">
NodeData に割り当てられたブロックは 1 つです。 enum であり、そのブロックは enum の判別式と埋め込まれた Ingredientenum を保持します。 反対に、Ingredient列挙型には、のキャッチ判別式とレジデンシャルがあります。 )Field(つまり、ポインター)。これは、そのバリアントのすべてが実際に単一のフィールドを保持するためです。そのフィールドは、ElementInner の割り当てに対応します、それ自体にRoute が組み込まれています struct.コンポーネントの名前やその属性のような XML イズムを保護するためのフィールドがElementInner
、もはやIngredientにはありません。 しかしもっと重要なのは、 ElementInnerにはかなり多くのシステムがあります:impl
<た: ElementTrait> ElementInner<た>
{ fn オリジナル(...) -> ElementInner<T> { // 総束の建物
} fn 要素名
(&自己 ) -> &QualName {
... } fn get_attributes(
&自己) -> & 属性 { ... }
// システムの形式の束 }
あるいは、実際にはこれらすべてのシステムの 1 つだけがelement_impl を消費します: T アリーナ! つまり、それらはすべて、すべての成分の種類に共通する製造中止の問題です。element_implアリーナを真に扱う正しいプロットはです。 :diagram() プロット、そしてそれが行う最も魅力的なことは、具体的なフォームのの実装に委譲することです::ダイアグラム().一般的なフォームから挿入する
では、周りの問題を消しましょう。 これは私がしました:
ターン
enum Ingredientをstruct Ingredientに変換し、フィールドはすべての種類の原料に頻繁に使用されます.所有する Ingredient.element_data アリーナ.... .. これは ElementData の形式で、実際にはサポートされているすべての成分の種類を認識している列挙型です。 ここにはジェネリックを持つ種類はありません: 構造体
材料
{ 要素名: QualName, 属性:
1属性,// ... 渡されたフィールドの形式 要素データ: ElementData, }enum ElementData
{丸(分野 <サークル>
), 楕円(フィールド<楕円>), ルート(分野<パス>)、 // ... }.テキストの発言資料メモリレイアウトは次のようになります:割り当てが1つ増えますが、これでコードサイズが変わるかどうか見てみましょう.コードサイズ私たちは、.textual remark materialピースの寸法を把握したいと考えています。 ELFファイル
# 成熟 $ objdump --piece-headers ./target/open/rsvg-bench Idx ディメンション VMA の決定LMAファイルOff ALGN 15 .textual ermer Material 0029FA17 000000000008A060 00000000000008A060 0008A060 24(2750999バイト)元のコードは 186912 バイト小さくなっています。 もはや地球を揺るがすものではありませんが、cargo-bloat はもはやさまざまな記録に触れないため、単形化される動機を持たない重複した機能を示しません.
成熟した:
$貨物 肥大化 - 開ける --クレート
ファイルディメンション クレート10.0%
38.7%
.0MiB librsvg # トレースオーバー25
.8% 100.0% 2.6MiB .テキストの発言素材 ピース サイズ、 の ファイル サイズ は 10.2MiBファイル .テキストのコメント素材オリジナル:
$
貨物
肥大化 --open --クレート
寸法 クレート 9 .2% 37 .5% 939.5KiB librsvg 24.6% 100 .0% 2.4MiB .テキストの発言素材 ピース サイズ、 ファイル サイズ は 10 .0MiBキャッシュの局所性を使用するよりも、非励起のサポートが必要なコードははるかに少なくなりますが、魅力的な関数は、もはやホット ループである必要はありません。 librsvg のほぼすべての時間は、ラスター化のために Cairo で、合成のために Pixman で費やされます。 動的ディスパッチ 完全な具体的な種類 ( Circle
、ClipPath、およびヒープその他。)ElementTraitを実装します。これには、ダイアグラム()プロット、それが上記のカテゴリで表示されなくなったと仮定しても。 これは、librsvg にとって最も役立つものです。 Fieldの活用というのは、そこではフォームの消去はやや厄介なものになるからです。 最後に、コードは、ElementData
のバリアント: