のページを見ればわかります。JSON serialization で1秒間で283,645件です。
ただし、その数字は現実的には殆ど意味がありません。
まず、一般的なWebサーバでは、ユーザー認証、データベースへのアクセス、HTML の作成等の処理をするので、その処理の時間の方がずっと長くなります。単に、Sanic の処理時間は無視できるというだけのことです。
また、Webアプリケーションで1秒間に1000件以上処理が必要になるケースは、それほどありません。まず、Niginx 等のWebサーバーの方で、静的ファイルの処理やキャッシュが可能です。そうしておけば、従業員1万人の会社の社内システムでも1秒間に1000件の処理が必要となるケースは少ないです。
現在でも、Dgango や Flask が主として使われているのは、一般的にはそれで十分なケースが多いためです。それで問題がある場合には、Python で Sanic を使うより、一般的にはスピードが速く並列処理の機能が充実している Go 言語を使った方がベターなケースが多いと思います。なお、ここで言っているのは、Sanic を使うなということではなくて、1秒間の処理件数が非常に多くなる場合には、その処理には Python そのものが向いていないことが多いという意味です。
閲覧数: 80回 · 石塚 正浩さんがリクエストした回答
ご回答ありがとう御座います。
>Python で Sanic を使うより、一般的にはスピードが速く並列処理の機能が充実している Go 言語を使った方がベターなケースが多いと思います。
>Python で Sanic を使うより、一般的にはスピードが速く並列処理の機能が充実している Go 言語を使った方がベターなケースが多いと思います。
なるほど、合わせて
まとめ
- のGILが、マルチスレッドを使ってもマルチコアの恩恵を受けれないようにしている。
こちらのブログが正しければ、一緒に考慮しますと、Pythonは、マルチコアCPUを生かせないプログラム言語だと言う事になります。AI(人工知能)や機械学習ライブラリを必要としないのであれば、PythonよりGo langの方が優れておりますが、Go lang用にAI(人工知能)や機械学習のライブラリが充実するか、PythonとフレームワークでマルチコアCPUを正式にサポートしていくかのどちらかが将来行われて解決して行くことを祈っております。なお、Go lang用でもTensorFlowと言うGoogleが開発したAI、機械学習、数値解析ライブラリが御座います。Go langでもこのAPIが使えますが、今はまだ、モデルの構築、学習が出来るのはPythonだけらしいので、将来改善される事を希望致します。
元の執筆者 · Wed
GIL の件ですが、重い処理を C/C++ で書いたり、C/C++ で書かれたライブラリーを使うと外すことができるので、普通は問題にはなりません。ただし、秒当たりの処理件数が非常に大量になると処理の衝突が避けられなくなるので、Python では効率が悪くなってしまいます。それで、Goの方を使用するように勧めているだけです。大規模なものでなければ、Python だけで開発しても問題にはなりません。大規模な場合にはPythonだけで耐えられるかどうかは計算とテストが必要になると思います。
機械学習に関しては、訓練の方は大量のデータを使って大量の計算をします。これに関しては、Python が得意で Go は苦手なので、Go で機械学習のモデルの構築、学習が出来るようになることは当分ありません。
一方で、学習した結果を使った処理では、結果を使うだけなので計算については軽い処理になります。もし、ここで大量の処理件数をこなさないといけないのであれば、上に述べて理由で Go の方が適しているので、Go 用のAPI があるという訳です。
TesorFlow も Go も Google が開発したものですが、Python と Go の特性を考慮した結果そうなっているので、一本化されることはないと思っていた方がいいでしょう。
質問を表示
回答者の紹介
Python3のブログを読んでの検証です。
もしかしたら、
Python2&3では、偽のマルチコア&マルチスレッドな為に、JAVAやGo lang(Go言語)の様な真のマルチコア&マルチスレッドとは違うのではないかと言う検証になります。
uvloopで、真のマルチコア&マルチスレッド対応?
と言う事が可能かも知れないです。
aon CEO 石塚正浩
tel:042-559-8638
iPhone:070-3861-5011
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
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 を掛けたものになります17.4. concurrent.futures – 並列タスク実行 — Python 3.6.1 ドキュメント
試しに30個ぐらいリクエストを送ってみる(Semaphoreは3)。
20個まで生成され、それ以降は再利用されているのを確認できた。 実装上ワーカースレッドの上限は変更できないけど、たしかに別に困るケースもなさそう。
uvloop
uvloopの方は、POSIXスレッド使わずに頑張ってるかもという話が出たので確認。
import uvloop # 中略 loop = uvloop.new_event_loop() asyncio.set_event_loop(loop)
実行すると、
おー ほんとだ。
おわりに
aodag先生とtokibito先生すごい… 自分はドキュメントを読みつつモヤモヤしたまま放置してたのですが、数十分で原因を見つけて教えてくれた。 自分もいよいよ明日から社会人なので、お二人目指して精進します。
aodag先生とtokibito先生のいるオープンコレクターさん、お仕事募集中だそうです(お世話になったので一言宣伝)。 ありがとうございました!
0 コメント:
コメントを投稿