Felsökning och profilering
En gyllene regel inom programmering är att kod inte gör det du förväntar dig att den ska göra, utan det du säger åt den att göra. Att överbrygga den luckan kan ibland vara ganska svårt. I den här föreläsningen går vi igenom användbara tekniker för att hantera felaktig och resurshungrig kod: felsökning och profilering.
Felsökning (debugging)
Printf-felsökning och loggning
“The most effective debugging tool is still careful thought, coupled with judiciously placed print statements” — Brian Kernighan, Unix for Beginners.
Ett första sätt att felsöka ett program är att lägga till utskriftssatser kring där du upptäckt problemet, och fortsätta iterera tills du extraherat tillräcklig information för att förstå vad som orsakar felet.
Ett andra sätt är att använda loggning i ditt program i stället för ad hoc-utskriftssatser. Loggning är bättre än vanliga utskriftssatser av flera skäl:
- Du kan logga till filer, sockets eller till och med fjärrservrar i stället för standardutmatning.
- Loggning stödjer allvarlighetsnivåer (som INFO, DEBUG, WARN, ERROR, etc.) som gör att du kan filtrera utdata därefter.
- För nya problem finns en rimlig chans att loggarna redan innehåller tillräcklig information för att upptäcka vad som går fel.
Här finns ett exempelprogram som loggar meddelanden:
$ python logger.py
# Rå utdata som med vanliga utskriftssatser
$ python logger.py log
# Loggformaterad utdata
$ python logger.py log ERROR
# Skriv bara ut ERROR-nivå och högre
$ python logger.py color
# Färgformaterad utdata
Ett av mina favorittips för mer läsbara loggar är att färgkoda dem.
Vid det här laget har du troligen märkt att terminalen använder färger för att göra saker mer läsbara.
Men hur gör den det?
Program som ls eller grep använder ANSI-escapekoder, som är särskilda teckensekvenser för att tala om för skalet att ändra färg på utdata.
Till exempel skriver echo -e "\e[38;2;255;0;0mDet här är rött\e[0m" ut meddelandet Det här är rött i rött i terminalen, så länge terminalen stödjer äkta färg (true color).
Om din terminal inte stödjer det (t.ex. macOS Terminal.app) kan du använda mer universellt stödda escapekoder för 16 färger, till exempel echo -e "\e[31;1mDet här är rött\e[0m".
Skriptet nedan visar hur man skriver ut många RGB-färger i terminalen (igen, så länge äkta färg stöds).
#!/usr/bin/env bash
for R in $(seq 0 20 255); do
for G in $(seq 0 20 255); do
for B in $(seq 0 20 255); do
printf "\e[38;2;${R};${G};${B}m█\e[0m";
done
done
done
Tredjepartsloggar
När du börjar bygga större programvarusystem stöter du sannolikt på beroenden som kör som separata program. Webbservrar, databaser och meddelandebrokers är vanliga exempel på sådana beroenden. När du interagerar med dessa system behöver du ofta läsa deras loggar, eftersom felmeddelanden på klientsidan inte alltid räcker.
Som tur är skriver de flesta program sina egna loggar någonstans i systemet.
I UNIX-system är det vanligt att program skriver loggar under /var/log.
Till exempel placerar webbservern NGINX sina loggar under /var/log/nginx.
På senare tid har system också börjat använda en systemlogg, som i allt högre grad är platsen där alla loggmeddelanden hamnar.
De flesta (men inte alla) Linux-system använder systemd, en systemdaemon som styr många delar av systemet, som vilka tjänster som är aktiverade och körs.
systemd lägger loggar under /var/log/journal i ett specialformat, och du kan använda kommandot journalctl för att visa meddelandena.
På macOS finns fortfarande /var/log/system.log, men fler och fler verktyg använder systemloggen som kan visas med log show.
På de flesta UNIX-system kan du också använda dmesg för att komma åt kärnloggen.
För att skriva till systemloggar kan du använda skalprogrammet logger.
Här är ett exempel på att använda logger och kontrollera att posten hamnat i systemloggen.
Dessutom har de flesta programspråk bindningar för loggning till systemloggen.
logger "Hello Logs"
# På macOS
log show --last 1m | grep Hello
# På Linux
journalctl --since "1m ago" | grep Hello
Som vi såg i föreläsningen om datahantering kan loggar vara väldigt utförliga och kräva viss bearbetning och filtrering för att få fram rätt information.
Om du ofta filtrerar i journalctl och log show kan du använda deras flaggor som gör en första filtrering av utdata.
Det finns också verktyg som lnav som ger bättre presentation och navigering i loggfiler.
Felsökare
När printf-felsökning inte räcker bör du använda en felsökare. Felsökare är program som låter dig interagera med ett programs körning, och de gör det möjligt att:
- Stoppa programkörningen när en viss rad nås.
- Stega genom programmet en instruktion i taget.
- Inspektera variabelvärden efter att programmet kraschat.
- Stoppa körningen villkorligt när ett givet villkor uppfylls.
- Och mycket mer.
Många programmeringsspråk levereras med någon form av felsökare.
I Python är detta Python Debugger pdb.
Här är en kort beskrivning av några kommandon som pdb stödjer:
- l(ist) - Visar 11 rader runt aktuell rad eller fortsätter föregående listning.
- s(tep) - Kör aktuell rad och stoppar vid första möjliga tillfälle.
- n(ext) - Fortsätter tills nästa rad i aktuell funktion nås eller funktionen returnerar.
- b(reak) - Sätter en brytpunkt (beroende på argument).
- p(rint) - Utvärderar uttrycket i aktuell kontext och skriver ut värdet.
Det finns även pp för utskrift med
pprint. - r(eturn) - Fortsätter tills aktuell funktion returnerar.
- q(uit) - Avslutar felsökaren.
Låt oss gå igenom ett exempel där vi använder pdb för att fixa följande felaktiga Python-kod (se föreläsningsvideon).
def bubble_sort(arr):
n = len(arr)
for i in range(n):
for j in range(n):
if arr[j] > arr[j+1]:
arr[j] = arr[j+1]
arr[j+1] = arr[j]
return arr
print(bubble_sort([4, 2, 1, 8, 7, 6]))
Notera att eftersom Python är ett tolkat språk kan vi använda pdb-skalet både för att köra kommandon och instruktioner.
ipdb är en förbättrad pdb som använder IPython-REPL och ger tab completion, syntax highlighting, bättre tracebacks och bättre introspektion samtidigt som samma gränssnitt som pdb-modulen behålls.
För mer låg-nivåprogrammering vill du sannolikt titta på gdb (och dess livskvalitetsförbättring pwndbg) samt lldb.
De är optimerade för felsökning av C-liknande språk men låter dig undersöka i princip vilken process som helst och se dess aktuella maskintillstånd: register, stack, programräknare osv.
Specialiserade verktyg
Även om det du försöker felsöka är en black-box-binär finns verktyg som kan hjälpa.
När program behöver utföra åtgärder som bara kärnan kan göra använder de systemanrop.
Det finns kommandon som låter dig spåra vilka systemanrop programmet gör.
I Linux finns strace, och i macOS/BSD finns dtrace.
dtrace kan vara svårt att använda eftersom det använder sitt eget språk, D, men det finns en wrapper som heter dtruss och ger ett gränssnitt mer likt strace (mer detaljer här).
Nedan följer exempel på hur strace eller dtruss används för att visa spårning av systemanropet stat vid körning av ls.
För en djupare genomgång av strace är den här artikeln och det här zinet bra läsning.
# På Linux
sudo strace -e lstat ls -l > /dev/null
# På macOS
sudo dtruss -t lstat64_extended ls -l > /dev/null
I vissa situationer kan du behöva titta på nätverkspaket för att förstå felet i programmet.
Verktyg som tcpdump och Wireshark är paketanalysverktyg som låter dig läsa innehållet i nätverkspaket och filtrera dem med olika kriterier.
För webbutveckling är utvecklarverktygen i Chrome/Firefox mycket praktiska. De har ett stort antal verktyg, inklusive:
- Källkod - inspektera HTML/CSS/JS-källkod för vilken webbplats som helst.
- Live-redigering av HTML, CSS och JS - ändra innehåll, stil och beteende för att testa (du kan själv se att skärmdumpar av webbplatser inte är giltiga bevis).
- Javascript-skal - kör kommandon i JS-REPL.
- Network - analysera tidslinjen för förfrågningar.
- Storage - undersök cookies och lokalt applikationslager.
Statisk analys
För vissa problem behöver du inte köra någon kod alls. Till exempel kan du, bara genom att noggrant läsa kod, upptäcka att loopvariabeln skuggar ett redan existerande variabel- eller funktionsnamn, eller att ett program läser en variabel innan den definierats. Det är här verktyg för statisk analys kommer in. Program för statisk analys tar källkod som indata och analyserar den med kodregler för att resonera om korrekthet.
I följande Python-exempel finns flera misstag.
Först skuggar loopvariabeln foo den tidigare definitionen av funktionen foo.
Vi skrev också baz i stället för bar på sista raden, så programmet kraschar efter sleep-anropet (som tar en minut).
import time
def foo():
return 42
for foo in range(5):
print(foo)
bar = 1
bar *= 0.2
time.sleep(60)
print(baz)
Verktyg för statisk analys kan identifiera den här typen av problem.
När vi kör pyflakes på koden får vi fel kopplade till båda programfelen.
mypy är ett annat verktyg som kan upptäcka typkontrollproblem.
Här varnar mypy för att bar först är en int och sedan castas till float.
Notera igen att alla dessa problem upptäcktes utan att köra koden.
$ pyflakes foobar.py
foobar.py:6: redefinition of unused 'foo' from line 3
foobar.py:11: undefined name 'baz'
$ mypy foobar.py
foobar.py:6: error: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]")
foobar.py:9: error: Incompatible types in assignment (expression has type "float", variable has type "int")
foobar.py:11: error: Name 'baz' is not defined
Found 3 errors in 1 file (checked 1 source file)
I föreläsningen om skalverktyg tog vi upp shellcheck, som är ett liknande verktyg för skalskript.
De flesta redigerare och IDE:er kan visa utdata från dessa verktyg direkt i redigeraren och markera varningar och fel. Detta kallas ofta lintning och kan också användas för andra typer av problem, som stilavvikelser eller osäkra konstruktioner.
I Vim kan insticksmodulerna ale eller syntastic ge den funktionen.
För Python är pylint och pep8 exempel på stil-linters, och bandit är ett verktyg för att hitta vanliga säkerhetsproblem.
För andra språk har människor sammanställt omfattande listor över användbara verktyg för statisk analys, till exempel Awesome Static Analysis (du kan titta på avsnittet Writing), och för linters finns Awesome Linters.
Ett komplement till stil-lintning är kodformatterare, som black för Python, gofmt för Go, rustfmt för Rust eller prettier för JavaScript, HTML och CSS.
Dessa verktyg autoformaterar koden så att den följer vanliga stilkonventioner för det aktuella språket.
Även om du kanske inte vill ge upp stilkontroll över koden hjälper standardiserat format andra att läsa din kod och gör dig bättre på att läsa andras (stilmässigt standardiserade) kod.
Profilering
Även om koden fungerar som väntat kanske det inte räcker om den samtidigt slukar all CPU eller allt minne. Algoritmkurser lär ofta ut big O-notation men inte hur man hittar flaskhalsar i program. Eftersom premature optimization is the root of all evil bör du lära dig både profileringsverktyg och övervakningsverktyg. De hjälper dig att förstå vilka delar av programmet som tar mest tid och resurser, så att du kan fokusera optimering där den spelar roll.
Timing
Precis som i felsökningsfallet räcker det i många scenarier att bara skriva ut tiden som koden tog mellan två punkter.
Här är ett exempel i Python med modulen time.
import time, random
n = random.randint(1, 10) * 100
# Hämta aktuell tid
start = time.time()
# Gör lite arbete
print("Sleeping for {} ms".format(n))
time.sleep(n/1000)
# Beräkna tid mellan start och nu
print(time.time() - start)
# Exempelutdata
# Sleeping for 500 ms
# 0.5713930130004883
Väggklocktid kan dock vara missvisande eftersom datorn kan köra andra processer samtidigt eller vänta på händelser. Det är vanligt att verktyg skiljer på Real, User och Sys tid. Generellt visar User + Sys hur mycket tid processen faktiskt spenderade på CPU:n (mer förklaring här).
- Real - Väggklocktid från start till slut, inklusive tid som tas av andra processer och blockeringstid (t.ex. väntan på I/O eller nätverk).
- User - Tid i CPU:n för användarkod.
- Sys - Tid i CPU:n för kärnkod.
Prova till exempel att köra ett kommando som gör en HTTP-förfrågan och prefixa det med time.
Med långsam anslutning kan du få utdata som nedan.
Här tog förfrågan över 2 sekunder, men processen tog bara 15 ms användartid och 12 ms kärntid på CPU.
$ time curl https://missing.csail.mit.edu &> /dev/null
real 0m2.561s
user 0m0.015s
sys 0m0.012s
Profilerare
CPU
Oftast när folk säger profilerare menar de egentligen CPU-profilerare, som är vanligast. Det finns två huvudtyper av CPU-profilerare: spårning och sampling. Spårningsprofilerare behåller en logg över varje funktionsanrop programmet gör, medan samplingsprofilerare provar programmet periodiskt (vanligen varje millisekund) och spelar in programmets stack. De använder dessa data för att presentera aggregerad statistik över vad programmet lagt mest tid på. Här finns en bra introduktion om du vill ha mer detaljer.
De flesta programmeringsspråk har någon kommandoradsprofilerare du kan använda för att analysera kod. De integreras ofta med fullfjädrade IDE:er, men i den här föreläsningen fokuserar vi på kommandoradsverktygen.
I Python kan vi använda modulen cProfile för att profilera tid per funktionsanrop.
Här är ett enkelt exempel som implementerar en rudimentär grep i Python:
#!/usr/bin/env python
import sys, re
def grep(pattern, file):
with open(file, 'r') as f:
print(file)
for i, line in enumerate(f.readlines()):
pattern = re.compile(pattern)
match = pattern.search(line)
if match is not None:
print("{}: {}".format(i, line), end="")
if __name__ == '__main__':
times = int(sys.argv[1])
pattern = sys.argv[2]
for i in range(times):
for file in sys.argv[3:]:
grep(pattern, file)
Vi kan profilera den här koden med kommandot nedan. Om vi analyserar utdata ser vi att I/O tar mest tid och att kompilering av regex också tar en del tid. Eftersom regex bara behöver kompileras en gång kan vi flytta ut det ur loopen.
$ python -m cProfile -s tottime grep.py 1000 '^(import|\s*def)[^,]*$' *.py
[omitted program output]
ncalls tottime percall cumtime percall filename:lineno(function)
8000 0.266 0.000 0.292 0.000 {built-in method io.open}
8000 0.153 0.000 0.894 0.000 grep.py:5(grep)
17000 0.101 0.000 0.101 0.000 {built-in method builtins.print}
8000 0.100 0.000 0.129 0.000 {method 'readlines' of '_io._IOBase' objects}
93000 0.097 0.000 0.111 0.000 re.py:286(_compile)
93000 0.069 0.000 0.069 0.000 {method 'search' of '_sre.SRE_Pattern' objects}
93000 0.030 0.000 0.141 0.000 re.py:231(compile)
17000 0.019 0.000 0.029 0.000 codecs.py:318(decode)
1 0.017 0.017 0.911 0.911 grep.py:3(<module>)
[omitted lines]
En invändning mot Pythons cProfile (och många profilerare) är att de visar tid per funktionsanrop.
Det kan snabbt bli ointuitivt, särskilt när du använder tredjepartsbibliotek eftersom interna anrop också räknas.
Ett mer intuitivt sätt att visa profileringsdata är tid per kodrad, vilket är vad radprofilerare (line profilers) gör.
Till exempel gör följande Python-kod en förfrågan till kurswebbplatsen och parsar svaret för att hämta alla URL:er på sidan:
#!/usr/bin/env python
import requests
from bs4 import BeautifulSoup
# Detta är en dekorator som talar om för line_profiler
# att vi vill analysera den här funktionen
@profile
def get_urls():
response = requests.get('https://missing.csail.mit.edu')
s = BeautifulSoup(response.content, 'lxml')
urls = []
for url in s.find_all('a'):
urls.append(url['href'])
if __name__ == '__main__':
get_urls()
Om vi använder Pythons cProfile får vi över 2500 rader utdata, och även med sortering är det svårt att se var tiden går.
En snabb körning med line_profiler visar i stället tidsåtgång per rad:
$ kernprof -l -v a.py
Wrote profile results to urls.py.lprof
Timer unit: 1e-06 s
Total time: 0.636188 s
File: a.py
Function: get_urls at line 5
Line # Hits Time Per Hit % Time Line Contents
==============================================================
5 @profile
6 def get_urls():
7 1 613909.0 613909.0 96.5 response = requests.get('https://missing.csail.mit.edu')
8 1 21559.0 21559.0 3.4 s = BeautifulSoup(response.content, 'lxml')
9 1 2.0 2.0 0.0 urls = []
10 25 685.0 27.4 0.1 for url in s.find_all('a'):
11 24 33.0 1.4 0.0 urls.append(url['href'])
Minne
I språk som C eller C++ kan minnesläckor göra att programmet aldrig frigör minne som det inte längre behöver. För att hjälpa vid minnesfelsökning kan du använda verktyg som Valgrind som hjälper dig att hitta minnesläckor.
I språk med garbage collection, som Python, är minnesprofilerare fortfarande användbara, eftersom objekt inte samlas upp så länge du har referenser till dem.
Här är ett exempelprogram och dess utdata när det körs med memory-profiler (notera dekoratorn, liksom i line-profiler).
@profile
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
del b
return a
if __name__ == '__main__':
my_func()
$ python -m memory_profiler example.py
Line # Mem usage Increment Line Contents
==============================================
3 @profile
4 5.97 MB 0.00 MB def my_func():
5 13.61 MB 7.64 MB a = [1] * (10 ** 6)
6 166.20 MB 152.59 MB b = [2] * (2 * 10 ** 7)
7 13.61 MB -152.59 MB del b
8 13.61 MB 0.00 MB return a
Händelseprofilering
Precis som med strace för felsökningsfallet kan du vilja ignorera kodens detaljer och behandla det du kör som en svart låda när du profilerar.
Kommandot perf abstraherar bort CPU-skillnader och rapporterar inte tid eller minne direkt, utan systemhändelser relaterade till programmet.
Till exempel kan perf enkelt rapportera dålig cachelokalitet, höga nivåer av page faults eller livelocks.
Här är en översikt:
perf list- Lista händelser som kan spåras med perf.perf stat COMMAND ARG1 ARG2- Hämtar antal för olika händelser relaterade till en process eller ett kommando.perf record COMMAND ARG1 ARG2- Spelar in körningen av ett kommando och sparar statistikdata i en fil som heterperf.data.perf report- Formaterar och skriver ut data iperf.data.
Visualisering
Profileringsutdata för verkliga program innehåller ofta stora mängder information på grund av programvaruprojekts inneboende komplexitet. Människor är visuella och ganska dåliga på att läsa stora mängder siffror och förstå dem. Därför finns många verktyg för att visa profileringsutdata på ett lättare sätt.
Ett vanligt sätt att visa CPU-profileringsdata för samplingsprofilerare är flamdiagram (flame graph), som visar en hierarki av funktionsanrop längs Y-axeln och tidsåtgång proportionellt längs X-axeln. De är också interaktiva, så du kan zooma in i specifika delar av programmet och se stackspår (prova att klicka i bilden nedan).
Anropsgrafer (call graphs) eller kontrollflödesgrafer (control flow graphs) visar relationer mellan underrutiner i ett program genom att representera funktioner som noder och funktionsanrop mellan dem som riktade kanter.
När detta kombineras med profileringsdata som antal anrop och tidsåtgång kan anropsgrafer vara väldigt användbara för att tolka programmets flöde.
I Python kan du använda biblioteket pycallgraph för att generera dem.

Resursövervakning
Ibland är första steget i prestandaanalys att förstå faktiskt resursutnyttjande. Program kör ofta långsamt när de är resursbegränsade, till exempel om minnet är för litet eller nätverket långsamt. Det finns mängder av kommandoradsverktyg för att undersöka och visa olika systemresurser som CPU-användning, minnesanvändning, nätverk, diskanvändning och så vidare.
- Allmän övervakning - Det kanske mest populära är
htop, en förbättrad version avtop.htopvisar olika statistik för processer som körs i systemet.htophar många alternativ och tangentbindningar. Några användbara är<F6>för att sortera processer,tför trädhierarki ochhför att växla trådar. Se ocksåglancesför en liknande implementation med bra UI. För aggregerade mått över alla processer ärdoolett annat smidigt verktyg som beräknar realtidsmått för många delsystem, som I/O, nätverk, CPU-utnyttjande, context switches osv. - I/O-operationer -
iotopvisar liveinformation om I/O-användning och är praktiskt för att se om en process gör tung disk-I/O. - Diskanvändning -
dfvisar mått per partition ochduvisar disk usage per fil i aktuell katalog. I dessa verktyg betyder flaggan-hhuman readable format. En mer interaktiv variant avduärncdu, där du kan navigera mappar och radera filer/mappar under navigeringen. - Minnesanvändning -
freevisar total mängd ledigt och använt minne i systemet. Minne visas också i verktyg somhtop. - Öppna filer -
lsoflistar filinformation om filer öppnade av processer. Det kan vara mycket användbart för att se vilken process som öppnat en viss fil. - Nätverksanslutningar och konfiguration -
sslåter dig övervaka statistik för inkommande och utgående nätverkspaket samt gränssnittsstatistik. Ett vanligt användningsfall är att ta reda på vilken process som använder en viss port på en maskin. För routing, nätverksenheter och gränssnitt kan du användaip. Notera attnetstatochifconfighar ersatts av dessa verktyg. - Nätverksanvändning -
nethogsochiftopär bra interaktiva CLI-verktyg för övervakning av nätverksanvändning.
Om du vill testa dessa verktyg kan du också skapa artificiell belastning med kommandot stress.
Specialverktyg
Ibland är black-box-benchmarking allt du behöver för att avgöra vilken programvara du ska använda.
Verktyg som hyperfine låter dig snabbt benchmarka kommandoradsprogram.
I föreläsningen om skalverktyg och skriptning rekommenderade vi till exempel fd framför find.
Vi kan använda hyperfine för att jämföra dem i vanliga uppgifter.
I exemplet nedan var fd 20x snabbare än find på min maskin.
$ hyperfine --warmup 3 'fd -e jpg' 'find . -iname "*.jpg"'
Benchmark #1: fd -e jpg
Time (mean ± σ): 51.4 ms ± 2.9 ms [User: 121.0 ms, System: 160.5 ms]
Range (min … max): 44.2 ms … 60.1 ms 56 runs
Benchmark #2: find . -iname "*.jpg"
Time (mean ± σ): 1.126 s ± 0.101 s [User: 141.1 ms, System: 956.1 ms]
Range (min … max): 0.975 s … 1.287 s 10 runs
Summary
'fd -e jpg' ran
21.89 ± 2.33 times faster than 'find . -iname "*.jpg"'
Precis som i felsökningsfallet kommer webbläsare också med fantastiska verktyg för profilering av sidladdning, så att du kan se var tiden går (laddning, rendering, skriptning osv). Mer information för Firefox och Chrome.
Övningar
Felsökning
-
Använd
journalctli Linux ellerlog showi macOS för att hämta superuser-åtkomster och kommandon under senaste dygnet. Om det inte finns några kan du köra några ofarliga kommandon somsudo lsoch kontrollera igen. -
Gör den här praktiska
pdb-handledningen för att bekanta dig med kommandona. För en djupare guide, läs den här. -
Installera
shellcheckoch prova att kontrollera skriptet nedan. Vad är fel i koden? Fixa det. Installera ett linter-tillägg i din redigerare så att varningarna visas automatiskt.#!/bin/sh ## Exempel: ett typiskt skript med flera problem for f in $(ls *.m3u) do grep -qi hq.*mp3 $f \ && echo -e 'Spellistan $f innehåller en mp3-fil i hög kvalitet' done -
(Avancerad) Läs om omvänd felsökning (reverse debugging) och få ett enkelt exempel att fungera med
rrellerRevPDB.
Profilering
-
Här finns implementationer av några sorteringsalgoritmer. Använd
cProfileochline_profilerför att jämföra exekveringstid för insertion sort och quicksort. Vad är flaskhalsen i respektive algoritm? Använd sedanmemory_profilerför att kontrollera minnesförbrukningen. Varför är insertion sort bättre? Kolla nu in-place-versionen av quicksort. Utmaning: användperfför att titta på cykelantal och cacheträffar/missar för varje algoritm. -
Här är lite (möjligen omständlig) Python-kod för att beräkna Fibonaccital med en funktion per tal.
#!/usr/bin/env python def fib0(): return 0 def fib1(): return 1 s = """def fib{}(): return fib{}() + fib{}()""" if __name__ == '__main__': for n in range(2, 10): exec(s.format(n, n-1, n-2)) # from functools import lru_cache # for n in range(10): # exec("fib{} = lru_cache(1)(fib{})".format(n, n)) print(eval("fib9()"))Lägg koden i en fil och gör den körbar. Installera förkrav:
pycallgraphochgraphviz. (Om du kan köradothar du redan GraphViz.) Kör koden som den är medpycallgraph graphviz -- ./fib.pyoch kontrollera filenpycallgraph.png. Hur många gånger anropasfib0? Vi kan göra bättre genom att memoizera funktionerna. Avkommentera de kommenterade raderna och generera bilderna igen. Hur många gånger anropar vi varjefibN-funktion nu? -
Ett vanligt problem är att en port du vill lyssna på redan används av en annan process. Låt oss lära oss hitta processens PID. Kör först
python -m http.server 4444för att starta en minimal webbserver som lyssnar på port4444. Kör i en annan terminallsof | grep LISTENför att skriva ut alla lyssnande processer och portar. Hitta processens PID och avsluta den medkill <PID>. -
Att begränsa en process resurser kan vara ett annat användbart verktyg i verktygslådan. Prova
stress -c 3och visualisera CPU-förbrukningen medhtop. Kör sedantaskset --cpu-list 0,2 stress -c 3och visualisera igen. Använderstresstre CPU:er? Varför inte? Läsman taskset. Utmaning: uppnå samma sak medcgroups. Prova att begränsa minnesförbrukningen förstress -m. -
(Avancerad) Kommandot
curl ipinfo.iogör en HTTP-förfrågan och hämtar information om din publika IP. Öppna Wireshark och försök sniffa request- och reply-paketen somcurlskickar och tar emot. (Tips: använd filtrethttpför att bara se HTTP-paket.)
Licensed under CC BY-NC-SA.