Skip to content

Instantly share code, notes, and snippets.

@nakamuray
Created March 30, 2019 08:16
Show Gist options
  • Save nakamuray/1eebc14c62cf50edf5d26867ef840ef6 to your computer and use it in GitHub Desktop.
Save nakamuray/1eebc14c62cf50edf5d26867ef840ef6 to your computer and use it in GitHub Desktop.
事前起動させておいた python process からの fork により、 python アプリケーションの起動高速化をしてみるテスト
import array
import json
import os
import socket
import sys
SOCKET_PATH = '/tmp/pyd.sock'
sock = socket.socket(socket.AF_UNIX)
sock.connect(SOCKET_PATH)
data = {
'argv': sys.argv[1:],
'env': dict(os.environ),
'cwd': os.getcwd(),
}
sock.sendmsg([json.dumps(data).encode('utf-8')], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, array.array('i', [0, 1, 2]))])
# TODO: signal
code = sock.recv(4096)
sys.exit(int(code))
import array
import json
import os
import socket
import sys
SOCKET_PATH = '/tmp/pyd.sock'
PRELOAD_MODULES = [
'IPython',
]
for m in PRELOAD_MODULES:
__import__(m)
sock = socket.socket(socket.AF_UNIX)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(SOCKET_PATH)
sock.listen()
while True:
print('daemon listening')
client, addr = sock.accept()
pid = os.fork()
if pid:
# parent
client.close()
# TODO: wait child to release zombies
continue
# child
sock.close()
pid2 = os.fork()
if pid2:
# new parent
# wait child and send status code to client
(_p, code) = os.waitpid(pid2, 0)
assert _p > 0
assert os.WIFEXITED(code)
code = os.WEXITSTATUS(code)
client.sendall(str(code).encode('utf-8'))
client.close()
else:
# new child
# setup environment and run code
# receive fds from socket
fds = array.array('i')
# XXX: waht values are correct to use as these?
maxfds = 256
msglen = 4096
msg, ancdata, flags, addr = client.recvmsg(msglen, socket.CMSG_LEN(maxfds * fds.itemsize))
print('msg', msg)
print('ancdata', repr(ancdata))
for cmsg_leve, cmsg_type, cmsg_data in ancdata:
if (cmsg_leve == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS):
fds.fromstring(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])
else:
print(repr(cmsg_data))
data = json.loads(msg)
sys.argv = data['argv']
os.environ.clear()
for k, v in data['env'].items():
os.environ[k] = v
os.chdir(data['cwd'])
print(fds)
for i in range(3):
os.dup2(fds[i], i)
os.close(fds[i])
client.close()
exec(open(sys.argv[0]).read())
sys.exit()
@nakamuray
Copy link
Author

python アプリケーションの起動高速化をするのに、事前に python process を起動して、必要なモジュールの読み込みまでを済ませておき、実際のアプリケーションはそこから fork する形で起動・実行すれば、モジュール読み込み時間が省けて速くなるのでは?と思い立ったので実験。
とりあえずのテストとして、手元の環境だと起動に微妙に時間がかかる ipython (IPython モジュール) を使う。

事前に

$ python3.7 daemon.py

としてデーモン側を起動・モジュール読み込みをさせておく。

普通に実行:

$ time python3.7 /usr/bin/ipython --help
...
python3.7 /usr/bin/ipython --help  0.65s user 0.07s system 99% cpu 0.725 total

事前起動した process を使って実行:

$ time python3.7 client.py /usr/bin/ipython --help
...
python3.7 client.py /usr/bin/ipython --help  0.07s user 0.01s system 30% cpu 0.263 total

... ということで、 ipython でヘルプを出すまでの時間は 0.725s → 0.263s となり、ひとまず高速化は出来たっぽい。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment