asyncioがPOSIXスレッドを使っている原因を調べる
tokibito先生 (id:nullpobug) がオフィスに遊びにおいでと声かけてくれたので、オープンコレクターさんに遊びに行ってました。 aodag先生 (id:aodag) と3人で雑談してたんですが、ふと以前気になっていたことを思い出したので聞いてみた。
気になっていたこと
とある勉強会の発表資料 を作っている時に、 asyncioとaiohttpを使ってとあるサーバにHTTPのリクエストを送るコード例を用意した。
import aiohttp import asyncio async def fetch(l, url): async with aiohttp.ClientSession(loop=l) as session: async with session.get(url) as response: return await response.text() async def main(l, url, num): tasks = [fetch(l, url) for _ in range(num)] return await asyncio.gather(*tasks) if __name__ == '__main__': loop = asyncio.get_event_loop() results = loop.run_until_complete(main(loop, 'http://localhost:8000', 3)) for r in results: print(r)
PyCharmでは、「Concurrency Diagram」を表示する機能があり、スレッドやプロセスの動きを確認できるのですが、 このコードを実行したときのスレッドの動きは次のようになる。
なぜか、 concurrent.futures.ThreadPoolExecutor
が現れている。
ドキュメントに何か書いてあるのか調べてみたのですが、それらしい記述が見つからず諦めていたのでaodag先生とtokibito先生に聞いてみた。
socket.getaddrinfo
数十分で原因を見つけてくれた。
socket.getaddrinfo
が同期的に実行されてしまうため、cpythonの実装としてはこれを非同期に実行できるように変えるのではなく、ひとまず concurrent.futures.ThreadPoolExecutor
により複数のスレッドで実行するようにしているらしい。
- https://github.com/python/cpython/blob/6f0eb93183519024cb360162bdd81b9faec97ba6/Lib/asyncio/base_events.py#L666-L673
- https://github.com/python/cpython/blob/6f0eb93183519024cb360162bdd81b9faec97ba6/Lib/asyncio/base_events.py#L627-L636
試しにgetaddrinfoが使われないコードサンプルとして、 aioredisを使ったサンプルを用意した。 ローカルに建てたRedisのサーバにUNIXドメインソケットで繋いでみる。
import asyncio import aioredis async def connection_example(key): conn = await aioredis.create_connection('/tmp/redis.sock') return await conn.execute('GET', key) async def main(num): tasks = [connection_example('my-key') for _ in range(num)] return await asyncio.gather(*tasks) if __name__ == '__main__': loop = asyncio.get_event_loop() results = loop.run_until_complete(main(3)) for r in results: print(r)
ちなみにredisのconfigは↓。
daemonize no pidfile /var/run/redis.pid unixsocket /tmp/redis.sock unixsocketperm 700 logfile "" databases 1
この時のConcurrency Diagramを見ると、
たしかにスレッドが生成されていない。
外部のRedisサーバへのアクセス
一方でRedisのサーバを外部に用意して繋いでみると (今回は arukas.io を使わせていただきました)、
import asyncio import aioredis async def connection_example(key): conn = await aioredis.create_connection( ('seaof-xxx-xxx.arukascloud.io', 311390), db=0, password='xxxxxxxxxxxxxxx') return await conn.execute('GET', key) async def main(num): tasks = [connection_example('my-key') for _ in range(num)] return await asyncio.gather(*tasks) if __name__ == '__main__': loop = asyncio.get_event_loop() results = loop.run_until_complete(main(3)) for r in results: print(r)
実行すると次の通り。
おー やっぱりワーカースレッドが生成されてしまうらしい。
ThreadPoolExecutorのワーカースレッドはいくつまで生成されるのか。
Semaphoreで同時に実行される数を3つに制限した時のConcurrency Diagramは次のようになる。
どうやらThreadPoolExecutor内のThreadが再利用されていない。 どこまで生成されるのかは、ドキュメントのThreadPoolExecutorのところに書いてあった。
max_workers が None か指定されない場合のデフォルト値はマシンのプロセッサの数に 5 を掛けたものになります
試しに30個ぐらいリクエストを送ってみる(Semaphoreは3)。
20個まで生成され、それ以降は再利用されているのを確認できた。 実装上ワーカースレッドの上限は変更できないけど、たしかに別に困るケースもなさそう。
uvloop
uvloopの方は、POSIXスレッド使わずに頑張ってるかもという話が出たので確認。
import uvloop # 中略 loop = uvloop.new_event_loop() asyncio.set_event_loop(loop)
実行すると、
おー ほんとだ。
おわりに
aodag先生とtokibito先生すごい… 自分はドキュメントを読みつつモヤモヤしたまま放置してたのですが、数十分で原因を見つけて教えてくれた。 自分もいよいよ明日から社会人なので、お二人目指して精進します。
aodag先生とtokibito先生のいるオープンコレクターさん、お仕事募集中だそうです(お世話になったので一言宣伝)。 ありがとうございました!
2017年抱負(エンジニアリング)
今週のお題「2017年にやりたいこと」
あけましておめでとうございます。 今年やりたいことを書き出してみる。
2016年はインプットのかなり多い年だったので、今年はアウトプットを増やしたい。
- 卒論の提出と卒業: まずはちゃんと卒業します。
- 書籍の出版: 実はもう動いているので必ずやりきる。これは2016年の目標でもありましたが、告知できるのは今年の5月頃になりそうです。
- Webサービスをリリース: いま作りたいものがあるのでなんとか形にしたい。
- Github 100 stars: 2016年の目標だったんですが、最も集めたのはKobinの50starsでした。今年こそ。
うーん... 直近でやりたいこと・やらないといけないことが多くてあんまり思いつかなかった。 もうちょっと余裕を持った生活が一番必要かもしれない。
抱負とまではなっていないんですが、今気になってる技術は、
- PostgreSQLドライバ実装: 以前、nakagamiさんにPostgreSQLのMessage Formatプロトコル を丁寧に解説してもらったので、一度書いておきたい
- Rustでシステムコールを直接バンバン叩く何かつくってみたい: 作るもの思いつかなければ、HTTPのサーバとかいいかな...
- 非同期のPythonサーバの実装: まだServerとのInterfaceがPEPに無いので、何らかの形で関われると嬉しい
おわりに
今年もよろしくお願いします。
2016年のふりかえり
2016年も終わりですね。大学院には進まずに都内のWeb系企業に就職するので、学生生活もそろそろ終わりみたいです。 3月頃から渋谷新宿あたりにいると思うので、またご飯行きましょう🍺
OSS活動
Write Code Every Dayには程遠いですが、昨年に比べてコードを書く量も増えてきています。 さすがに技術的にも成長はしているみたいで、コーディングスピード(特にPython)があがったのもContributionsの数に繋がったのかな。 つくってきたOSSをふりかえる。
Kobin
趣味でWebフレームワークとドキュメンテーション、サンプルアプリ、周りのツール群を全部1人で書いていくのは思っていたよりも多くの時間が必要ですが、学んだことは思っていた以上にかなり多かったです。学生のうちに取り組んでおいてよかったことの1つですね。
まだまだ利用者が多いわけではないので、新しい構文やモジュールは気軽に導入していて、Pythonのバージョンアップで追加されたモジュールや構文を試す意欲が湧いていい。
来月の神戸Pythonの回で、Kobinのハンズオンをすることになったので、近くの方は是非参加して下さい。 自分の作ったフレームワークのハンズオンの依頼を受けるのはうれしいですね。
その他
Kobin関連以外だと、公開したソフトウェアはこのあたり。GoやPython、ElectronアプリケーションにAngular2のUIコンポーネント。
- GitHub - c-bata/feedy: RSS Feed Fetching Framework for Python
- GitHub - c-bata/comet: NicoNico style tweet viewer for Desktop.
- GitHub - c-bata/gosearch: Web crawler and Search engine in Golang.
- GitHub - c-bata/chalice-linebot: LINE BOT on AWS Lambda + API Gateway using Chalice
- GitHub - c-bata/ng2-card: Card view component for angular2
- GitHub - c-bata/rexecute: Re-execute a command when specified files are modified.
ソフトウェア以外だと、PyCon JPやPyCon APAC/Koreaでの登壇やgihyo.jpでの連載ですね。 今年も英語の勉強がついつい後回しになっていてまずい。
- 「PyCon APAC 2016 in Korea」参加レポート:レポート|gihyo.jp … 技術評論社
- PyCon APAC 2016 in KoreaでLTしてきた - c-bata web
- 基礎から学ぶWebアプリケーション・フレームワークの作り方 at PyConJP 2016
- あなたもWebフレームワークを作りませんか? 高専生によるWebフレームワーク作成講座 #pyconjp #pyconjp_202 - Togetterまとめ
あとはポートフォリオもAngular2で更新した。
デザイン
自分でもロゴやWebデザインがある程度出来るといいなと思っていて、IllustratorとかSketchでロゴを書いたりしている。 悩んだときは id:denari01 に頼るようにしていて、相談すると自分では言葉にできない違和感をちゃんと日本語に落とし込んで解説してくれる。
Kobinのロゴいいよねって言ってくださる方が結構いらっしゃるんですが、これは彼が考えてくれたものです。そろそろなにか美味しいもの奢らないといけない。
Repository | Design |
---|---|
Feedy | |
Comet | |
kobin-todo |
写真
余談ですが、デザインの延長で写真に興味が湧いてきていて、旅行のときはSONYのα6000を持ち歩いてる。
高専の卒業と就職
7年間通った高専・専攻科も (卒研が無事に終われば) 今年度で卒業みたいです。長かったですね。
情報系の技術レベルに関して疑問に思うことは何度かありましたが、専攻科のクラスメートは信じられないくらい優秀で、他の高専でも id:puhitaku みたいにものづくりに対して半端ない熱量持ってる人がいるのでいい刺激になりました。 5年後・10年後には、みんなかなり活躍してる気がするので、自分もそのときには恥ずかしくないくらいには活躍したい。
卒研終わったらクラスメートとあと1回くらいは海外行こう。
2017年
技術的な興味を優先してきたのですが、ある程度形になってきたところで満足して、中途半端な完成度のまま放置しているものも多かったです。 最近は、Sphinxのメンテナをされてる方やJasper(Electron製のGithub Issues Reader)の開発をされている方のブログ記事を読んで、どういうものづくりしていきたいのかとか、モチベーションの保ち方について考えることがありました。
ちゃんと作り込むところまでモチベーションを保つには「自分が楽をできる」とか「お金になる」みたいな分かりやすい理由がもう少し必要そうです。
今は1つ実現したいアイデアがあるので、とりあえず .io
ドメインを購入してKobinを使って実装しています。
4月までにはある程度形にしてプライベートベータで知り合いには試してもらえるようにしたい。
- Sphinx のメンテナになって一年が経過した話 - Hack like a rolling stone
- GitHub用のIssue Reader「Jasper」の開発を振り返ってみる - maru source
おわりに
GithubのメールアドレスやWantedlyへのスカウトメール・メッセージが、2015年ぐらいからかなり増えてきていて、知名度もあがってきたのかなと嬉しく思ってました。 うまくいかないことも結構ありましたが、こうやって文字に起こしてみるとそれなりに色々やってきたみたいです。
2017年もよろしくお願いします。