MacのFinderのTags

仕事のプロジェクトでFinderを使いこなしている人がいたので、仕組みについてちょっと調べてみました。

こういうやつです。

f:id:toshi0383:20160329190536p:plain

右クリックのメニューからタグを選択できます。

f:id:toshi0383:20160329201403p:plain

ターミナルで ls すると、@ というStickyBitがついています。

$ ls -l
total 40
-rw-r--r--@ 1 toshi0383  staff  168 Sep 30 18:13 Bar.java
-rw-r--r--@ 1 toshi0383  staff  205 Dec 17 13:00 Foo.java
-rw-r--r--@ 1 toshi0383  staff  224 Jul  5  2015 Hello.java
-rw-r--r--@ 1 toshi0383  staff  938 Jul  5  2015 README.md
-rw-r--r--  1 toshi0383  staff    4 Sep 30 15:08 version

Tagをつけてもファイル自体の差分は出ないようです。

$ git status
On branch topic/1236_2
nothing to commit, working directory clean

コマンドラインからも、 xattr というコマンドでTagを含むmetadataを編集できるようです。manページによると、これらのメタデータはファイル自体には含まれない、ということでした。

Extended attributes are arbitrary metadata stored with a file,
     but separate from the filesystem attributes (such as modification time or
     file size).

どこかにMapping情報を持っているのでしょうね。

ファイルを探すとき、色でイメージできるのはかなり直感的な操作になると思います。仕事は赤、趣味は緑、みたいな。これから使っていこうかな。

CoreAnimatorを使ってみました

本日から始まったtry! Swiftカンファレンスで@TimOliverAU が紹介していたCoreAnimator。気になったので早速購入して使ってみました。

https://itunes.apple.com/jp/app/core-animator/id934434650?l=en&mt=12&at=10l8JW&ct=hatenablog

たっけー。ま、自分でもアプリ作るし、先行投資ってやつです。

まずは、こちらのチュートリアルを3秒くらいで適当にご覧ください。ちょー便利そうですよね。

こちらは早速自分でも使ってみた様子です。最新版はUIの使い勝手も向上しているようです。この状態で右上のExportをすると、

Untitled from Toshihiro Suzuki on Vimeo.

こんなパネルが出てきました。いろいろ設定できるようです。このままExportを押すと、 f:id:toshi0383:20160302220245p:plain

こんな感じで、200行くらいのUIViewサブクラスが生成されました。

swiftfile from Toshihiro Suzuki on Vimeo.

キャンバスのサイズがそのままUIViewのRectになるようですので、正確に調整した方が良いと思います。解像度別に画像を用意したい場合は、最初CoreAnimatorに貼り付ける画像ファイル名に@3x のsuffixをつけておけば、Exportの時に @2x@1x のimageも同時に縮小して書き出してくれます。images というフォルダに書き出され、生成されたコードの方は @1x サイズのものを参照するため、最終的にはこれらを Assetカタログに移して、SwiftGenでenumを生成して、生成されたコードをごにょって、とかは必要になるでしょう。

http://www.coreanimator.com/support/#canvas

そのままXcodeに突っ込んだところ、シミュレータでもちゃんと動きました。

app from Toshihiro Suzuki on Vimeo.

似たようなアプリで、QuartzCode というのもあるようです。こちらはCAReplicatorLayerなどもサポートしているようです。いやーやりますねえ。

QuartzCode - Turn your animations to objective-c or swift OS X / iOS animations code

明日も早いのにこんな時間になってしまいました。ひとまずCoreAnimatorさえあれば、CoreAnimationがグッと身近になる気がしました。複雑なアニメーションでも臆せずチャレンジできそうです。皆さんも使ってみてはいかがでしょうか?

それでは!

参考

iOS向けコードジェネレータまとめ - Qiita

TVMLKitchen

こんにちは、皆さんAppleTV楽しく使っていますか?私はなんと毎日使っています。なぜなら案件についているからです!

というのは実は(!)冗談で、今このゲームにハマっているからです。

Gameloft Video Game Developer Worldwide

Audiのファミリーカーでマルチプレイに参戦し、速そうなクルマを追い抜かして楽しんでいます。AppleTVのリモコンはハンドルにもなるんですよ!ご存知でしたか?
ちなみにハンドルネームは「練馬最速の男」です。周辺地域でのランキングは7位くらいです。ヨロシクお願いします。

余談はさておき、今日は最近作ったライブラリの紹介をしたいと思います。

github.com

年末にTVMLとネイティブアプリのハイブリッドを実現する方法について発表したのですが、その仕組みを簡単に使えるようにしたものになります。

使い方は簡単で、AppDelegateのdidFinishLaunchingWithOptions でこんなおまじないを唱えるだけで、

Kitchen.prepare(launchOptions)

あとはこのようにどこからでも簡単にTVMLのページを表示することができます。

Kitchen.serve(jsFile: "Sample.xml.js")

上は、アプリのMainBundleのSample.xml.js を読み込む例ですが、MainBundleからでなくても、サーバのURLを指定したり、

Kitchen.serve(urlString: "http://.../Catalog.xml.js")

xml文字列を直接読み込ませたり、

let xmlString = // get XML string
Kitchen.serve(xmlString: xmlString)

いろいろな使い方ができます。

使用上のポイントがいくつかありますので説明します。

UINavigationControllerがTVMLと共有される

まず1つ目は、実装の都合によりnavigationControllerがTVML側と共有になるということです。同じUIWindowにある場合は、navigationControllerは基本的にTVML側と共有になります。

つまりTVML => Native => TVML と遷移した場合は、Remote(AppleTVのリモコン) のMenuボタンでバックすることができるわけです。なんて素敵なんでしょう!

共有のnavigationControllerにアクセスするにはちょっとコツがあって、UINavigationControllerを直接辿れる場合は、いつも通り下記のようにすることができますが、

func showDetailViewController() {
    let vc = DetailViewController()
    navigationController.presentViewController(vc, animated: true, completion: nil)
}

UINavigationControllerのインスタンスが辿れない場合は下記のようにします。

Kitchen.navigationController.presentViewController(vc, animated: true, completion: nil)

actionHandler

もう1つは、TVML側で使用するJavaScriptは、基本的には最初のおまじないの時にしか注入できないということです。つまり、Kitchen.serve(urlString: "http://..../Catalog.xml.js") というような形でTVMLのページを表示したとして、このボタンを押したらどうするということは、ライブラリで独自に提供するactionHandlerの仕組みを利用して、起動時に定義しておく必要があるということです。こんな風に。

    Kitchen.prepare(launchOptions, actionIDHandler:{ actionID in
        switch actionID {
        case "openHogeViewController":
            print("Hoge")
        default:
            print("Hage")
        }
    })

actionIDは、TVML側で次のようにして定義します。

<lockup actionID="openHogeViewController" titleId="1234">
    <img src="${this.BASEURL}music_1.lcr" width="308" height="308" />
    <title class="whiteText">Title 1</title>
</lockup>

これで、actionIDが設定された要素をClickすると、actionHandlerがactionIDの値を引数にして呼び出される、というわけです。

なぜTVMLなのか

TVMLマークアップ言語ですので、HTMLがちょっとかけるくらいのデザイナーの方でもとっつきやすいと思います。簡単なページであればTVMLでサクッと作ってしまえば工数の節約になりますし、後から運用ができるというのも利点だと思います。まああまり複雑な使い方はせず、お知らせやライセンス情報のページを表示する、くらいにしておくのが幸せだと思います。

以上になります。実はデータ構造を渡すだけで複雑なビューも実現してくれるレシピ機能の実装も目論んでいたのですが、そこまでするならNativeでも良さそうなので、一旦途中でやめています。皆さんのコントリビュート、お待ちしています。

追記

ちなみにTVMLKitchen というネーミングは、jpsim氏のSourceKitten を真似しました。美味しそうでしょう?

テストコードを読んでライブラリの難しいコードを理解する

こんにちは、@toshi0383 です。

この1年間振り返ると様々なライブラリにお世話になってきました。ライブラリは中身の実装は知らなくても使えるよう、「まアいつものアレでしょ」という処理が抽象化されているというのが便利な点なわけですが、ソースコードを読んで動きを理解する必要がどうしても出てくると思います。ドキュメントが充実しているライブラリばかりではないですしね。今日はそんな時に効率よく理解を深めるために私が実践している方法をご紹介します。

ズバリ一言で、「テストコードを読め」です。

例えば、このReactiveKitの処理があります。

var operation: Operation<Int, TestError>!
operation = create { observer in
    observer.next(1)
    observer.next(2)
    observer.next(3)
    observer.success()
    simpleDisposable = SimpleDisposable()
    return simpleDisposable
} 

はじめちょっと何をやっているのかわからなかったのですが、対応するテストコードはこうなっていました。

var disposable: DisposableType!
disposable = operation.observeNext(on: ImmediateExecutionContext) {
    observedEvents.append($0)
} 
...
...
expect(observedEvents) == [1, 2, 3]

これだけ見れば、observeNextにImmediateExecutionContext とかいうのを渡すとoperationが発火して、値が観測できるようだ、ということがわかります。 このレベルであれば、下手にググるよりはテストコードを読んだ方が、素早く正しい使い方がわかるわけです。

もう一つ例を載せておきます。SwiftStateのテストコードです。

var invokeCount = 0 
        
let machine = StateMachine<MyState, NoEvent>(state: .State0) { machine in
        
    // add 0 => 1 => 2
    machine.addRouteChain(.State0 => .State1 => .State2) { context in
        invokeCount++
        return
    }
        
}   
        
// tryState 0 => 1 => 2
machine <- .State1
machine <- .State2
        
XCTAssertEqual(invokeCount, 1, "Handler should be performed.")

addRouteChainは、指定された遷移通りに状態が遷移した時に一度だけCallbackを呼び出す、ということが理解できますね。

そんなところで、我々はコードを読み書きしているわけで、コードの解説記事を読むくらいだったら、コードを説明しているコードを読んだ方が早いケースもそれなりにあるかな、と。まあ後は、気軽に 作者に聞いちゃう、ということで、よろしくお願いいたします。

草々

Swift Package Manager のPackageがlinkerエラーでビルド失敗する

久しぶりにSwift Package Managerを触ったら、 この構成で、

$ ls
Package.swift   main.swift
$ cat main.swift 
print("hello")
$ cat Package.swift 
import PackageDescription

let package = Package(
    name: "Queue",
    dependencies: []
)

こんなエラーが出るようになりました。

$ swift build
Undefined symbols for architecture x86_64:
  "__TFSSCfT21_builtinStringLiteralBp8byteSizeBw7isASCIIBi1__SS", referenced from:
      _main in main.swift.o
  "__TFs5printFTGSaP__9separatorSS10terminatorSS_T_", referenced from:
      _main in main.swift.o
  "__TIFs5printFTGSaP__9separatorSS10terminatorSS_T_A0_", referenced from:
      _main in main.swift.o
  "__TIFs5printFTGSaP__9separatorSS10terminatorSS_T_A1_", referenced from:
      _main in main.swift.o
  "__TMSS", referenced from:
      _main in main.swift.o
  "__TTSg5P____TFs27_allocateUninitializedArrayurFBwTGSax_Bp_", referenced from:
      _main in main.swift.o
  "__TZvOs7Process11_unsafeArgvGSpGSpVs4Int8__", referenced from:
      _main in main.swift.o
  "__TZvOs7Process5_argcVs5Int32", referenced from:
      _main in main.swift.o
  "_globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_func5", referenced from:
      _main in main.swift.o
  "_globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_token5", referenced from:
      _main in main.swift.o
ld: symbol(s) not found for architecture x86_64
<unknown>:0: error: link command failed with exit code 1 (use -v to see invocation)
<unknown>:0: error: build had 1 command failures
error: exit(1): ["/Library/Developer/Toolchains/swift-2.2-SNAPSHOT-2016-01-11-a.xctoolchain/usr/bin/swift-build-tool", "-f", "/Users/toshi0383/dev/tmp/sample-package/.build/debug/Queue.o/llbuild.yaml"]

-v をつけてビルドしたらこういうコマンドがこけていたようなので、

/Library/Developer/Toolchains/swift-2.2-SNAPSHOT-2016-01-11-a.xctoolchain/usr/bin/swiftc -o /Users/toshi0383/dev/tmp/sample-package/.build/debug/Queue /Users/toshi0383/dev/tmp/sample-package/.build/debug/Queue.o/main.swift.o -target x86_64-apple-macosx10.10 -Xlinker -all_load -g -sdk /Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk -L/usr/local/lib

いろいろ試しているうちに、最後の -L/usr/local/lib というのをなくしたらビルドが通りました。

$ /Library/Developer/Toolchains/swift-2.2-SNAPSHOT-2016-01-11-a.xctoolchain/usr/bin/swiftc -o /Users/toshi0383/dev/tmp/sample-package/.build/debug/Queue /Users/toshi0383/dev/tmp/sample-package/.build/debug/Queue.o/main.swift.o -target x86_64-apple-macosx10.10 -Xlinker -all_load -g -sdk /Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk -L/usr/local/lib
$ .build/debug/Queue
hello

参考までに記事にしました。

前回触った時からしたことといえば、

  • Swiftをビルドした
  • Xcode7.3beta をインストールした

くらいなのですが。

原因不明なのですがひとまず。

sniper, the OSX app terminator in Swift

sniper というコマンドラインツールをSwiftで作ったので、作る過程で得られた知見も交えて紹介したいと思います。

f:id:toshi0383:20151107082936p:plain

github.com

TL;DR

  • sniper は、アプリのプロセスを探して殺すことができる簡単なコマンドです。
  • bin/sniper をどこかにコピーして使ってください。
  • sniper はCIプロセスで使うとたまに便利だと思います。
  • sniperOSX以外のアプリは見つけられないし殺せません。
  • アプリのプロセスを殺すにはNSRunningApplicationを使いました。
  • コマンドラインツールにはDynamic Frameworkは組み込めないため、壮大なワークアラウンドが必要。(なので諦めましょう。)
  • サバゲーが楽しみすぎて sniper とかちょっと恥ずかしい名前つけてしまった。

背景

下記の課題がありました。

  • UIテストをCIプロセスに組み込みたいが、初回起動時にしか出ないダイアログとかがある
  • 使用するSimulatorを決めてアプリを使い回すよりは、毎回リセットして初回起動として扱いたい
  • Simulatorをリセットするには、泥臭いAppleScript を書くか、 Simulatorを削除(rm -rf ~/Library/Developer/CoreSimulator/Devices/*) するしかない模様だった
  • AppleScriptは書きたくない
  • でもSimulatorが起動している状態で上記の rm コマンドで削除すると、次回ちゃんと起動しなくなってテスト失敗する
  • Simulatorを駆逐したい

というわけで、Simulatorを殺すうまい方法はないかと調べていたわけです。

ps コマンドからの kill ${pid} のことを思い出したんですが、なんとなくOSXではご法度な気がするし。。

で、見つかりました。

NSRunningApplication

NSRunningApplication というのがあって、これに terminate() というメソッドがあります。こいつを取れれば、殺せそうです。

取得する方法はいくつかあるみたいで、

NSRunningApplication.runningApplicationsWithBundleIdentifier(bundleId)

もしくは

NSWorkspace.sharedWorkspace().runningApplications

でもこれで本当に自分以外のプロセスも取れちゃうんでしょうか。やってみましょう。

$ echo "import AppKit; NSWorkspace.sharedWorkspace().runningApplications.forEach{print(\$0)}" | xcrun swift
Welcome to Apple Swift version 2.1 (700.1.101.6 700.1.76). Type :help for assistance.
<NSRunningApplication: 0x10060aa50 (com.apple.loginwindow - 89)>
<NSRunningApplication: 0x10060aae0 (com.apple.inputmethod.Kotoeri - 271)>
<NSRunningApplication: 0x10060abe0 (com.apple.internetaccounts - 277)>
<NSRunningApplication: 0x10060ace0 (com.apple.AirPlayUIAgent - 279)>
<NSRunningApplication: 0x10060ade0 (com.apple.iChat - 296)>
<NSRunningApplication: 0x10060aee0 (com.apple.systemuiserver - 303)>
<NSRunningApplication: 0x10060afe0 (com.apple.dock - 302)>
<NSRunningApplication: 0x10060b0e0 ((null) - -1)>
<NSRunningApplication: 0x10060b1e0 (com.apple.finder - 304)>
<NSRunningApplication: 0x10060b2e0 (com.apple.sharingd - 309)>
<NSRunningApplication: 0x10060b3e0 (com.adobe.accmac.ACCFinderSync - 312)>
<NSRunningApplication: 0x10060b4e0 (com.apple.dock.extra - 315)>
<NSRunningApplication: 0x10060b5e0 (com.apple.Spotlight - 317)>
<NSRunningApplication: 0x10060b6e0 (com.apple.storeuid - 335)>
<NSRunningApplication: 0x10060b7e0 (com.apple.MailServiceAgent - 458)>
<NSRunningApplication: 0x10060b8e0 (com.apple.iTunesHelper - 486)>
<NSRunningApplication: 0x10060b9e0 (com.kensington.trackballworks.helper - 488)>
<NSRunningApplication: 0x10060bae0 (com.security.apple.Keychain-Circle-Notification - 468)>
<NSRunningApplication: 0x10060bbe0 (com.apple.TISwitcher - 487)>
<NSRunningApplication: 0x10060bce0 (com.apple.notificationcenterui - 470)>
<NSRunningApplication: 0x10060bde0 (com.fabriceleyne.hourlite - 491)>
<NSRunningApplication: 0x10060bee0 (com.linebreak.CloudAppMacOSX - 492)>
<NSRunningApplication: 0x10060bfe0 (com.apple.wifi.WiFiAgent - 481)>
<NSRunningApplication: 0x10060c0e0 (com.adobe.acc.AdobeCreativeCloud - 478)>
<NSRunningApplication: 0x10060c1e0 (com.adobe.AdobeIPCBroker - 504)>
<NSRunningApplication: 0x10060c2e0 (com.adobe.acc.AdobeDesktopService - 514)>
<NSRunningApplication: 0x10060c3e0 (com.adobe.acc.HEXHelper - 515)>
<NSRunningApplication: 0x10060c4e0 (com.adobe.accmac - 517)>
<NSRunningApplication: 0x10060c5e0 ((null) - -1)>
<NSRunningApplication: 0x10060c6e0 (com.apple.lateragent - 534)>
<NSRunningApplication: 0x10060c7e0 (com.apple.cloudphotosd - 729)>
<NSRunningApplication: 0x10060c8e0 (com.apple.EscrowSecurityAlert - 1049)>
<NSRunningApplication: 0x10060c9e0 (com.apple.nbagent - 1341)>
<NSRunningApplication: 0x10060cae0 (com.apple.Terminal - 1447)>
<NSRunningApplication: 0x10060cbe0 (com.apple.iCal - 52911)>
<NSRunningApplication: 0x10060cce0 (co.gitup.mac - 65045)>
<NSRunningApplication: 0x10060cde0 (com.apple.Safari - 16036)>
<NSRunningApplication: 0x10060cee0 (com.apple.WebKit.Networking - 16038)>
<NSRunningApplication: 0x10060cfe0 (com.tinyspeck.slackmacgap - 16258)>
<NSRunningApplication: 0x10060d0e0 (com.apple.qtkitserver - 16266)>
<NSRunningApplication: 0x10060d1e0 (com.apple.WebKit.Plugin.64 - 16272)>
<NSRunningApplication: 0x10060d2e0 (com.apple.WebKit.Databases - 84902)>
<NSRunningApplication: 0x10060d3e0 (com.apple.WebKit.WebContent - 2098)>
<NSRunningApplication: 0x10060d4e0 (com.github.atom - 5135)>
<NSRunningApplication: 0x10060d5e0 (com.github.atom.helper - 5141)>
<NSRunningApplication: 0x10060d6e0 (com.apple.coreservices.uiagent - 13010)>
<NSRunningApplication: 0x10060d7e0 (com.apple.WebKit.WebContent - 13061)>
<NSRunningApplication: 0x10060d8e0 (com.apple.WebKit.WebContent - 13066)>
<NSRunningApplication: 0x10060d9e0 (com.apple.WebKit.WebContent - 13067)>
<NSRunningApplication: 0x10060dae0 (com.apple.WebKit.WebContent - 13068)>
<NSRunningApplication: 0x10060dbe0 (com.apple.dt.Xcode - 13088)>
<NSRunningApplication: 0x10060dce0 (com.apple.iphonesimulator - 13512)>
<NSRunningApplication: 0x10060dde0 (com.apple.WebKit.WebContent - 13632)>
<NSRunningApplication: 0x10060dee0 (com.apple.WebKit.WebContent - 13633)>
<NSRunningApplication: 0x10060dfe0 (com.apple.WebKit.WebContent - 13636)>

めっちゃとれちょる。というわけでこれでいけそうです。

コマンドラインツールの作成

で、CIに組み込むのでコマンドラインツールにしたかったんです。
以前 kitasuke氏のスライド を見て試したことがあって、非常に大変だったことは覚えていたのですが、まあとりあえずやってみようということで。

で、今回は案外簡単にできました。
Xcodeのプロジェクトテンプレートも Command Line Tool です!
オプションのパース用にライブラリが必要だったのですが、たまたま選んだSwiftCLI がgit-submoduleでのインストール推奨にしてくれていたため、DynamicFrameworkのための壮大なワークアラウンドが必要なかったのです。
というかまあソースコード直接突っ込めばなんでもいけるわけです。ただ、SwiftはModuleのnamespaceがあるので、ClassPrefixをつけるとかいうObjective-Cの素晴らしい文化は、基本的にはありません。よってライブラリ同士でクラス名が競合することが多く、その場合どうするかというと。。考えたくもないですね。(自分でprefixつけて回る?いやいやいや。。)

なぜDynamicFrameworkがコマンドラインツールに直接組み込めないのかというと... まあいまはそういう仕様なんです。諦めましょう。
どうしても組み込みたい方は上記kitasukeさんのslideとか、これも参考になるかと思います。

使い方

使い方は、こんな感じになりました。README.mdから抜粋です。

# List all running apps
$ sniper target
37 apps found.
com.apple.loginwindow: 89
.
.
.

# Filter by keyword
$ sniper target firefox
1 app found.
org.mozilla.firefox: 4822

# Terminate process by bundle ID
$ ./sniper shoot -b org.mozilla.firefox
Terminating org.mozilla.firefox: 4822 ...

# Terminate process by PID
$ sniper shoot -p 4703
Terminating com.apple.iphonesimulator: 4703 ...

で、今回の目的 "Simulatorを駆逐する" を達成するには、こうすると良いと思いました。

$ sniper shoot -b com.apple.iphonesimulator  # shutdown before reset(remove) all simulators
$ rm -rf ~/Library/Developer/CoreSimulator/Devices/*

TravisやCircle、bitrise のようなCIサービスであればコンテナが毎回使い捨てだと思うので、なにも考えなくても毎回すべてのアプリが駆逐された状態かと思いますが、余っているMacにJenkins立てているケースも多々あるかと思いますので、そういった場合に使えるかと思います。

まとめ

というわけで、Swiftで簡単にコマンドラインツールを作ることができました。
簡単な処理だと他のscript言語の方が簡単にかけるケースの方がよっぽど多いと思いますが、Macアプリのプロセスをどうこうするとかの場合は、AppKitを使うのが一番手っ取り早そうだなというのが所感でした。

以上になります。

参考

Swift array map の性能測定の続き

前回の続き

前回の記事に対して@es_kumagai さんからフィードバックをいただきました。
今週忙しくて、やっと見る時間が取れました。

結論から言うと、今回の検証方法(ターミナルからswiftやswiftc を利用する)においては、標準のmapより速い実装が手に入りました。

たった1000件での比較ですが、こうなりました。前回から計測方法も多少改善しました。手抜きですみません。

map : 0.000211954116821289
map7: 0.000133037567138672

まあ1/10000 秒の差とか、もはやモバイル開発においてはどうでもいい話ですね。めちゃくちゃ大量のデータ扱うこともそう滅多にないでしょうし。

ボトルネックはやはりarrayの初期化だったようです。
コードはフィードバックでいただいたものを貼っておきますね。ありがとうございます。

gist.github.com

-O オプション

なお、-OをつけるとDouble値の出力がおかしくなりました。
どうして。。?

$ xcrun -sdk macosx swiftc -O  map.swift
$ ./map 
=== : 0.000318050384521484
map : 0.000821113586425781
map : 5.79357147216797e-05
map7: 3.00407409667969e-05
map : 5.10215759277344e-05
map7: 2.88486480712891e-05
map : 5.10215759277344e-05
map7: 2.62260437011719e-05
map : 4.98294830322266e-05
map7: 2.59876251220703e-05

計測に使用したコードはこちらにあげました。

なおコマンドラインから指定できるコンパイルオプションはこうなっているようです。

  -Onone                 Compile without any optimization
  -Ounchecked            Compile with optimizations and remove runtime safety checks
  -O                     Compile with optimizations

通常は-Onone が使われているようです。

まとめ

ひとまず今回の検証方法においては、標準のmapより速い実装が手に入りました。

ちなみに、Playgroundではこうなります。

map  : 0.181938886642456
map7 : 0.619832992553711

モヤっとしますね。

まだまだ不明点だらけで、勉強しないといけないことが山積みだということがわかりました。

以上です。