ラベル 備忘 の投稿を表示しています。 すべての投稿を表示
ラベル 備忘 の投稿を表示しています。 すべての投稿を表示

2016-11-21

macOS版xibロード関数

...なんか管理が大変だったので、Hatenaから黙って引っ越しました。

macOSでもxibからロードする関数を立てておきたかったのと、swift3対応のため備忘。
システム上、高頻度の利用シーンがあり、3まで来てある程度Appleの設計もこなれて来たので、ライブラリとして書いてみた。

いつも思うが、Appleはxibに対して冷めたい。
設計者からすれば、もうnibみたいなレガシーシステムはデベロッパから切りたいので、何も考えずstoryboard使えということだとは思うが。
じゃないと、initWithNibnameコンストラクタ廃止して、NSArrayにxibから生成されたNSViewつめてインスタンス返すようなわかりにくいパスだけ残すなんて設計にしないと思う。

ただ、storyboardだとカスタムviewを単体で定義できないし、programableにviewController上のviewにバインドしたいときとか、特定の場所に特殊なviewを出したりしたいときにはやっぱりどうしてもまだ使う。
storyboardの中では結局xib使ってるんだけどね。。。複雑になり過ぎてユーザに開放できないわということだろう。
ドキュメンターに定評のあるAppleも説明コストのほうがかさむと判断したのかもしれない。
好意的に見ると、xib使わなきゃできないようなアプリケーションは、仕様が複雑になりすぎてるか、やりたいことを見失ってるから気づけ—ということかも知れない。
確かにiOSでiPad proをメインユースにとでも想定していないならそうかも知れない。
小さい画面であれもこれもできますと凝るほど、ユーザには負担がかかるんだよという、非常にわかりにくいメッセージだと考えると個人的には非常に納得できる。

で、Apple Developerより先に引っかかったのがこれ。


Load a UIView from nib in Swift


これはiOS向けのレポートだけど、いくつか比較した中では一番妥当に感じたので、ここからぱくってみる。
iOS版は数年前にObjCで書いたんだけど、iOSとmacOS、上物エンジンはさすがに若干違うので一部書き換えてある


assertはvendorが扱いを変えたり、個人の実験考察に誤りがあって棄却が必要になったとき、対応できるよう入れてあるモノ
ちなみに、何度か遭遇したけど何らかの条件下ではunsafePointerの順番ひっくり返るんだよなぁ。。。。
原因となり得る要素が多すぎて確信できる答えはまだ出てないけど。
たいていのレポートはfirstで決め打ち取り出ししているけど、なにか記述していない前提があるんだと思う

また無駄な処理を。。。といわれそうだけど、世界中に配るモノの一部なので一応保険に保険を掛けた構造といことで。
道具箱にframeworkとして入れておいてもいいんだけど、Categoryみたいにimportで切り分けられない。。。
一発間に冗長クラスを介在させるとかも考えたけど、それはそれでめんどくさいのでprox実装にしました。
あと、private gistをblogに貼ったら、robotがどう振る舞うかの実験もかねて。


-- 以下おもいつきの余談 --
それにしても、autoLayoutは複雑なCG座標系の概念でさえ、いかにも設計せず実装できまっす!みたいな顔をしているし、それを使ったstoryboardはXML生成の最適化処理にバグを持ってる気がして大いに気にくわないんだけど、コードで全部書くとパラメータがOOのあおりで意識しないところで書き換えられるなんてことは少ない反面、表示位置確認くらいの簡単なデバッグなんかに時間がかかるというジレンマ。。。
storyboardなりxibなりを書き換えたあとは、DerrivedDataごとclean(Xcode: Opt + ⇧ + ⌘ + k)が癖になってしまった。


概念上でもレイヤで完全に分離することで、利用者へ出しているIFをコントロールしたい、xibはもうカプセルに囲い込んで、xibのIFはstoryboardを通してしか使えないようにしたい現れなのかと。
余計な仮定はオッカムの剃刀でそぎ落とせ、KISSの法則も同じようなことだけど鉄則なので解らないでもない。
興味を持って考えるべきことはなるべく最小セットにとどめないと、思わぬ方向から要求が来たときに致命的な制約がかかるので。

ただ、やったらめったらそげばいいというモノではなくて、一貫性をもって取捨選択していく。
この一貫性というのが設計の本質といっていい(と私は考える)。

誰かをディスるわけではないが、よくS/Wに何百人月も投入して失敗するケースと、研究室やらベンチャーの少人数で高い生産性みたいな話があるけど、実はこれ当たり前の話。。。

だって、
関わる人たちの考えていることや意見の一貫性は、関わる人数が増加傾向にあるほど複雑化して損なわれる確率が高いのは想像にたやすい。
逆に少人数で、輪講なんかで同じ主張の本を読み、生活コンテキストやらイデオロギーやらフィロソフィーがある程度そろっていると、それぞれ違った課題を抱えていても、最初に思いつく解決アプローチは皆同じだったり、細かい設計まで言わなくても納得できる品質レンジに収まってたりして一貫性は保たれやすい
から

後者は私の仮説込みだけど、Blooksが人月の神話でとうの昔に言ってたことの受け売り(個人的にこう解釈している)です。

失敗経験則ではたいてい矛盾する2つ以上の課題を同時に解決しようとしてるんだけど、不思議なことに課題分析に工数を使うべきとの意見だけはついぞ出てこず、そして倒れるというアンチパターン。。。なぜだろう。

2016-11-08

default引数

ぱくりと言われようと、便利なモノは便利なわけで。


swiftもPythonと同じようにdefault引数設定ができるわけだけど、Appleのドキュメントにはあまり明示的にこう使えと書いていないので、備忘。


 


まず、Pythonでもswiftでも私がdefault引数をよく使うシーンは、リファクタリングの時。


例えば、ある関数が外部とのIFとして立てられていたとする。



internal func hoge( ) {....



 ただ、今この瞬間から似て非なる新仕様をぶち込まれ(みたくなっ)て、hogeに引数を待たせたバージョンがあると、全体設計として非常に管理しやすくなるじゃんという状況になったとしよう。


 


Javaなんかだったらこうする。



// テストが通っているのでこれは凍結


internal func hoge( ) {....} 


 


// 新IFとして、新規コードからはこれを呼ぶ


internal func hoge(abc : [Abc]) {...



一般にコンパイラはシグニチャで関数宣言を見分けるから。


これはswiftでもPythonでも同じようにできたのでサブセットして道具箱に入れておくといいかもしれない。


 


ただしこれが、デフォルト引数を持っている言語だとこう表現できる



 internal func hoge(_ abc: [Abc] = []) {


    if abc.isEmpty {


        // 昔のhoge()を一文字違えず埋め込むか、sub関数管理にする


        ....


    } else {


        // 新規コードからのIFとして引数版のhoge処理


    }



 Java版もそうだけど、これらの何が嬉しいのかというと、既にコード中にアホほど存在するcaller hoge()はびた一文書き換えなくてすむし、挙動変化が管理しやすい。あと従来関数と機能のfusionがしやすい。


 


なおJava版のほうも決して悪いわけではない。


ファイル長に寛大で、関数単位でバージョン管理するのがしっくりくるようなチームならむしろJava版の方が合っているでしょう。こっちもfusionするならhoge(abc:...)の中で条件によってhoge()を呼べばいい。


要はwrapperで統一IFにする。


 


私的になるたけ1ファイルは低ノイズで主張点だけに絞ってざっと読み込みたいほうなのと、Python流が苦ではないので後者のほうが好きというだけの話。


大事なのは、十分テストされたといえる既存コードは可能な限り保存して、新規コードとは分離できるところ。これがOOPの基本嬉しいこと。


なんなら引数に関数を放り込むIFにして、default nilにしておいてもいい。


 


こうするのがややこしくて、少人数で作ってて、連絡コストがそう高くないなら、hogeをnewHogeと改名する方向でもいいかも



// internal func hoge ...


available(*, deprecated: X.x, message: "ツカウナキケン")


private func hoge() {...}


 


internal func newHoge(....



こちらの方が、新IFからは確実に使えなくなり境界がはっきりするので、状況によっては一貫性を保ってテキスト全置換でいっそ変えてしまった方が理想。


コンパイラは関数名なんざ数列にしか見えてないので。


 


ただし文字ヅラに騙されて変なミス入れ込むのが人間。これが現実。


特に私は間抜けなので、よく全置換をやって、かつコメントも何も残してないもんだから、何時間もうんうん唸って(泣きながら)ふて寝したあとに、そういえばそうだった!とさもエウレカを得たような気分になって正常軌道に戻すことはあるけれど。。。でもそれって気をつけていれば一切支払わなくていいコストだったわけで、結局損失なのでよほどじゃない限り避けるようにはしている。


 


 ちなみに、availableに頼るときはprojectで一貫したwarning controlがされていて、意識的にwarningの見分けが付く状況でないと意味はない。


砂丘の砂粒ほどwarningが存在する中で、"ツカウナキケン"を意識して見わけるのはもはや拷問。


AppStore バイナリサイズ 調節備忘録

このところ概算2週にひとねたのペースでMASにコードを送り込んでいるんだけど、ん?と思うところがあったので、備忘。


 


ScalaやHaskellで一度でも成立したモノ書いちゃうと、メンテナンシーやらテスト単位の取りやすさから、もうその辺の手続き型の言語は使え(いたく)なくなるわけで。。。


型を自由に作れない、OSなんかの環境とシステムバウンダリが明確に分けられていないなんちゃってHaskellとはいえ、なるべくpureにswift3なんぞで書いてます。


が、いざレビューが通ってMASに公開されたらバイナリサイズが思ってたより無茶苦茶でかい。


 


MASだからサイズのせいでdownloadが伸びない何てことはあまりないので、別に放置でも良かったのだけど、購入者の中には気にする人もいるので、今回対応してみました。


 


気づいたきっかけは、最近出したAppSummonerというwindowエクスプローラアプリなんだけど、最初はあまり気にせずv1.0をMASレビュー通して、公開されたらなんかでかい。。。アプリページのサイズ欄には22.2MBと。


 


気になるので、XcodeのArchiverからiTCに送ったアーカイブをローカルに吐かせてサイズを見ると。。。ほんまや。。。


 


管理単位明確化のために、MASにあげているスクショやらdescriptionやらもgitで管理していて、一緒にバイナリにほうり込まれているためそれら全部外して再コンパイルするも、19MB...


 


で、.appのContents以下を見てみると、Frameworks以下が一律8MB。そして、使ってもないCoreDataのdynlibなんかも同梱されている。。。launcharも本体も全く同じ構成だったので恐らくはテンプレ。


いや、各ライブラリ、ブリッジとか言うサイズの域を凌駕してるし、Framework設定の意味はどうした。


 


Xcode7ではそんなことなかったのだが、どうやらXcode8+swift3では、SwiftのdynlibをFrameworksに何でもかんでもぶち込むようになっているご様子。


ditributer側の立場に立つと、必要なlibだけフィルタかけるような細かい変則ルールを持ち込んで、iTCへ"動かんのじゃぁ"と山ほどクンロク入れられるよりは、何も考えず全部盛りにして、私のようなあほがポチポチボタン押しても、とにかく最低限動くものとしてビルドするように設計した方が、対応コストが安いという計算があるのだろう。


こうしておけば、確率的にはその中のサイズを気にするクラスタのみが文句を言ってくる、とりあえず動いてはいるので、損益補填とかいう話にもなりにくい。


確かに気持ちはわかる。。。わかるが!というお話。


 


今回Xcode8にあげたのと、login起動を仕込むには、app形式をもう1個Library/LoginItemsに内包しないといけないので、計算上ただしい。


つまり、私が書いたコードはカスほどに小さい。


まぁ、昔のフロッピーのCMよろしく、1MBで図書館一棟ぶんの文字情報を格納できるわけで、少々手が早くてもひとりで作ってて図書館以上の情報量を2週で生産できるわきゃないのだけど。


 


閑話休題


で、ここからが解決編、といえるかどうかはあやしいので、私はこうした編。


今回、いかんともしがたい課題が3点ほどある。


1. launchAtLogin機能を仕様上残すには、launcharアプリを内包させないといけない


    - つまり、ひとつのアプリなんだけどなかのひとは2人必要。


2,3年前はlaunchctl直接いじって突っ込めたんだけど、SANDBOXのせいでできなくなった。Appleとしては、疑わしいpermissionは全不許可ということだろう。


今回のappでもレビュー中、それroot扱いなの?ってところで一悶着あったし。


 


2. swiftコードのFrameworkはdynlibなので、app形式を取っている以上必ず手の届く所に置かないといけない


    - 同じモノを、各構成appでいちいち持たせないといけない。DRYではないが、なんらかそうせざるを得ない理由が出荷前にあったんだと思う。


 


 3. (これは個人的に) コンパイルオプションとかはなるべく変えたくない


    - コンパイルオプションはもはや人間が管理するもではないと思っているし、toolchainのdistributerはここを変えて配布する権利をがっちり握っているので、オッカムの剃刀的な考えから、いじるとしても必要最低限にとどめたい。


自分の間抜けさから、まぁことあるごとに忘れるし、忘れる可能性を秘めた工程は自動化するか、それができなきゃ排除(興味を持たないように)するポリシーでうちはやってます。


 


特にコンパイルオプションに関しては明後日の方の設定一つ変えられると、全く予期してなかった挙動を示す確率が非常に高い。 


これはむかーし学生時代、狂ったようにintelのブートストラップ書いてた頃のgas、gccから得た教訓。LLVMといっても、外部とのIFはほぼgccだし。


中間抽象化言語導入するなら、どっかで損切りして捨てればいいのにといつも思う。。。


 


 で、上記3条件のandを取ると、最も低コストな解決策は、launcharのswift捨てだとデザイナーとして今回は判断しました。


だって、login起動判断に必要なlaunchar-helper.appはAppDelegateとmain.storyboardしか持たないし、起き上がって判定かけたら直ぐterminateするカゲロウみたいな存在だから。


 



- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
BOOL running = NO, active = NO;
NSString* mainAppBundleId = @"xxxx.xxxx.xxxx";
    NSArray* applications = [NSRunningApplication   runningApplicationsWithBundleIdentifier:mainAppBundleId];
    if (applications.count > 0) {
        NSRunningApplication* app = applications.firstObject;
        running = YES;
        active = app.isActive;
    }

    // Quit
    if (!running && !active) {
        NSString* scheme = @"xxxx://"; 
        NSURL* applicationURL = [[NSURL new] initWithString:scheme];
        [[NSWorkspace sharedWorkspace] openURL:applicationURL];
    }

    [NSApp terminate:nil];
}



これだけのために、いつ正常対応するか解らないundocumentedな仕様と、各社似て非なる仕様を持つダイナミックリンクライブラリの調査をする気にはなりませんわ。


何が起きているかと、対して何がしたいか、何ができるかがはっきりしてから、10分で終わった。


 


こういうときswift/ObjC両方書けるって便利。


あ、あとXPC書くにはまだObjC書けないと辛いか。


 


もう!functor(関手)が実装されていて、環境依存から解放されてこそのFPでしょうが。


誰でもいいから、この超環境依存FP早くなんとかしてくれぃ。


Swift解体新書 その1 ニーモニック確認環境

通常ならswift de classDrivenの続きのはずだが、一旦中断してクロスコンパイル環境から解体することにした。


 


なぜなら、WWDC2015 - 408にむっちゃ恐ろしいことが発表されていたから...


忙しさに負けて、チェック抜け...間抜け


 


詳しい解説はネット上和訳が出回っているのでここでは割愛するが、これによるとAppleは(少なくともswiftのPLであろうabraham氏によれば)、



継承構造は継承と言いつつ資源配分問題になると、


途端に厄介な問題を抱え込むので、そこは正式にはサポらんよ。


Protocol-Orientedで書いてね、てへぺろ



 と、いろいろ誤解を招く訳しかたをしたが、言いたいことはclass/struct/enumで継承使って親クラスの資源を無考で使えると思うなよ、そもそもabstractみたいな概念自体わしゃ好かん。


そんな細かいこというやつは、protocol+extension(これは明言してないけどObjCならprotocol+Category)で記述しろと。。。


 


これつまり、Appleはダブルチェックロッキングとかめんどくさいこと考えたくないから、独自路線を行きます。JavaやらC++と同じ概念で継承を考えると細かい所で違うものになるから痛い目見るよと(独自解釈)。


Alan Kay先輩とBjarne Stroustrup先輩の視点差に関して、ObjCでは業界トレンドに任せてごまかしていたことをついに認めたコメントなんだと思う(独自解釈)。


 


 


ということで、基本に立ち返る。


言われてみれば、swiftcも1.1時代にobjCとたいして変わらんことしか確認せず、まだ具体的には触ったことがなかったのでちょっと触ってみた。


最終Goalはニーモニックでクラス階層構造を確認すること。


この記事のGoalはとりあえず.o, .sファイル相当の内容確認ができるところまで。


 


---


閑話休題。では、以下本日のゴールに向かって順番に。


まずはswiftc  これがないとはじまらない。


これはxcodeなり、swift.orgからなりtoolchain取ってくれば入っているはず。



$ swiftc -v


Apple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29)


Target: x86_64-apple-macosx10.9


 



 素知らぬ顔して、clangがLLVMのフロントにあることを告白している。


前に見たときこんなの出力してたっけ? そんななわかりきってるので、今日は触れない。


 


次、binutils これは、swiftc -SなりxcodeのDebug Workflow > Dissassembly でも代用はできるけど、サマるんだったら入れておいて損はない。


macならbrewで入れればまず問題は無いだろう。EL-CapitanのSIP問題に引っかかったら、ググって自己解決してください。


Linuxならapt-get


ここではgobjdumpが使えれば必要十分。


 


解析ツールはそろったので、次それぞれの使い方に進みます。


swiftc


これは、コンパイルするのに使う。


とりあえずの試料としてざざっとLoopテストを書いてみた。


asの確認をするのであれば平でfuncを書いた方が分かりやすいが、とりあえず。。。あとprobe効果なんかもほぼ考慮していないので、試料内容に関する突っ込みはご容赦いただきたい。


もちろん配列がL2に入るかなんて、とりあえず数回して誤差詰めとけ位にしか考えていない。


 



 


 Playgroundで書き出したが、XcodeのREPLの起こし方がアホで、編集の度にインタプリタ実行をかけ直そうとする。Loop試験のような何百万もループ回すコードだとさすがに固まるため、途中でコピペしてAtomでやった。


たぶんなんらかXcode設定はあるかと思うが、どちらにせよREPLだとs/n比低すぎてLoopの試験にはならんので、結局コンパイルする予定だからあまりこの場では深く考えないものとする。


あくまでGoalはニーモニック確認


 


 で、この試料をswiftcで実行ファイルにビルドする。



$ swiftc -O -sdk $(xcrun --show-sdk-path --sdk macosx) -o aStruct.out for-inExp-struct.swift



どうやらエントリポイント(main)はfoundationにリンクすることで、バイパスしているらしい。普通はこれで走るわけはないので注意。


 


今どきはアルゴリズムが成熟しているため、-Oを付けた方がより実測値に近づくがこれはお好み。 


--sdkはiphoneos(全小文字)にすればiOS向けにクロスできるけど、objdumpが欲しいだけなので、x86でいいだろう。


試料でUIKitではなくFoundationをimportしているのはそういう理由。


 



$ aStruct.out



でターミナルなんかから直接実行されるファイルができあがる。


もろもろ面倒なので、script書いた



 


OUTPUTはこんな感じ



StructTrials


opened for : av = 59.826675, total = 5982.667542


closed for : av = 26.785226, total = 2678.522583


for-in : av = 27.640303, total = 2764.030273


zipIndexing : av = 61.732862, total = 6173.286194


while : av = 59.986423, total = 5998.642334



zip、whileはなんとなく想像ついたけど、開区間(...) 閉区間(..<)でこんな違うんだ。


zip同様Loopごとに余計な命令群でも挟まってるんだろう。この辺は、dumpoファイルを元に解析予定なので後述(たぶん)


Pythonがrange使ってる理由がなんか解る気がする。


 


AppleDevにはfor-inで書けとしか書いてなかったんで、swiftでは実質高速列挙なんだろうな。ObjCではenumerate...blocksと挙動が違ったような覚えあるけど。


ほんとはfor(;;)も比較対象に入れたかったけど、syntaxに無かったようなので断念。


この辺の考察も想像の羽を広げてる場合ではないので、後述。


 


gobjdump


gnuなのでgas, gcc同様、頭にgが付く。



gobjdump -S -d aStruct.out > objStruct.txt



こんな短いコードだけど、FPテクを使ってるからか全貌はかなりの長さ。


以下、callqで飛んでいる興味のあるところだけだらだらコピペする



0000000100002090 <__TFV7aStruct7runTest11letUsLoopinfT_T_>:
100002090: push %rbp
100002091: mov %rsp,%rbp
100002094: push %r15
100002096: push %r14
100002098: push %rbx
100002099: push %rax
10000209a: mov %rdi,%rbx


// reset()
10000209d: movq $0x0,0x4b10(%rip) # 100006bb8 <__Tv7aStruct5totalSi>
1000020a4: 00 00 00 00


// self.myFunc() のはず
1000020a8: callq 100005314 <__ZL20fixStringForCoreDataP11objc_object+0xc9>
....



__ZL20fixStringForCoreDataP11objc_objectは0x10000524bとあるので、offset0x9cで


0x100005314へ。



100005314: jmpq   *0xdee(%rip)        # 100006108 <__ZL17_load_method_type+0x267> 



 



0000000100006108 <__DATA.__la_symbol_ptr>:
100006108: in (%dx),%al 


...



うーーん、anonymous関数ポインタのハンドリング...で、これ以上読み進める自信が無いので、すこしFP の手を緩める。


FPでは値も処理も区別しないため、何段階かアプローチはあるのだけれど、MAXひよってこうした。


 



もはや、関数ポインタを即値で代入しているだけで、何がしたいのかよくわからないが、おかげでdumpファイルが100K近く小さくなってくれた。


つまり固定値ラベルを追えばいいだけに。


 


いや、目的を考えたら、かっこつけずに初手からこうすべき話なのは解っていたのだが。。。FP+OOP脳に邪魔された。


 


stack操作はノイズだとして、各こういった対応になる


closed for -> av = 26.785226


for i in 0 ..< testValues.count {
        total += testValues[i]
} 


 


0000000100002200 <__TF7aStruct6test01FT_T_>:


....


....


10000220b: mov 0x10(%rax),%rcx


10000220f: test %rcx,%rcx


100002212: je 100002235 <__TF7aStruct6test01FT_T_+0x35>


100002214: mov 0x499d(%rip),%rdx # 100006bb8 <__Tv7aStruct5totalSi>


10000221b: add $0x20,%rax


10000221f: nop


100002220: add (%rax),%rdx


100002223: mov %rdx,0x498e(%rip) # 100006bb8 <__Tv7aStruct5totalSi>


10000222a: jo 100002237 <__TF7aStruct6test01FT_T_+0x37>


10000222c: add $0x8,%rax


100002230: dec %rcx


100002233: jne 100002220 <__TF7aStruct6test01FT_T_+0x20>


100002235: pop %rbp


100002236: retq 


100002237: ud2 


100002239: nopl 0x0(%rax)



 オーバーフローでオペコード例外とか。


インデクスはrcxに最大値取ってからdecしてるけど、一般的な条件分岐jmpらしい。全てがレジスタで完結しているし、妥当な最適化がされているから速い。


 


open for ->  av = 59.826675


let max = testValues.count - 1
for i in 0...max {
        total += testValues[i]
}


0000000100002240 <__TF7aStruct6test02FT_T_>:
....
100002244: mov 0x4965(%rip),%rax # 100006bb0 <__Tv7aStruct10testValuesGSaSi_>
10000224b: mov 0x10(%rax),%rcx
10000224f: test %rcx,%rcx
100002252: je 100002287 <__TF7aStruct6test02FT_T_+0x47>
100002254: dec %rcx
100002257: xor %edx,%edx
100002259: nopl 0x0(%rax)
100002260: mov 0x4951(%rip),%rsi # 100006bb8 <__Tv7aStruct5totalSi>
100002267: add 0x20(%rax,%rdx,8),%rsi
10000226c: mov %rsi,0x4945(%rip) # 100006bb8 <__Tv7aStruct5totalSi>
100002273: jo 100002283 <__TF7aStruct6test02FT_T_+0x43>
100002275: cmp %rdx,%rcx
100002278: je 100002285 <__TF7aStruct6test02FT_T_+0x45>
10000227a: inc %rdx
10000227d: cmp 0x10(%rax),%rdx
100002281: jb 100002260 <__TF7aStruct6test02FT_T_+0x20>
100002283: ud2
100002285: pop %rbp
100002286: retq
100002287: ud2
100002289: nopl 0x0(%rax)



このレベルだと、letによる参照透過性の違いは見られない。 


そして、cmp %rdx, rcxあたり怪しすぎる。rcxとrdx二本ふりまわしてdec,inc繰り返してるのか。


retqの前にud2が来てるのもなんかおかしい気もするが、こういうものだっけ?


-Oつけてもこれだと言うことは、..<はなんらか展開しくってるように思える。


実行速度も出てないし、開区間指定はまだこなれていないのかもしれない。


そもそも、for文は昔から0からはじまる閉区間なので、需要があるまではあまり力入れてないのかも


 


for-in  -> av = 27.640303


for v in testValues {
        total += v
}


0000000100002290 <__TF7aStruct6test03FT_T_>:
....
100002294: mov 0x4915(%rip),%rax # 100006bb0 <__Tv7aStruct10testValuesGSaSi_>
10000229b: mov 0x10(%rax),%rcx
10000229f: test %rcx,%rcx
1000022a2: je 1000022c5 <__TF7aStruct6test03FT_T_+0x35>
1000022a4: mov 0x490d(%rip),%rdx # 100006bb8 <__Tv7aStruct5totalSi>
1000022ab: add $0x20,%rax
1000022af: nop
1000022b0: add (%rax),%rdx
1000022b3: mov %rdx,0x48fe(%rip) # 100006bb8 <__Tv7aStruct5totalSi>
1000022ba: jo 1000022c7 <__TF7aStruct6test03FT_T_+0x37>
1000022bc: add $0x8,%rax
1000022c0: dec %rcx
1000022c3: jne 1000022b0 <__TF7aStruct6test03FT_T_+0x20>
1000022c5: pop %rbp
1000022c6: retq
1000022c7: ud2
1000022c9: nopl 0x0(%rax)



あーなるほど。testValuesの次アイテムを取り出すのに、raxをアドレスぶんオフセットかけてるのか。 closed forと極めて似ている。処理時間も近似している。


 


while -> av = 59.986423

var test = testValues.count - 1
while test > 0 {
        total += testValues[test]
        test -= 1
} 

<__TF7aStruct6test04FT_T_>:
1000022d0: push %rbp
1000022d1: mov %rsp,%rbp
1000022d4: mov 0x48d5(%rip),%rax # 100006bb0 <__Tv7aStruct10testValuesGSaSi_>
1000022db: mov 0x10(%rax),%rcx
1000022df: cmp $0x2,%rcx
1000022e3: jb 100002317 <__TF7aStruct6test04FT_T_+0x47>
1000022e5: test %rcx,%rcx
1000022e8: je 100002315 <__TF7aStruct6test04FT_T_+0x45>
1000022ea: dec %rcx
1000022ed: nopl (%rax)
1000022f0: mov 0x48c1(%rip),%rdx # 100006bb8 <__Tv7aStruct5totalSi>
1000022f7: add 0x20(%rax,%rcx,8),%rdx
1000022fc: mov %rdx,0x48b5(%rip) # 100006bb8 <__Tv7aStruct5totalSi>
100002303: jo 100002315 <__TF7aStruct6test04FT_T_+0x45>
100002305: dec %rcx
100002308: jo 100002315 <__TF7aStruct6test04FT_T_+0x45>
10000230a: test %rcx,%rcx
10000230d: jle 100002317 <__TF7aStruct6test04FT_T_+0x47>
10000230f: cmp 0x10(%rax),%rcx
100002313: jb 1000022f0 <__TF7aStruct6test04FT_T_+0x20>
100002315: ud2
100002317: pop %rbp
100002318: retq
100002319: nopl 0x0(%rax)
100002320: push %rbp
100002321: mov %rsp,%rbp
100002324: push %r14
100002326: push %rbx
100002327: mov 0x45d2(%rip),%rbx # 100006900 <__ZL12demangleLock+0x40>
10000232e: test %rbx,%rbx
100002331: jne 1000023b9 <__TF7aStruct6test04FT_T_+0xe9>
100002337: mov $0x20,%edi
10000233c: mov $0x7,%esi
100002341: callq 1000054b8 <__ZL20fixStringForCoreDataP11objc_object+0x26d>
100002346: mov %rax,%rbx
100002349: mov 0x3cd8(%rip),%rax # 100006028 <__ZL17_load_method_type+0x187>
100002350: mov %rax,(%rbx)
100002353: mov 0x4496(%rip),%rax # 1000067f0 <__TMLFT_T_>
10000235a: test %rax,%rax
10000235d: jne 10000237e <__TF7aStruct6test04FT_T_+0xae>
10000235f: mov 0x3cda(%rip),%rsi # 100006040 <__ZL17_load_method_type+0x19f>
100002366: add $0x8,%rsi
10000236a: mov $0x1,%edi
10000236f: mov %rsi,%rdx
100002372: callq 100005488 <__ZL20fixStringForCoreDataP11objc_object+0x23d>
100002377: mov %rax,0x4472(%rip) # 1000067f0 <__TMLFT_T_>
10000237e: mov %rax,0x8(%rbx)
100002382: movq 0x3ca6(%rip),%xmm0 # 100006030 <__ZL17_load_method_type+0x18f>
100002389: pshufd $0x44,%xmm0,%xmm0
10000238f: movdqu %xmm0,0x10(%rbx)
100002394: xor %eax,%eax
100002396: lock cmpxchg %rbx,0x4561(%rip) # 100006900 <__ZL12demangleLock+0x40>
10000239d: mov %rax,%r14
1000023a2: je 1000023b9 <__TF7aStruct6test04FT_T_+0xe9>
1000023a4: mov $0x20,%esi
1000023a9: mov $0x7,%edx
1000023ae: mov %rbx,%rdi
1000023b1: callq 1000054be <__ZL20fixStringForCoreDataP11objc_object+0x273>
1000023b6: mov %r14,%rbx
1000023b9: mov %rbx,%rax
1000023bc: pop %rbx
1000023bd: pop %r14
1000023bf: pop %rbp
1000023c0: retq



やめてくれ!もはや見る気がしない。zipはfuncに立てるのを忘れたが、なんか皆察しの通りかと。


 


objdumpからニーモニック人力評価のための環境構築というGoalは満たせたし、


長くなりすぎて何がしたいのかが不明瞭になってきたので、今日はこの辺にしておこう。


 


 気が向いたら次は今回大幅に日和ったラムダ式の追い方か、いきなり核心を突いて継承とprotocol+extesionの違いを見てみよう。


Cocoaで統計処理 四分位数実装編

Objective-C, 仕様検討, 個人的見解, 思案中


元OS屋から見て普通に考えるとCocoaでは演算高速化のため、NSExpressionによる統計関数を用意している(のだと思う)


なぜなら、開発サイドから見ればそうすることにより



  • 開発者が独自で変な実装しだす確率を下げる (テストしてないところをがんがんほじくり返されるから。何が起きるか解らないため、提供者としてはむっちゃいや。特に演算処理は高速化を容赦なく求められるところなので、開発者は1命令でも削りたくてうずうずしている)



  • 自分たちの都合に合わせて、実装を自由に変えてリリースできる (当時ああは言ったけど、経営者の気が変わったときとか、頭のおかしいエンジニアが新製品や論文の売り文句に騙されて昨日と全く違った実装をこっそり入れてリリースしたくなったとか。)

  • なんかややこしいH/W仕様を使いたくなったらベンダー最適化し放題  (リリース時は時間に追われ、仕様もよくわかんなかったけど、よくよく見たらむっちゃ便利機能が見つかった。よくある。本当に。)


なんて戦略が取れるから。


しかしググった感じ、iOSではNSExpression#expressionForFunction@”median:”は使えない?なんてレポートもちらほら…いや、中央値出せなくてpercentile出すのはめんどくさい。なにより統計コードの天敵は計算ミス。


 


中央値算出くらいなら奇数サイズなら配列の真ん中、偶数サイズなら中央2項の算術平均取るだけなので、簡単でバグも入りにくいから自分で書いてもいいけど、sciPyが使えれば演算速度以外のほぼ全ての問題は解決するので、iOSへのPython組み込みとのProsVSConsに関しては非常に悩ましい&#12290 ;


ちなみに、自分の間抜けさを熟知しているプログラマほど自分で書くことを避ける。なにより、世の中で既に誰かが書いてて、公開されてて、かつ動いてるコードを僕がスクラッチから起こす必要はどこにもない。


 


とも思いつつ、だめもとで



NSExpression#expressionForFunction@”median:” argumentsSmilie: :(NSArray*)…



使ってみた。



—– [XXX:XXX] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[theObject compare:]: unrecognized selector sent to instance 0xXXXXXX’



いやいやいや…自分の間抜けさを実感させられただけでした。このログはtheObjectを直比較してるので、どの属性で比較したいか解らんと怒られてます。


それだけ?使えんじゃん!


 


ちなみにiOS8.4 SDKのヘッダにはこうコメントされている



+ (NSExpression *)expressionForFunctionSmilie: :(NSString *)name argumentsSmilie: :(NSArray *)parameters;
// Expression that invokes one of the predefined functions. Will throw immediately if the selector is bad; will throw at runtime if the parameters are incorrect.
// Predefined functions are:
// name parameter array contents returns
//————————————————————————————————————————————-
// sum: NSExpression instances representing numbers NSNumber
// count: NSExpression instances representing numbers NSNumber
// min: NSExpression instances representing numbers NSNumber
// max: NSExpression instances representing numbers NSNumber
// average: NSExpression instances representing numbers NSNumber
// median: NSExpression instances representing numbers NSNumber
// mode: NSExpression instances representing numbers NSArray (returned array will contain all occurrences of the mode)
// stddev: NSExpression instances representing numbers NSNumber
………. 



 つまり、公式に記述のあるOSX 10.5 laterにくわえ少なくともiOS8.4では最頻値まで使えるんじゃないか?


インチキ英語しか身につけてないので読み飛ばしただけかもしれないけど、だったらそう書け…


 


アルゴリズムとしては



  1. CoreDataから全アイテムをソートした配列にして取り出す

  2. 全配列から中央値出す -> 2Q

  3. lastObject -> 4Q

  4. 全配列前後を再帰させる -> 前1Q、後3Q


以上。ヒンジならこれで必要十分なはず、あとはこれをネタ元に配列が更新されたら四分位数再設定


sciPyのscoreatpercentileみたいにするなら四分位数からバイナリ探索して何percentileか出す。


レコード数が大きいから毎回Zone評価してリンク組み替えよりは算術探索の方が速い(はず…Smilie: ;) 。ステップ数は測ってないけど、NSSetのcontainsObject:は意外と遅いので…内部でループ回してるんじゃなかろうかと思うくらい。


テスト用に組み込んであるPythonの検算コードとも値は一致したので、とりあえず一件落着?


pyObjC組み込んだはいいけど、全然速度が出ないなんていう事態が怖くていまいち腰の重い僕でした。




from WordPress
How to Struggle Against S/W
http://ift.tt/1Ns1dKL