Topic: lag startup script e import  (Letto 155 volte)

0 Utenti e 1 Visitatore stanno visualizzando questo topic.

Offline andi75

  • python unicellularis
  • *
  • Post: 3
  • Punti reputazione: 0
    • Mostra profilo
lag startup script e import
« il: Aprile 21, 2023, 21:50 »
Ciao, vorrei rilasciare in open source un piccolo progetto, un sistema di distribuzioni delle chiavi SSH (la cosa più "simile" che esista, giusto per capirci, è Keyper). Ci sarà una parte server, quasi finita, e una parte client usata da sshd.
Il motivo per cui ho deciso di chiedere aiuto (cosa che solitamente..... non è da me  :angel: ) è che da mesi sbatto la testa su un problema, senza trovare un soluzione. Soluzione che probabilmente neppure c'è, ma prima di rivedere tutto o dover passare ad altri linguaggi (immagino compilati, vedi go, perché tanto ho fatto profiling in perl e riscontro il medesimo problema) vorrei sapere se qualcuno si è imbattuto nello stesso problema.
Veniamo al dunque: sshd dovrebbe richiamare il mio script specificato tramite il parametro AuthorizedKeyClient. Lo script dovrebbe richiamare una API RESTful sul server e ottenere le authorized_keys per l'utente. La chiamata avviene, minimo, due volte perché c'è una preautenticazione e una autenticazione. Il problema è che, per quanto abbia ridotto all'osso lo script, fatto profiling, scelto i moduli meno onerosi sotto il profilo delle tempistiche di import, a seconda della macchina su cui viene eseguito, lo script porta via 50-100ms (per la maggior parte dovuti all'import, paradossalmente urllib.request impiega più tempo nell'import che a richiamare l'API e ottenere una risposta).
Tutta questa premessa per far capire che 100ms sembrano pochi... ma come minimo vanno moltiplicati per due... alla fine della fiera, il malcapitato che si connette in ssh ha un ritardo compreso fra 300 e 400ms. Quindi la connessione non è istantanea come quando sshd legge i files authorized_keys statici.
Python è il 3 (esattamente 3.9) che viene richiamato con i parametri -S (non ho bisogno del site e risparmio qualche ms) -B (so che potrebbe sembrare un controsenso, ma tanto lo script viene eseguito come nobody e non potrebbe scrivere la __pycache__ nella /usr/local/bin, oltre al fatto che non vorrei avere una cache la dentro). Le import sono normali import (inutile fare lazy o mettersi a scrivere più codice di quanto ne server, se tanto i moduli che importo li uso tutti, e non guadagnerei tempo di caricamento). Non mi proponete di creare un daemon che resta in esecuzione, apre un socket, e fare un altro clientino che comunica su quel socket: 1) ci ho già pensato e fatto i miei test 2) in ogni caso pure uno script che comunica su un socket, ridotto all'osso, impiega i suoi 40-50ms... su un i7 eh! 3) non ha senso trasformare un semplice client in un client+server (rasoio di Occam, inoltre fare un daemon ben fatto presuppone anche dover fare una unit systemd, uno script di init per chi aborrisce systemd, ecc.). 4) il tutto avrebbe voluto essere lightweight, sotto il profilo di semplicità e soprattutto non introdurre lag (in contrapposizione con sssd e simili o con chi aggancia sshd con AD  :sick:)


time ssh test exit

real    0m0,340s
user    0m0,019s
sys     0m0,000s

time ssh localhost exit

real    0m0,166s
user    0m0,013s
sys     0m0,004s

time /usr/local/bin/akclient.py -m akc -t ssh-ed25519 -u andrea -a http://auth/akc -p
ssh-ed25519 <blablabla> andrea@lonewolf
ecdsa-sha2-nistp256 <blablabla> andrea@lonewolf

real    0m0.096s
user    0m0.068s
sys     0m0.016s

python3 -BSX importtime /usr/local/bin/akclient.py -m akc -t ssh-ed25519 -u andrea -a http://auth/akc -p
import time: self [us] | cumulative | imported package
import time:      1156 |       1156 | _frozen_importlib_external
import time:       180 |        180 |   time
import time:       590 |        769 | zipimport
import time:       112 |        112 |     _codecs
import time:       948 |       1060 |   codecs
import time:       603 |        603 |   encodings.aliases
import time:      1078 |       2740 | encodings
import time:       361 |        361 | encodings.utf_8
import time:       306 |        306 | _signal
import time:       427 |        427 | encodings.latin_1
import time:        71 |         71 |     _abc
import time:       455 |        525 |   abc
import time:       498 |       1022 | io
import time:        90 |         90 |       _stat
import time:       297 |        386 |     stat
import time:      1646 |       1646 |     _collections_abc
import time:       248 |        248 |       genericpath
import time:       360 |        608 |     posixpath
import time:      1191 |       3829 |   os
import time:       333 |        333 |       types
import time:       921 |       1253 |     enum
import time:       193 |        193 |       _sre
import time:       381 |        381 |         sre_constants
import time:       520 |        900 |       sre_parse
import time:       434 |       1526 |     sre_compile
import time:        57 |         57 |           _heapq
import time:       248 |        305 |         heapq
import time:       171 |        171 |         itertools
import time:       197 |        197 |         keyword
import time:        80 |         80 |           _operator
import time:       436 |        515 |         operator
import time:       238 |        238 |         reprlib
import time:       106 |        106 |         _collections
import time:      1001 |       2530 |       collections
import time:        89 |         89 |       _functools
import time:      1373 |       3992 |     functools
import time:       120 |        120 |     _locale
import time:       197 |        197 |     copyreg
import time:       830 |       7915 |   re
import time:      1129 |       1129 |   gettext
import time:      1623 |      14494 | argparse
import time:       102 |        102 | fcntl
import time:       756 |        756 | locale
import time:       103 |        103 | errno
import time:       161 |        161 |   fnmatch
import time:       117 |        117 |   zlib
import time:       200 |        200 |     _compression
import time:       202 |        202 |       _weakrefset
import time:       811 |       1013 |     threading
import time:       336 |        336 |     _bz2
import time:       294 |       1841 |   bz2
import time:       256 |        256 |     _lzma
import time:       298 |        554 |   lzma
import time:        73 |         73 |   pwd
import time:        64 |         64 |   grp
import time:       687 |       3495 | shutil
import time:       147 |        147 |         _json
import time:       388 |        535 |       json.scanner
import time:       444 |        978 |     json.decoder
import time:       415 |        415 |     json.encoder
import time:       253 |       1645 |   json
import time:        19 |       1664 | json.encoder
import time:       730 |        730 |     signal
import time:       263 |        263 |     warnings
import time:       515 |        515 |     contextlib
import time:        87 |         87 |     msvcrt
import time:        54 |         54 |     _posixsubprocess
import time:        69 |         69 |     select
import time:       168 |        168 |       collections.abc
import time:        75 |         75 |       math
import time:       480 |        721 |     selectors
import time:       536 |       2972 |   subprocess
import time:      1696 |       4668 | platform
import time:       225 |        225 |   _socket
import time:        51 |         51 |   array
import time:      1546 |       1822 | socket
import time:      1649 |       1649 |   _ssl
import time:        61 |         61 |       _struct
import time:       142 |        203 |     struct
import time:       119 |        119 |     binascii
import time:       258 |        578 |   base64
import time:      3032 |       5259 | ssl
import time:       131 |        131 |   urllib
import time:        33 |         33 |     _bisect
import time:       143 |        176 |   bisect
import time:       123 |        123 |   email
import time:       272 |        272 |     _hashlib
import time:        74 |         74 |     _blake2
import time:       190 |        535 |   hashlib
import time:       764 |        764 |     http
import time:       432 |        432 |         email.errors
import time:        32 |         32 |                 _string
import time:       596 |        627 |               string
import time:       214 |        840 |             email.quoprimime
import time:       142 |        142 |             email.base64mime
import time:       116 |        116 |                 quopri
import time:        98 |        213 |               email.encoders
import time:       174 |        387 |             email.charset
import time:       670 |       2037 |           email.header
import time:        56 |         56 |               _random
import time:        29 |         29 |               _sha512
import time:       337 |        421 |             random
import time:        83 |         83 |               _datetime
import time:       699 |        781 |             datetime
import time:       795 |        795 |             urllib.parse
import time:       322 |        322 |               calendar
import time:       222 |        543 |             email._parseaddr
import time:       529 |       3068 |           email.utils
import time:       285 |       5389 |         email._policybase
import time:       512 |       6332 |       email.feedparser
import time:       229 |       6560 |     email.parser
import time:       134 |        134 |       uu
import time:       352 |        352 |       email._encoded_words
import time:       108 |        108 |       email.iterators
import time:       514 |       1107 |     email.message
import time:       799 |       9229 |   http.client
import time:       374 |        374 |     weakref
import time:       365 |        738 |   tempfile
import time:       154 |        154 |     urllib.response
import time:       209 |        363 |   urllib.error
import time:      1166 |      12458 | urllib.request
import time:       118 |        118 | _bootlocale
import time:       105 |        105 |     unicodedata
import time:       236 |        341 |   stringprep
import time:       569 |        909 | encodings.idna

Offline andi75

  • python unicellularis
  • *
  • Post: 3
  • Punti reputazione: 0
    • Mostra profilo
Re:lag startup script e import
« Risposta #1 il: Aprile 22, 2023, 17:03 »
PS: Ovviamente ho anche già provato (vedi https://peps.python.org/pep-3147/) a specificare un path (in tmpfs) per il bytecode... non guadagno nulla o comunque talmente poco che non vale la pena (anche perché poi avrebbe implicazioni sulla sicurezza). L'unico modo con cui ho guadagnato è creare una cache delle authorized_keys, e fare in modo che le richieste RESTful vengano inoltrate una sola volta e non due successive. Ho spostato gli import della parte HTTP/S/REST/JSON in modo che vengano importati i moduli necessari unicamente se ho bisogno di effettuare una chiamata, da cache mi evito alcuni import (ma non tutti, mi servono argparse, time, sys, os).
Tuttavia c'è ancora qualcosa che mi sfugge:


time python3 -Sc 'print ("Hello, world!")'
Hello, world!

real    0m0.020s
user    0m0.015s
sys     0m0.004s

time perl -e 'print "Hello, world!"'
Hello, world!
real    0m0.004s
user    0m0.004s
sys     0m0.000s

time /bin/echo "Hello, world!"
Hello, world!

real    0m0.002s
user    0m0.002s
sys     0m0.000s


Perché?

Fin'ora l'ho sempre saputo e me ne sono fregato perché ho sempre scritto scriptini "che devono fare qualcosa" ma non importa se lo fanno senza lag. Oppure architetture server.  Non avevo mai approfondito...

Inoltre noto che ci sono import di default:

python3 -S -X importtime -c 'print ("Hello, world!")'
import time: self [us] | cumulative | imported package
import time:      1761 |       1761 | _frozen_importlib_external
import time:       263 |        263 |   time
import time:       902 |       1165 | zipimport
import time:       174 |        174 |     _codecs
import time:      1404 |       1577 |   codecs
import time:      1157 |       1157 |   encodings.aliases
import time:      1659 |       4392 | encodings
import time:       634 |        634 | encodings.utf_8
import time:       538 |        538 | _signal
import time:       771 |        771 | encodings.latin_1
import time:       175 |        175 |     _abc
import time:       871 |       1045 |   abc
import time:       937 |       1981 | io
Hello, world!


Anche questo ahimè in tanti anni non lo avevo approfondito e sinceramente non era significativo, ora lo è.

Infine, per addentrarci ulteriormente (non riporto l'output perché sarebbe troppo esteso, chi è curioso può provare da se):

strace -tt python3 -Sc 'print ("Hello, world!")'


Comunque anche con strace il confronto con perl non regge, sembra che il problema siano gli import.
Ci sarebbero anche gli handlers dei segnali (alcuni ms "persi", stessa identica cosa in perl se volete divertirvi con strace).

Offline andi75

  • python unicellularis
  • *
  • Post: 3
  • Punti reputazione: 0
    • Mostra profilo
Re:lag startup script e import
« Risposta #2 il: Aprile 22, 2023, 17:12 »

strace -c /usr/local/bin/akclient.py -m akc -t ssh-ed25519 -u andrea -a http://auth/akc -p
ssh-ed25519 <pippo> andrea@lonewolf
ecdsa-sha2-nistp256 <pluto> andrea@lonewolf
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 24.77    0.000322           0       390        28 stat
 15.00    0.000195           0       221           read
 14.31    0.000186          10        18           getdents64
  9.92    0.000129           0       142         5 openat
  7.77    0.000101           0       229           fstat
  6.62    0.000086           0       147           close
  5.69    0.000074           0       192         3 lseek
  4.54    0.000059           0       120           mmap
  3.77    0.000049           1        26           brk
  3.38    0.000044           0       110        89 ioctl
  1.46    0.000019           4         4           lstat
  0.54    0.000007           2         3           fcntl
  0.54    0.000007           7         1           sysinfo
  0.46    0.000006           2         3         2 readlink
  0.38    0.000005           1         3           dup
  0.38    0.000005           1         3           getrandom
  0.31    0.000004           0        27           mprotect
  0.15    0.000002           0         8           munmap
  0.00    0.000000           0         4           write
  0.00    0.000000           0         8           poll
  0.00    0.000000           0        68           rt_sigaction
  0.00    0.000000           0         1           rt_sigprocmask
  0.00    0.000000           0         2         2 access
  0.00    0.000000           0        14           getpid
  0.00    0.000000           0         6           socket
  0.00    0.000000           0         6           connect
  0.00    0.000000           0         5           sendto
  0.00    0.000000           0         6           recvfrom
  0.00    0.000000           0         1           recvmsg
  0.00    0.000000           0         4           setsockopt
  0.00    0.000000           0         5         3 execve
  0.00    0.000000           0         2           uname
  0.00    0.000000           0         2           flock
  0.00    0.000000           0         1           unlink
  0.00    0.000000           0         2           arch_prctl
  0.00    0.000000           0         1           gettid
  0.00    0.000000           0        23           futex
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0         1           set_robust_list
  0.00    0.000000           0         1           epoll_create1
  0.00    0.000000           0         1           prlimit64
  0.00    0.000000           0         2           sendmmsg
------ ----------- ----------- --------- --------- ----------------
100.00    0.001300           0      1814       132 total


In effetti (non riporto l'output per intero, troppo lungo):

strace -e stat /usr/local/bin/akclient.py -m akc -t ssh-ed25519 -u andrea -a http://auth/akc

stat("/usr/local/sbin/python3", 0x7ffee0d5edf0) = -1 ENOENT (No such file or directory)
stat("/usr/local/bin/python3", 0x7ffee0d5edf0) = -1 ENOENT (No such file or directory)
stat("/usr/sbin/python3", 0x7ffee0d5edf0) = -1 ENOENT (No such file or directory)
stat("/usr/bin/python3", {st_mode=S_IFREG|0755, st_size=5479736, ...}) = 0
stat("/usr/bin/Modules/Setup.local", 0x7ffee0d5ab60) = -1 ENOENT (No such file or directory)
stat("/usr/bin/lib/python3.9/os.py", 0x7ffee0d5aa80) = -1 ENOENT (No such file or directory)
stat("/usr/bin/lib/python3.9/os.pyc", 0x7ffee0d5aa80) = -1 ENOENT (No such file or directory)
stat("/usr/lib/python3.9/os.py", {st_mode=S_IFREG|0644, st_size=39065, ...}) = 0
stat("/usr/bin/lib/python3.9/lib-dynload", 0x7ffee0d59be0) = -1 ENOENT (No such file or directory)
stat("/usr/lib/python3.9/lib-dynload", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/usr/lib/python39.zip", 0x7ffee0d5d460) = -1 ENOENT (No such file or directory)
stat("/usr/lib", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/usr/lib/python39.zip", 0x7ffee0d5d140) = -1 ENOENT (No such file or directory)
stat("/usr/lib/python3.9", {st_mode=S_IFDIR|0755, st_size=12288, ...}) = 0
stat("/usr/lib/python3.9", {st_mode=S_IFDIR|0755, st_size=12288, ...}) = 0
stat("/usr/lib/python3.9", {st_mode=S_IFDIR|0755, st_size=12288, ...}) = 0
stat("/usr/lib/python3.9/encodings/__init__.cpython-39-x86_64-linux-gnu.so", 0x7ffee0d5d580) = -1 ENOENT (No such file or directory)
stat("/usr/lib/python3.9/encodings/__init__.abi3.so", 0x7ffee0d5d580) = -1 ENOENT (No such file or directory)
stat("/usr/lib/python3.9/encodings/__init__.so", 0x7ffee0d5d580) = -1 ENOENT (No such file or directory)
stat("/usr/lib/python3.9/encodings/__init__.py", {st_mode=S_IFREG|0644, st_size=5588, ...}) = 0
stat("/usr/lib/python3.9/encodings/__init__.py", {st_mode=S_IFREG|0644, st_size=5588, ...}) = 0
stat("/usr/lib/python3.9", {st_mode=S_IFDIR|0755, st_size=12288, ...}) = 0
stat("/usr/lib/python3.9/codecs.py", {st_mode=S_IFREG|0644, st_size=36673, ...}) = 0
stat("/usr/lib/python3.9/codecs.py", {st_mode=S_IFREG|0644, st_size=36673, ...}) = 0
stat("/usr/lib/python3.9/encodings", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/usr/lib/python3.9/encodings", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/usr/lib/python3.9/encodings", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/usr/lib/python3.9/encodings/aliases.py", {st_mode=S_IFREG|0644, st_size=15677, ...}) = 0
stat("/usr/lib/python3.9/encodings/aliases.py", {st_mode=S_IFREG|0644, st_size=15677, ...}) = 0
stat("/usr/lib/python3.9/encodings", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/usr/lib/python3.9/encodings/utf_8.py", {st_mode=S_IFREG|0644, st_size=1005, ...}) = 0
stat("/usr/lib/python3.9/encodings/utf_8.py", {st_mode=S_IFREG|0644, st_size=1005, ...}) = 0
stat("/usr/lib/python3.9/encodings", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/usr/lib/python3.9/encodings/latin_1.py", {st_mode=S_IFREG|0644, st_size=1264, ...}) = 0
stat("/usr/lib/python3.9/encodings/latin_1.py", {st_mode=S_IFREG|0644, st_size=1264, ...}) = 0


...un delirio  :confused:

Offline GlennHK

  • python sapiens sapiens
  • ******
  • Post: 1.723
  • Punti reputazione: 1
    • Mostra profilo
    • La Tana di GlennHK
Re:lag startup script e import
« Risposta #3 il: Maggio 04, 2023, 09:24 »
Python è lento... non credo ci sia molto altro che si possa fare, dato che probabilmente non hai controllo su quale interprete fa girare il tuo script.


Se le performance sono così critiche forse è meglio switchare a qualcos'altro.