Slide 1

Slide 1 text

Server Side Swift 実用性評価 2017 @ainame iOSDC 2017/09/17

Slide 2

Slide 2 text

自己紹介 ● @ainame / Satoshi Namai ● iOSエンジニア ● RubyKaigiでもLTしてきます - 9/19

Slide 3

Slide 3 text

話すこと/話さないこと 話す ● Server Side Swift(SSS)実際にやってみた事例 / 良い点・苦労する点 ● Redis ● 今後SSS環境への希望 話さない ● Web開発、vapor / Kitsura / Perfect などのFrameworkの詳細 ● swift-server/http プロジェクト

Slide 4

Slide 4 text

Swift OSS化 ● 2015年末〜Swift + 各種ライブラリがOSS化 ○ apple/swift - 本体 ○ apple/swift-package-manager - パッケージ管理 ○ apple/swift-corelibs-foundation - 非Apple環境でも動くFoundation ○ apple/swift-corelibs-libdispatch - Concurrency用Framework a.k.a GCD ○ apple/swift-corelibs-xctest - ユニットテスト用Framework ○ apple/swift-llvm, swift-clang, swift-lldb - Compiler & Debugger

Slide 5

Slide 5 text

Swift Foundation App Standard Library Core Foundation Darwin or Glibc Dispatch C Library

Slide 6

Slide 6 text

Swift Foundation App Standard Library Core Foundation Darwin or Glibc Dispatch C Library システムコール などOSに依存するレ イヤー

Slide 7

Slide 7 text

Server Side Swiftとは? (とても)頑張ればなんでも書けるはず ● アプリケーション(Web API, Web Page, Job Queue, gRPC, …) ● ミドルウェア(Web Server, KVS, DB, 各種Agent, ...) ● 各種スクリプト(Cron, 書き捨て, ...) Linux上で動いたらだいたいServer Side Swiftでは?? (現状Ubuntu上でapt install swift出来ない...)

Slide 8

Slide 8 text

ジョブキューシステム を作ってみた

Slide 9

Slide 9 text

Redis Web/Rails app Cron/Rails runner One-off scripts Worker Worker Worker Worker Ruby on Rails ● DB処理 ● メール・Push通知送信 ● Webクローラー ● 動画エンコーダー など... Sidekiq http://sidekiq.org/about

Slide 10

Slide 10 text

mperham/sidekiq ● Rubyでもっともレイテンシーが低いジョブキューシステム ○ 大量のJobをLatencyが低く実行できる ○ スレッドベースで並列化されていて省メモリ IOバウンドな処理を大量にさばくのに向いている ○ リトライ、スケジューリング、一括再試行など自由自在 ○ 信頼性が高い/便利機能をもつPro/Enterprise版がある ○ スケールアウトしても Redisにかかる負荷を低くなるような設計 Sidekiq http://sidekiq.org/about

Slide 11

Slide 11 text

class VideoEncodeWorker include Sidekiq::Worker def perform(video_id, size) video = Video.find(video_id) encoded_file = VideoEncoder.new.encode(video, size) FileUploader.new.upload(encoded_file, to: key) end end class VideosController < ApplicationController def create @video = Video.create!(video_params) ["1920x1080", "1270x720", "960x540"].each do |size| VideoEncodeWorker.perform_async(@video.id, size) end { status: "OK" } end end

Slide 12

Slide 12 text

ainame/Lumpik ● gemのクローン ○ RubyのSidekiq ClientでPushするpayloadと互換があるのでJobだけSwiftも可 ○ 使用するRedisコマンド、リトライロジックレベルで互換あるので遅いはずがない ● Ruby実装との違い ○ GCD経由でネイティブスレッドを活用し CPUバウンドな処理を並列化出来る!! ○ そもそもベースラインの実行速度が早いはず

Slide 13

Slide 13 text

Redis Web/Rails app Cron/Rails runner One-off scripts Worker Worker Worker Worker Swift環境 ● DB処理 ● メール・Push通知送信 ● Webクローラー ● 動画エンコーダー など... Sidekiq http://sidekiq.org/about Lumpik Vapor/Kituraなどが既にある 参入者いない〜!

Slide 14

Slide 14 text

import Foundation import Lumpik class EchoWorker: Worker { struct Args: Argument { var message: String } var jid: Jid? var queue: Queue? var retry: Int? required init() {} func perform(_ args: Args) throws { print(args.message) sleep(3) } }

Slide 15

Slide 15 text

ベンチマーク (jobs/sec) x2 faster than Ruby!

Slide 16

Slide 16 text

Demo

Slide 17

Slide 17 text

ListWorker SaveWorker keyword:“Swift” maxPage:50

Slide 18

Slide 18 text

ListWorker SaveWorker keyword:“Swift” maxPage:50

Slide 19

Slide 19 text

ListWorker SaveWorker https://aa.jp/1 https://bb.jp/1 https://cc.jp/3 https://dd.jp/4 https://ee.jp/5

Slide 20

Slide 20 text

ListWorker SaveWorker

Slide 21

Slide 21 text

Demo https://github.com/ainame/LumpikExample

Slide 22

Slide 22 text

移植方法 ● OSS版Swift/Swift Package Manager + Xcode上で書いた ○ swift package generate-xcodeproj ● Ruby版の実装を元に可能な限り同じロジックで実装 ○ Rubyと同じものがほぼ実装できる ● VaporのRedisクライアントなどを利用 ○ SwiftのRedisクライアントの中で一番使いやすい ● 各種言語の標準ライブラリから実装方法を学ぶ ○ RubyのProcess.daemonを元に他にも ainame/Swift-Daemon を作った

Slide 23

Slide 23 text

良かった ● Xcode(IDE)が使える ● Swift Package Manager(v4)は普通に使える ● 型安全の安心感 ● 実行速度早い

Slide 24

Slide 24 text

悪かった ● 定番のライブラリが整っていない ○ Foundation がカバーしてないコアな部分にオレオレ実装多い ○ Socket通信とか ○ 依存が増えてダルい ● CompletionHandler問題 ○ 既存のURLSessionとかもしくはIO処理するコードには completionHandlerを取るものが多い ○ 他のサーバーサイド言語のようには書きづらい ... ● 思ったより早くない(なかった) ○ ボトルネックがなんなのかは正確に知る必要がある

Slide 25

Slide 25 text

オレオレlibc問題 まずこれを作るところから始まる

Slide 26

Slide 26 text

mperham/sidekiq.cr ● Sidekiq作者によるCrystal版の実装 ○ Crystalは静的型付で実行速度がかなり速い Rubyがグリーンスレッドで concurrency実現 ○ 速いけどマルチコアを使いきれない ● ainame/Lumpikより速い(マルチコアを使ったとしても...) ○ Redisクライアントがボトルネックになっていた

Slide 27

Slide 27 text

Swift vs Crystal vs Ruby (jobs/sec)

Slide 28

Slide 28 text

SwiftのRedisクライアント https://github.com/ainame/swift-redis-clients-benchmark

Slide 29

Slide 29 text

x10 faster than Vapor! Swift https://github.com/stefanwille/redis-client-benchmarks https://github.com/ainame/swift-redis-clients-benchmark

Slide 30

Slide 30 text

Swiftのクライアントはなぜ遅い ● (作為的な)ベンチマーク ≠ 現実のアプリケーション ○ setコマンドを1,000,000回実行した時のThroughputを測っている ○ このベンチマークでは Pipeliningが実装されてないだけで圧倒的に遅くなる ● Socket通信のレイヤーがみんな独自実装 ○ Buffering IOが実装されてない ○ システムコールを呼びまくってボトルネックに

Slide 31

Slide 31 text

Performance Tuning ● Pipelining (Redis特有の方法) https://redis.io/topics/pipelining ○ 1度のIO処理で複数のコマンドを実行する方法 ○ Redisサーバー/クライアント間のRTT(Round Trip Time)が減る ○ send/recvシステムコールのuser land -> kernel landへのcontext switchが減る ● Buffered-IO ○ send/recv システムコールを減らすためメモリ上のバッファを活用

Slide 32

Slide 32 text

Pipelining 非Pipelining ● Client: INCR X ● Server: 1 ● Client: INCR X ● Server: 2 ● Client: INCR X ● Server: 3 ● Client: INCR X ● Server: 4 Pipelining ● Client: INCR X ● Client: INCR X ● Client: INCR X ● Client: INCR X ● Server: 1 ● Server: 2 ● Server: 3 ● Server: 4 一度ずつの write/readで済む

Slide 33

Slide 33 text

実用性評価 (感想)

Slide 34

Slide 34 text

感想 ● Cのライブラリが使えばとりあえず何でも動かすのはできる ○ 今の所OS毎の対応を自前で対応する努力が必要 ● Swiftで書くモチベーションとは ○ IO周りのライブラリがとにかく貧弱 ○ それ他の言語でも出来るのでは・・・? ○ (自分の場合はミドルウェアを作る方法を実際に作って学びたかった) ● 既存のフレームワーク、痒いところに手が届かない(今回の例では) ○ Pipelining, Transactionなどの普通っぽい実装が無い

Slide 35

Slide 35 text

Server Side Swift まだまだ道のりは険しい