Sziasztok, a szabvány shell parancssorában viszek be egy többsoros szöveget:
user@host~: sh -c 'a=" asdf
> mnb"'; a=${a#*d}; echo "$a";'
ez jól működik, viszont egyelőre sehogy sem tudom megoldani, hogy a \newline-nál csapjon le ebből a többsoros szövegből.
Szerintetek hogy tudnám ezt kifejezni szabvány shell-ben?
- 638 megtekintés
Hozzászólások
Játszottam vele:
[user@host ~]$ a=" asdf
> mnb"
[user@host ~]$ p=$'\n'
[user@host ~]$ echo "${a#*$p}"
mnb
Erre vágytál?
Szerk.: Ez kimaradt: bash-5.0.11-1.fc31.x86_64
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
sajnos ez csak bash-hal működik, ott nem is lenne kérdés, de nekem dash vagy bármi alatt kéne, ezért is hangsúlyoztam és hangsúlyozom, hogy szabvány shell alatt kell, hogy működjön, és nem értem még, hogy hogy fejezzem ki a \newline-t, úgy, hogy működjön (ha változóba raktam, akkor is úgy tűnt, hogy csak sima karakterre ment)....
- A hozzászóláshoz be kell jelentkezni
Muszáj ezt shellben? Esetleg sed vagy awk nem jöhet szóba? Vagy tr? Mondanám azt, hogy a shell nem erre való, ráadásul van shell, ami tudja is. De, hogy épp dash-sel hogyan lehet megcsinálni, azt nem tudom. Van Fedorára is dash, de most így szombaton inkább mást csinálok. :) Egyébként bash közvetlenül is megengedi:
echo "${a#*$'\n'}"
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Most nem próbáltam ki, de read vagy cut nem játszik?
- A hozzászóláshoz be kell jelentkezni
Igen, szabyánv shellben érdekel, hogy ne csak más karakterre, hanem \n -re is tudjak vágni, ez a kihívás, és nem értem még a logikáját, az ellenőriztem, hogy \012 -ként rátolja el az újsort, tényleg, nincs semmi trükk, de a kifejezésben erre vágni még nem tudok. Szóval ez a kihívás:
read: a szabvány shellben a read a leírások szerint és a kísérleteim szerint TÉNYLEG csak STDIN-ről olvas.
Nem szeretném temp fájlba kiíratni a cuccot, nem szeretnék subshelt nyitni, amiből megint csak mindig macera az eredmény visszakapása, de mindképpen alapvetően az a kérdés, hogy a szabvány shellben egy változóban lévő tetszőleges oktet-sorozatot csak az ASCII vagy bármelyik karakterkódú karakternél el tudom-e vágni :)
- A hozzászóláshoz be kell jelentkezni
Még a newline-ra látok esélyt, de például a \0-ra már nem. Szóval, ha bináris feldolgozást szeretnél, írd meg C-ben! :)
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
egyelőre örülnék, hogy ha \newline-ra is el tudnám vágni szabvány shellben, ez a kihívás :)
- A hozzászóláshoz be kell jelentkezni
$ dash
$ v="első
> második
> harmadik"
$ echo "${v%%
> *}"
első
- A hozzászóláshoz be kell jelentkezni
hm, erre nem gondoltam, hogy közvetlenül vigyem be, ne \n formában, de műk, köszi! :)
- A hozzászóláshoz be kell jelentkezni
Ugyan bash-sel, de kipróbáltam a newline bevitelét közvetlenül, amikor még nem volt hozzászólás a topic-odban, működött is, de nem említettem, mert annyira ronda megoldásnak éreztem, hogy mindenképp valami jól megfogalmazható megoldásra vágytam. Igaz, akkor sem tudtam volna meg, hogy dash-sel működik-e, vagy sem. De mindegy is, már okafogyott, lényeg, hogy megvan a megoldás.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
ez egyfajta gondolkozás, amire nekem is ritkábban áll rá az agyam, de abszolút nyitott vagyok blikkfangosabb megoldásra, de én még nem találtam!
- A hozzászóláshoz be kell jelentkezni
Na most, ha mégis \n
-es formában akarod, akkor a Caro által javasolt read
is megoldás lehet:
#!/bin/sh
ORISTR="nulladik\nelső\nmásodik\nharmadik\nnegyedik"
get_nldl_str_elem()
{
I=0
echo "$1" | while read -r line;
do
if [ "$I" = "$2" ];
then
echo $line
exit
fi
I=$((I+1))
done
}
Használat:
echo `get_nldl_str_elem "$ORISTR" 1`
Ez persze nem a legszebb (POSIX shellben nincsenek tömbök, így nem lehet egyszer tömbbé alakítani), de működik. Aki akar finomíthat rajta.
- A hozzászóláshoz be kell jelentkezni
á, igen, ez nem ugrott be, hogy miért nem pipe-ból read-elek, ez kellett nekem, asszem:
user@host:~/d$ sh -c 'echo " asdf
mnb" | while read -r line; do echo "$line ."; done'
asdf .
mnb .
pseudo indexelt tömböket szoktam használni eval segítségével létrehozva, olvasva.
- A hozzászóláshoz be kell jelentkezni
> ez kellett nekem, asszem
Akkor megérte leírni, bár tagadhatatlan, hogy az sz által leírt megoldás jóval egyszerűbb.
- A hozzászóláshoz be kell jelentkezni
a="
asdf
mnb" ; a=${a#?}; # levágtam az első (\n) karaktert
a programnak egyébként ezen a szinten azt kéne tudnia, hogy megmondja, melyik sor hány karakter hosszú esetleges bevezető szóközöket is beleértve
- A hozzászóláshoz be kell jelentkezni
wc -m
sh -c 'echo " asdf
mnb" | while read -r line; do echo "$line" | wc -m; done'
5
4
- A hozzászóláshoz be kell jelentkezni
Én is while read-del szoktam, ráadásul, ha 1 sorban több változó van (pl. mysql oszlopok), akkor felsorolom, hogy read A B C
és a do után echo "$A - $B - $C" már rögtön változóban felhasználható.
- A hozzászóláshoz be kell jelentkezni
Jogos, bár itt csak egy változó volt, az egész sor.
- A hozzászóláshoz be kell jelentkezni
Csak az a baj, ugye a |-os megoldásokkal, hogy alhéjat nyitnak, és az eredményt nem tudom visszakapni a főfüggvényekben...
- A hozzászóláshoz be kell jelentkezni
Miért ne tudnád? Illusztrálnád egy konkrét példával, hogy mire gondolsz?
- A hozzászóláshoz be kell jelentkezni
Szerintem erre:
a=alma; echo korte | read a; echo "$a"
alma
Ugye azért, mert a pipe miatt önálló shellben az az 'a' változó egészen más memóriaterületen lett allokálva.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Dehát pont erre adtam egy példát, ahol függvényből kérjük le az egyes elemeket. Ezt minden további nélkül át lehet írni hosszra is.
#!/bin/sh
ORISTR="nulladik\nelső\nmásodik\nharmadik\nnegyedik"
get_nldl_str_elem_len()
{
I=0
echo "$1" | while read -r line;
do
if [ "$I" = "$2" ];
then
echo $line | wc -m
exit
fi
I=$((I+1))
done
}
Bármi, amit egy subshellben (UNIX shellben a "függvény" is csak egy subshell) kiírunk a stdout
-ra, azt el lehet kapni a subshell operátorokkal és fel lehet használni. Tényleg nem értem a problémát.
- A hozzászóláshoz be kell jelentkezni
ez igaz, ha bent hagyom függyvényben, akkor kezelhetőbb ebből a szempontból, a wc nem játszik, mert ugye shellben maradok, több irányban is gondolkozom, hogy mi legyen a kód, amiben végül is egy ASCII art karaktereit elemzem és kezelem, amihez először is meg akarom állapítani a dimenzióit. Most egyelőre azon az úton vagyok, hogy a fő ciklusomban karakterről karakterre olvasom, asszem végül is így fogom megkapni, hogy hány oszlopos, hány soros, ami nekem kell
- A hozzászóláshoz be kell jelentkezni
De miért ne játszana a wc
? Az is a stdout
-ra írkál, tehát el lehet kapni a kimenetét. Nem kell függvénybe se rakni:
strlen=`echo "$line" | wc -m`
Tudnál mutatni példát, hogy mi a probléma?
- A hozzászóláshoz be kell jelentkezni
Ennél azért ez egy cseppet olcsóbb:
strlen="${#line}"
- A hozzászóláshoz be kell jelentkezni
én is ebben gondolkodtam, csak sajnos tapasztaltam olyat, hogy a sor eleji szóközt nem számolja...
- A hozzászóláshoz be kell jelentkezni
$ v=" "
$ echo ${#v}
4
- A hozzászóláshoz be kell jelentkezni
önmagában nekem is jól csinálja, de aztán valahol meg azt írta, hogy 3 5 3, bár most van egy valamennyire működő megoldásom, de nyilván én is az egész feladatra a legjobb, legrövidebb és legalapabb-legbiztosabb megoldást szeretném, hogy a kapott értékekkel a legjobban dolgozhassak, igazából az lesz a program, hogy egy ascii-artot oszlopokra hasogassak és paramétertől függően csal egy részét mutassam rajzoljam ki a szabvány terminál bal oldalán...
- A hozzászóláshoz be kell jelentkezni
azért, mert ahogy írtam, szabvány shellben kell az eredmény, itt tart a projekt:
a="
asd
qwe r
v x";
l=${#a}; w=0; c=2; aw=0; while [ $c -le $l ]; do a=${a#?};
cc=`printf "%d" "\"$a"`;
if [ $cc -ne 10 ]; then aw=$((aw+1)); else echo $aw; aw=0; fi;
c=$((c+1)); done; echo
- A hozzászóláshoz be kell jelentkezni
A wc
egy külső parancs, amit szabvány shellből is meg lehet hívni. Ami pedig a subshell-t illeti, ha subshellbe rakod a ciklust is, akkor ki tudod echozni belőle a változódat:
#!/bin/sh
a="
asd
qwe r
v x"
MAXW=0
RMAXW=$(echo "$a" | (while read -r line;
do
CW=`echo "$line" | wc -m`
if [ "$MAXW" -le "$CW" ];
then
MAXW="$CW"
fi
done
echo $MAXW))
echo $RMAXW
Így a RMAXW
változóban megkaptad a legszélesebb sor hosszát, azaz az ASCII-art valódi szélességét. És ez POSIX shellből is megy.
- A hozzászóláshoz be kell jelentkezni
Ez soronként hány processzt pazarol el?
- A hozzászóláshoz be kell jelentkezni
"Pazarol el?" Lássuk csak...
Soronként három processzt indít: egy read
-et, egy echo
-t és egy wc
-t. Ha van egy "teljes képernyős" (80x25)-ös ASCII-artunk, akkor ez összesen 75 processzt fog indítani.
A másik karakterenként indít egy printf
-et. Az összesen 2025 processz egy 80x25-ös ASCII-art esetén, mivel ott a newline is számít.
Azaz 75-2025=-1950 processzt "pazarol el".
- A hozzászóláshoz be kell jelentkezni
"Soronként három processzt indít: egy read
-et, egy echo
-t"
Hogymi?!
A read mindenképpen shell builtin, különben nem tudná a shellben látható környezeti változó értékét változtatni.
Az echo lehet külön processz, lásd /bin/echo, de a legtöbb shellben builtin, éppen az extra processzek overheadjének elkerülése miatt. A printf szintén.
Cserébe viszont ott vannak a subshellek a pipe-oknak és a command substitution-öknek...
- A hozzászóláshoz be kell jelentkezni
> A read mindenképpen shell builtin, különben nem tudná a shellben látható környezeti változó értékét változtatni.
Azaz lehet külön és builtin is, de akkor legyen builtin. Az mínusz 25 processz.
> Az echo lehet külön processz, lásd /bin/echo, de a legtöbb shellben builtin, éppen az extra processzek overheadjének elkerülése miatt. A printf szintén.
A echo
és a printf
a POSIX shellben nem builtin.
> Cserébe viszont ott vannak a subshellek a pipe-oknak és a command substitution-öknek...
A subshellek? Két subshell van és mind a kettő a cikluson kívül. Az összesen kettő subshell. A pipe és a substitution ugyan ott van, de közben Zahy fentebb mutatott jobbat. Azaz:
CW=${#line}
És ezzel a rössel megspóroltuk a substitution-t, az echo
-t a pipe-t és a wc
-t is. Akkor soronként 0 processz. Szemben a 2025 printf
-fel és 2025 subsitutionnel.
- A hozzászóláshoz be kell jelentkezni
Az, hogy egy parancs a "Utilities" fejezetben kapott helyet, nem jelenti azt, hogy azt nem lehet builtinként implementálni.
Lásd pl. alias, cd, getopts, read, ulimit, amik mind abban a fejezetben vannak, de nem lehet őket külső parancsként implementálni.
A pipe és a command substitution is subshelleket használ.
- A hozzászóláshoz be kell jelentkezni
A ciklus belsejében jelen pillanatban nulla darab parancs, subshell és substitution található. De lentebb benchmarkoltam is egyet. Az eredmény magáért beszél. A karakterenkénti feldolgozás kb. ~130x pazarlóbb, mint a soronkénti.
- A hozzászóláshoz be kell jelentkezni
Igen, mostmár, mert Zahy lelőtte a poént.
Mondjuk a ciklus körüli subshell még mindig felesleges...
- A hozzászóláshoz be kell jelentkezni
Tényleg? Nézzük meg Zahy megoldása nélkül az eredeti javaslatomat:
#!/bin/sh
a=`cat "$1"`
MAXW=0
RMAXW=$(echo "$a" | (while read -r line;
do
CW=`echo "$line" | wc -m`
if [ "$MAXW" -le "$CW" ];
then
MAXW="$CW"
fi
done
echo $MAXW))
echo "Width: ""$RMAXW"" ch."
root@Csabi:~# time for i in {1..50}; do asciiwidetest1 asciiart.txt >/dev/null; done
real 0m3,235s
user 0m0,144s
sys 0m0,272s
Ez még így is ~11x gyorsabb, mint a karakterenkénti feldolgozás (~36 sec vs. ~3.2 sec).
> Mondjuk a ciklus körüli subshell még mindig felesleges...
Nem az. Ha leszedem, a végeredmény nulla lesz. Próbáld ki.
- A hozzászóláshoz be kell jelentkezni
"Tényleg? Nézzük meg Zahy megoldása nélkül az eredeti javaslatomat:
~3.2 sec"
Hát bizony, a pipe és wc sokkal lassabb, mint a shell-től megkérdezni a string hosszát.
echo "Width: ""$RMAXW"" ch."
Höhö.
"> Mondjuk a ciklus körüli subshell még mindig felesleges...
Nem az. Ha leszedem, a végeredmény nulla lesz. Próbáld ki."
De felesleges, csak nem jól szedted le:
RMAXW=$(while read -r line
do
......
done <"$1"
echo $MAXW)
echo "Width: $RMAXW ch."
- A hozzászóláshoz be kell jelentkezni
> Hát bizony, a pipe és wc sokkal lassabb, mint a shell-től megkérdezni a string hosszát.
Nem mondod... Lényeg megvolt, hogy a karakteres feldolgozáshoz képest még így is 11x gyorsabb volt?
> De felesleges, csak nem jól szedted le:
A megfordítás tényleg nem jutott eszembe. Viszont az érintett subshell egyszer futott az egész script alatt, úgyhogy nem túl sok vizet zavart.
- A hozzászóláshoz be kell jelentkezni
"Nem mondod... Lényeg megvolt, hogy a karakteres feldolgozáshoz képest még így is 11x gyorsabb volt?"
Nem, a lényeg az, hogy a subshellek és a külsö processzek drágák.
A karakterenkénti feldolgozás önmagában nem baj, a karakterenkénti subshell volt ott az igazi gond. A subshellek és a külső processz még soronként is gond: egy tisztességesen implementált karakterenkénti feldolgozás több mint ötször gyorsabb tud lenni, mint a soronként $(echo "$line" | wc -m)-et futtató változat.
"Viszont az érintett subshell egyszer futott az egész script alatt, úgyhogy nem túl sok vizet zavart."
Az érintett subshellel együtt a script elején lévő $(cat "$1") is mehetett a levesbe, és ezek együtt kb 30% gyorsulást jelentenek. Jobban megnézve, a ciklus körüli command substitution is teljesen felesleges, anélkül már 40%-nál járunk.
- A hozzászóláshoz be kell jelentkezni
> Nem, a lényeg az, hogy a subshellek és a külsö processzek drágák.
> A karakterenkénti feldolgozás önmagában nem baj, a karakterenkénti subshell volt ott az igazi gond.
Meg is válaszoltad magadnak. A másik karakterenként csinált subshellt és printf
call-t. Na, az az igazi pazarlás.
> A subshellek és a külső processz még soronként is gond: egy tisztességesen implementált karakterenkénti feldolgozás több mint ötször gyorsabb tud lenni, mint a soronként $(echo "$line" | wc -m)-et futtató változat.
Viszont, ha nincs soronként külső processz, meg subshell, akkor a soronkénti feldolgozás mindenképpen sokkal gyorsabb lesz, mint a karakterenkénti feldolgozás, mert kevesebbet kell interpreterből iterálni. (Egyébként mit értesz "tisztességesen implementált" karakterenkénti feldolgozáson?)
> Az érintett subshellel együtt a script elején lévő $(cat "$1") is mehetett a levesbe, és ezek együtt kb 30% gyorsulást jelentenek. Jobban megnézve, a ciklus körüli command substitution is teljesen felesleges, anélkül már 40%-nál járunk.
Ez mondjuk jogos, a cat
tényleg sokat nyomott.
- A hozzászóláshoz be kell jelentkezni
Tudom, hogy meg lehet, végül is a progi ezen fázisának ez lett kb a kódja:
a="
asd
qwe r
v x";
l=${#a}; w=0; c=2; aw=0; while [ $c -le $l ]; do a=${a#?};
cc=`printf "%d" "\"$a"`;
if [ $cc -ne 10 ]; then aw=$((aw+1));
elif [ $aw -gt $w ]; then w=$aw; aw=0; fi;
c=$((c+1)); done; echo "Width: $w ch.";
- A hozzászóláshoz be kell jelentkezni
jobbnak tűnik, a printf builtin :)
- A hozzászóláshoz be kell jelentkezni
A printf
mióta builtin a POSIX shellben? Az csak bash-ban builtin. POSIX-ban az külön utility. Direkt POSIX compliant megoldást kértél, miért a bash-specifikus megoldás most?
- A hozzászóláshoz be kell jelentkezni
A printf (és az echo) abban a shellben builtin, amelyik úgy implentálja. A Bash-en kívül ilyen a dash, ksh, ksh93, mksh/lksh, yash, FreeBSD és NetBSD /bin/sh, ...
- A hozzászóláshoz be kell jelentkezni
De ő szabvány shell-es megoldást kért. A szabvány a POSIX. Abban nem builtin egyik sem.
- A hozzászóláshoz be kell jelentkezni
hm, na most ezen elgondolkoztam, mert én bash-t nem használok, de dash-ban is van builtin printf, és olyan megfogalmazásban is olvastam, hogy ez POSIX builtin utility, most akkor, látva, hogy printf-em van a különféle shellekben, még dash-ban is, szóval használom, de végül is érdekel, hogy akkor most mi az abszolút igazság??
- A hozzászóláshoz be kell jelentkezni
A dash
egy konkrét shell, ami nem 100% POSIX compliant. (http://man7.org/linux/man-pages/man1/dash.1.html)
Például pont a builtin printf
-nek van is nem javított bugja, ami töri a POSIX-kompatibilitást (mondjuk téged pont nem érint).
Te egész konkrétan szabvány shell megoldást kértél, én azt adtam, egy szóval nem mondtad, hogy dash-specifikus legyen.
- A hozzászóláshoz be kell jelentkezni
viszonylag sokat olvastam most arról, hogy akkor mi a fene van az echo-val és a printf-fel, ha jól értem, azt mondja a POSIX, hogy valahol legyen:
https://www.unix.com/man-page/posix/1posix/printf/
a ${#var} is helyenként szarul működni látszott, úgy, hogy nem vette figyelembe a sor eleji szóközöket, ami viszont biztos nem opció nekem, szóval, most ez van, a tuti külső processzindításokat kerülném:
a="
asd
qwe r
v x";
l=${#a}; w=0; c=2; aw=0; while [ $c -le $l ]; do a=${a#?};
cc=`printf "%d" "\"$a"`;
if [ $cc -ne 10 ]; then aw=$((aw+1));
elif [ $aw -gt $w ]; then w=$aw; aw=0;
elif [ $aw -le $w ]; then aw=0;
else aw=-1; fi;
c=$((c+1)); done; echo "Width: $w ch.";
- A hozzászóláshoz be kell jelentkezni
Benchmarkoltam.
A tiéd:
#!/bin/sh
a=`cat "$1"`
l=${#a}; w=0; c=2; aw=0; while [ $c -le $l ]; do a=${a#?};
cc=`printf "%d" "\"$a"`;
if [ $cc -ne 10 ]; then aw=$((aw+1));
elif [ $aw -gt $w ]; then w=$aw; aw=0;
elif [ $aw -le $w ]; then aw=0;
else aw=-1; fi;
c=$((c+1)); done; echo "Width: $w ch.";
Az enyém:
#!/bin/sh
a=`cat "$1"`
MAXW=0
RMAXW=$(echo "$a" | (while read -r line;
do
CW=${#line}
if [ "$MAXW" -le "$CW" ];
then
MAXW="$CW"
fi
done
echo $MAXW))
echo "Width: ""$RMAXW"" ch."
A tesztfájl (RAW itt: http://oscomp.hu/depot/asciiart.txt):
888888888888888888888
s 88 ooooooooooooooo 88 s 888888888888888888888888888888888888888
S 88 888888888888888 88 SS 888888888888888888888888888888888888888
SS 88 888888888888888 88 SSS 8888 - --+ 8888
SS 88 ooooooooooooooo 88 sSSS 8888 o8888888o | 8888
sSS 88 888888888888888 88 SSSSS 8888 o88888888888o 8888
SSS 88 888888888888888 88 SSSSS 8888 8888 88888 8888 | 8888
SSS 88 ooooooooooooooo 88 SSSSS 8888 o888 888 888o 8888
SSS 88 888888888888888 88 SSSSS 8888 8888 888 8888 8888
SSS 88 888888888888888 88 SSSSS 8888 8888 888 8888 8888
SSS 88 oooooooooo 88 SSSSS 8888 8888o o888o o8888 8888
SSS 88 8888888888 .::. 88 SSSSS 8888 988 8o88888o8 88P 8888
SSS 88 oooooooooo :::: 88 SSSSS 8888 8oo 9888889 oo8 8888
SSS 88 8888888888 `' 88 SSSSS 8888 988o o88P 8888
SSS 88ooooooooooooooooo88 SSSS 8888 98888888P 8888
SSS 888888888888888888888__SSSS 8888 8888_____
SSS | * * * )8c8888 SSSS 888888888888888888888888888888888888888
SSS 888888888888888888888. SSS 888888888888888888888888888888888888888
SSS 888888888888888888888 \_ SSsssss oooooooooooooooooooooooooooo ssss
SSS 888888888888888888888 \\ __SS 88+-8+-88============8-8==88 S
SSS 888888888888888888888-. \\ \ S 8888888888888888888888888888
SSS 888888888888888888888 \\\ \\ `.__________.' ` .
SSS 88O8O8O8O8O8O8O8O8O88 \\. \\______________________________`_.
SSS 88 el cheapo 8O8O8O88 \\ '. \|________________________________|
SS 88O8O8O8O8o8O8O8O8O88 \\ '-.___
S 888888888888888888888 /~ ~~~~~-----~~~~---.__
.---------------------------------------------------. ~--.
\ \______\ __________________________________________\-------^.-----------.
:' _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ `\ \
::\ ,\_\,\_\_\_\_\\_\_\_\_\\_\_\_\_\,\_\_\_\ \ o '8o 8o .
|::\ -_-_-_-_-_-_-_-_-_-_-_-_-_-___ -_-_-_ _ _ _ _ \ 8o 88 88 \
|_::\ ,\_\_\_\_\_\_\_\_\_\_\_\_\_\___\,\_\_\_\,\_\_\_\_\ \ 88 \
`:\ ,\__\_\_\_\_\_\_\_\_\_\_\_\_\ \,\_\_\_\,\_\_\_\ \ \ 88 .
`:\ ,\__\_\_\_\_\_\_\_\_\_\_\_\____\ _ ,\_\_\_\_\ \ 88o .|
:\ ,\____\_\_\_\_\_\_\_\_\_\_\____\ ,\_\ _,\_\_\_\ \ \ 'ooooo'
:\ ,\__\\__\_______________\__\\__\,\_\_\_\,\___\_\_\ \
`\ -- -- --------------- -- -- - - - --- - - )____________
`--------------------------------------------------'
A teszt:
root@Csabi:~# time for i in {1..50}; do asciiwidetest0 asciiart.txt >/dev/null; done
real 0m35,913s
user 0m3,760s
sys 0m11,236s
root@Csabi:~# time for i in {1..50}; do asciiwidetest1 asciiart.txt >/dev/null; done
real 0m0,282s
user 0m0,008s
sys 0m0,008s
Azaz a tiéd 50 elemzést kb. 36 másodperc alatt csinált meg, az enyém pedig kb. 1/4 másodperc alatt. A különbség 127.351x-es. Ekkora overheaddel jár, ha karakterenként dolgozol fel egy interpretált nyelvből a soronkénti feldolgozáshoz képest.
- A hozzászóláshoz be kell jelentkezni
izgalmas, köszi, kezd kifejezetten érdekes lenni számomra, hogy ez a fajta soronként feldolgozás megbízható-e, mert elég kompatibilisnek tűnik, és nekem tetszik, ha működik, akkor mégis ezen az úton mennék tovább, tesztelem én is, kösz
- A hozzászóláshoz be kell jelentkezni
Nincs mit, de ha azzal kezded, hogy az a feladat, hogy "igazából az lesz a program, hogy egy ascii-artot oszlopokra hasogassak és paramétertől függően csal egy részét mutassam rajzoljam ki a szabvány terminál bal oldalán", akkor már rég mondtam volna, hogy ne szenvedj shell-el. Ezt jobb megírni mondjuk C-ben:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
int f2mem(char *path, char **dst_out, size_t *size)
{
FILE *f;
size_t l;
char *buf;
*dst_out = NULL;
f = fopen(path, "rb");
if (f == NULL)
{
return 1;
}
fseek(f, 0, SEEK_END);
l = ftell(f);
buf = malloc(l);
if (buf == NULL)
{
fclose(f);
return 2;
}
fseek(f, 0, SEEK_SET);
fread(buf, 1, l, f);
fclose(f);
*size = l;
*dst_out = buf;
return 0;
}
int main(int argc, char *argv[])
{
int r, i;
char *buf, c, col, row, start, stop;
bool copy;
size_t size;
if (argc != 4)
{
fprintf(stderr, "Usage: getcols <file> <start> <stop>\n");
return 0;
}
start = strtol(argv[2], NULL, 10);
if (errno != 0)
{
fprintf(stderr, "Erroneous start column: %s\n", argv[2]);
return 3;
}
stop = strtol(argv[3], NULL, 10);
if (errno != 0)
{
fprintf(stderr, "Erroneous stop column: %s\n", argv[3]);
return 4;
}
r = f2mem(argv[1], &buf, &size);
if (r == 1)
{
fprintf(stderr, "Cannot open file: %s\n", argv[1]);
return 1;
}
if (r == 2)
{
fprintf(stderr, "Cannot allocate memory.\n");
return 2;
}
i = 0;
row = 0;
col = 0;
copy = false;
while (i < size)
{
c = buf[i++];
if (c == 10)
{
col = 0;
++row;
copy = false;
}
else
{
if ((col == start) && (!copy))
{
copy = true;
}
if (copy)
{
fprintf(stdout, "\033[%d;%dH%c", row, col - start, c);
}
if ((col == stop) && (copy))
{
copy = false;
}
++col;
}
}
free(buf);
}
Ez pontosan azt csinálja, amit szeretnél: N db oszlopot kicopy-z a fájlból és kiírja a terminálra. Leteszteltem, működik, persze bug lehet benne.
Sz*rk: Frissítettem a programot: beleraktam, hogy magától mozgassa a képernyő bal felére a tartalmat, escape szekvenciákkal.
- A hozzászóláshoz be kell jelentkezni
impozáns és meg fogom nézni! De nekem most megvan az okon, hogy miért shellel kell kiúsztatni :)
- A hozzászóláshoz be kell jelentkezni
És titok, hogy mi az ok? :)
- A hozzászóláshoz be kell jelentkezni
Kíváncsi lettem. ;)
real 0m2.624s
user 0m1.417s
sys 0m1.194s
real 0m0.015s
user 0m0.007s
sys 0m0.008s
real 0m0.005s
user 0m0.002s
sys 0m0.003s
Az utóbbi megoldás:
#!/bin/sh
awk \
'{
if ( length() > L )
L=length()
}
END {
print L
}' $1
Bár nem felel meg a specifikációnak. :(
- A hozzászóláshoz be kell jelentkezni
Iderakom a C programodat is:
real 0m0.002s
user 0m0.001s
sys 0m0.001s
Bár ekkora és ilyen bonyolult C programot még nem láttam. ;)
A lényeg: Vagy 30 éve junixozom, dolgoztam fel már 30M sort is, ami ennél sokkal bonyolultabbnak tűnt ;), de szöveget soha nem shellben, mert nem arra való. (Mert akkor sqlben is lehetne.:)) Úgy 1-1,5M rekordig awk - ha nincs különös követelmény, felette C.
- A hozzászóláshoz be kell jelentkezni
Az nem baj, hogy a benchmarkolt shell kódok és a C kód teljesen mást csinál? :)
Biztos jó amúgy erre az oszlopcopyzásra az awk
is, de én meg azt nem ismerem annyira. (Fogadnék egy teszkógazdaságos gumikecskepörköltbe, hogy tovább tartott volna megkeresnem, hogy awk
-ból hogy oldom meg, mint ameddig megírtam a C-s verziót.) Amúgy is, célfeladatra céleszköz; ez pontosan azt csinálja, amit sas kért, nem kell bűvészkedni mindenféle paraméterekkel, ráadásul ez 10 kB, az awk
meg ~650 kB. :P
- A hozzászóláshoz be kell jelentkezni
:-D
Némi túlzással, ha odalépsz egy programozó mögé, akkor első pillantásra el sem tudod dönteni, hogy C-ben, awk-ban vagy shellben írja a kódot. Szóval a mondásodból csak annyi igaz, hogy nemigen dolgoztál még awk-ban.
A használt programnyelv kiválasztásánál egyik szempot csak annyi, hogy a C:awk kb. 1:6 futásidőt igényel. De tesztelésre is jó az awk, mert utána berakod a kódot C-be, vagy PIC assemblerbe :-D és pillanatok alatt át lehet írni. Legalábbis egyszerűbb feladatoknál, ahol nincs sok regexp. Ha meg van, akkor az awk győz az asszociatív tömbökkel. (Magyarázat: Bonyolultabb szöveges feldolgozásoknál általában szükség van szótárakra, sőt javító szótárakra is. Bár lehet ilyet írni, illetve biztosan meg is írták már mindenféle nyelvre, de az awk sokkal gyorsabb feladatmegoldást tesz lehetővé.)
Ehhez a feladathoz nagyságrendileg legfeljebb a rejtélyes substr() függvényre lehet szükség. ;)
A C programod hatalmas, biztosan jól meg van írva, de fix vagy maximált hosszú sorok feldolgozáskor
- nincs buffer - csak egy (max két) sornyi (nincs fopen, seek, meg ilyen marhaságok)
- nincs malloc()
- a fentiek alapján nyugodtan lehet használni a marhák szerint obsolete gets() függvényt
Ezzel 2/3 program ki is van dobva.
Az awk tényleg marhanagy. Én meg POWER-en programoztam, ahol jóval kisebbek a binárisok, mint egy pécén. :P
Ha már marháskodunk, ahol a tesztelést végeztem (linux) az awk mérete csak 418k, a hátam mögött (freebsd) csak 167k. :)
- A hozzászóláshoz be kell jelentkezni
> Némi túlzással, ha odalépsz egy programozó mögé, akkor első pillantásra el sem tudod dönteni, hogy C-ben, awk-ban vagy shellben írja a kódot. Szóval a mondásodból csak annyi igaz, hogy nemigen dolgoztál még awk-ban.
Nem igazán világos, hogy mi az összefüggés a két mondat között, pláne aközött, amit én mondtam...
> A használt programnyelv kiválasztásánál egyik szempot csak annyi, hogy a C:awk kb. 1:6 futásidőt igényel. De tesztelésre is jó az awk, mert utána berakod a kódot C-be, vagy PIC assemblerbe :-D és pillanatok alatt át lehet írni. Legalábbis egyszerűbb feladatoknál, ahol nincs sok regexp. Ha meg van, akkor az awk győz az asszociatív tömbökkel. (Magyarázat: Bonyolultabb szöveges feldolgozásoknál általában szükség van szótárakra, sőt javító szótárakra is. Bár lehet ilyet írni, illetve biztosan meg is írták már mindenféle nyelvre, de az awk sokkal gyorsabb feladatmegoldást tesz lehetővé.)
Asszem elkerülte a figyelmedet a "célfeladatra céleszköz" kitétel. Ha regexp hegyekre lett volna szükség, nem veszem elő a C-t. De itt pár oszlopot kellett kicopyzni.
> Ehhez a feladathoz nagyságrendileg legfeljebb a rejtélyes substr() függvényre lehet szükség. ;)
> A C programod hatalmas, biztosan jól meg van írva, de fix vagy maximált hosszú sorok feldolgozáskor
> - nincs buffer - csak egy (max két) sornyi (nincs fopen, seek, meg ilyen marhaságok)
> - nincs malloc()
> - a fentiek alapján nyugodtan lehet használni a marhák szerint obsolete gets() függvényt
> Ezzel 2/3 program ki is van dobva.
Csak előrántottam a már ötvenezer éve megírt "rántsunk be egy fájlt memóriába" függvényemet és kész; lévén ASCII-artról beszélünk, tuti befér a memóriába, onnantól kezdve meg édes mindegy, hogy most hogy is van bufferelve. (De ha akarod, kimérheted, szerintem a soronkénti beolvasás lassabb lesz, mint az egybeni, mert az annyiszor annyi blokkművelet, még akkor is, ha az FS a cache-ből rántja elő a szektort.)
> Az awk tényleg marhanagy. Én meg POWER-en programoztam, ahol jóval kisebbek a binárisok, mint egy pécén. :P
> Ha már marháskodunk, ahol a tesztelést végeztem (linux) az awk mérete csak 418k, a hátam mögött (freebsd) csak 167k. :)
Most szét vannak szedve a PowerMac-ek, így nem tudom lemérni mennyi lenne PPC-n a bináris, de valószínűleg ha x86-on 10 kB volt, akkor POWER-en se lesz nagyobb, mint 418k vagy 167k. :P
- A hozzászóláshoz be kell jelentkezni
Első idézet magyarázata: Valószínűleg nem sokat kellene tanulnod az awk használatához. Van egy-két specifikum, de azt felszedi az ember menet közben.
Második idézet magyarázata: Csak úgy l'art pour l'art írogattam az awk szépségéről és hasznosságáról. Semmi bajom a C-vel.
Harmadik idézet magyarázata: Az indoklás elfogadható szintű. ;)
A soronkénti beolvasás tényleg lassabb. Ha tudjuk, hogy ASCII-artról van szó, akkor kell mondani egy gets()-t és máris ott csücsül a default 5k bufferben a szöveg. Ezt az oprendszer és a shell biztosítja. Az awk esetén is.
A "célfeladatra céleszköz", ok nézd meg a kódomat. Gyorstalpaló és egyszerűsített awk tanfolyam:
- Egy program 3 opcionális részből állhat: BEGIN{} {(body)} END{}
- A body minden beolvasott sorra végrehajtódik, amíg a sorok el nem fogynak.
- A length() függvény paraméter nelkül az $0 változó hosszát adja vissza, ami nem más, mint a beolvasott sor. Ezért is nem féltem 2x meghívni, mert ez a hossz már a beolvasáskor előáll.
- A numerikus változók 0 értékre inicializáltak. (Ide ennyi elég.)
Itt a vége. Ha eddig nem tudtad volna elolvasni a kódomat (amit azért nem hiszek), akkor máris menni fog.
Nem feledvén, hogy a programod jóval többet elvégez...
#include <stdio.h>
#include <string.h>
main()
{
int L=0,L1=0;
char Buffer[0x100];
while ( gets(Buffer) )
{
L1=strlen(Buffer);
if ( L1 > L )
L=L1;
}
}
Ez kb. az awk programnak megfelelő valami. Ebből vagy az awk-ból előállitva a programodat sokkal szebb lesz, és kb. ugyanannyi sorral kell kiegészíteni.. ;)
Az látszik, hogy a feladatot megoldva mindkét nyelven rövid a fejlesztési idő és hasonló az eredmény.
Bár nem ismerem a PowerMac-et, de mondjuk győztél. ;(
- A hozzászóláshoz be kell jelentkezni
Kösz az awk
gyorstalpalót. Majd lehet, hogy egyszer jobban belemélyedek.
A mellékelt C programmal csak egy baj van, hogy a gets()
a stdin
-ről olvas be, azaz ahhoz, hogy ez a program bármit csináljon be kell pipe-olni neki az ASCII-artot, tehát nem standalone; ahhoz akkor fgets()
kell, tehát a fájlkezelést nem lehet megúszni. Az előre a veremben allokált buffer persze a malloc()
-ot feleslegessé teszi, de hát csak gyorsan elővettem a saját fájlberántómat...
Szóval, ha kiegészítjük a C példád, hogy a paramétereket is lekezelje, meg ne csak a hosszát nézze a sornak, hanem copyzza ki, amit kell (BTW copy, írtad a substr()
függvényt; na az C-ben nincs, csak C++-ban: a string
ősosztály egyik metódusa, meg amúgy is, azzal nem sokra megyünk, mert a terminál bal oldalán kell "lebegtetni" a kicopyzott oszlopokat), akkor ezt kapjuk:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
#define SZ_LINEBUF 4096
int main(int argc, char *argv[])
{
FILE *f;
char buf[SZ_LINEBUF], col, row, start, stop, c;
bool copy;
if (argc != 4)
{
fprintf(stderr, "Usage: getcols <file> <start> <stop>\n");
return 0;
}
start = strtol(argv[2], NULL, 10);
if (errno != 0)
{
fprintf(stderr, "Erroneous start column: %s\n", argv[2]);
return 3;
}
stop = strtol(argv[3], NULL, 10);
if (errno != 0)
{
fprintf(stderr, "Erroneous stop column: %s\n", argv[3]);
return 4;
}
f = fopen(argv[1], "rb");
if (f == NULL)
{
fprintf(stderr, "Cannot open file: %s\n", argv[1]);
return 1;
}
row = 0;
while (!feof(f))
{
fgets(buf, SZ_LINEBUF, f);
col = 0;
copy = false;
while ((c = buf[col]) != 0)
{
if ((col == start) && (!copy))
{
copy = true;
}
if (copy)
{
fprintf(stdout, "\033[%d;%dH%c", row, col - start, c);
}
if ((col == stop) && (copy))
{
copy = false;
}
++col;
}
++row;
}
fclose(f);
}
Ez 60 sor a másik felállás 100 sora helyett, szóval az "Ezzel 2/3 program ki is van dobva." az inkább jó 1/3-nak (pontosabban: 40%-nak). :P A bináris mérete is 10k-ról 6k-ra ment össze. Amitől megszabadultunk: a berántós függvény, a malloc()
és lekezelése, valamint a sortörések külön kezelése (fgets()
itt intézi helyettünk); szóval annyira azért nem volt bloated az a C program, amit adtam. :P
Sebességben kb. semmi különbség nincs a kettő között; pár tízezreléknyi különbséget lehet csak mérni az egyben beolvasós javára:
root@Csabi:~# time for i in {1..50000}; do getcols_b asciiart.txt 30 60 >/dev/null; done
real 2m0,616s
user 0m3,072s
sys 0m31,368s
root@Csabi:~# time for i in {1..50000}; do getcols asciiart.txt 30 60 >/dev/null; done
real 2m0,598s
user 0m2,956s
sys 0m31,396s
De gondolom ez az fs-cache miatt van így. Valami retroplatformon biztos nagyobb lenne a különbség. :P
- A hozzászóláshoz be kell jelentkezni
A mellékelt C programmal csak egy baj van, hogy a
gets()
astdin
-ről olvas be, azaz ahhoz, hogy ez a program bármit csináljon be kell pipe-olni neki az ASCII-artot, tehát nem standalone
Aha. Ilyenkor ezt szoktam ajánlani. ;)
program < asciiart.txt
Az a "visszakacsacsőr" olyat tesz, mintha a program ezt csinálta volna: openat(STDIN_FILENO,"asciiart.txt",O_RDONLY)
Tehát itt egy darab pipeline sincs. Ezt átirányításnak hívják.
Mivel csak a "program" és a shell vesz részt a Nagy Műveletben, ezért "standalone". (WTF standalone?)
Valójában a következő történik:
keyboard > (STDIN_FILENO) shell ((STDOUT_FILENO) ---> buffer > screen ; (STDERR_FILENO) > screen)
- program indításakor
keyboard > (STDIN_FILENO) program ((STDOUT_FILENO) ---> buffer > screen ; (STDERR_FILENO) > screen)
- átirányításkor
asciiart.txt > (STDIN_FILENO) program ((STDOUT_FILENO) ---> buffer > screen ; (STDERR_FILENO) > screen)
Persze a keyboard és a file elemhez is tarozik buffer, pontosan akkora, amekkora őket megilleti. Hacsak mást nem mondasz nekik. ;)
A shell és a program is ugyanazokat a fd-ket használja, de ez még viszonylag egyszerű eset. Ha sok program fut, akkor már érdemes úrrá lenni a kavarodáson.
írtad a
substr()
függvényt; na az C-ben nincs
Ki hitte volna? ;)
De ilyen van:
buffer[j+1]=0;
puts(&buffer[i]);
Ami kb. megfelel a "print substr(buffer,i+1,j-i+1)" awk kifejezésnek.
De gondolom ez az fs-cache miatt van így. Valami retroplatformon biztos nagyobb lenne a különbség. :P
Hááát, ezt 10M alatt nem fogod megtudni. :-D
Az ilyen "írok-olvasok" feldolgozások általában egyenletes sebességgel futnak. Ugyanez több szálon sem problematikus. A feladat akkor kezdődik, ha sok adattal, több szálon, bonyolult feldolgozást és indexelést is kell végezni. Ilyenkor a legfontosabb a feladathoz hangolt diszk alrendszer és az io buffer (per stream) mérete. Jelentős diszk terhelésnél érdemes úgy hangolni a rendszert, hogy a több szál kicsivel hosszabb ideig fusson, mint az egy. ;)
A retroplatform alatt mit értsek? (C64 nem ér!)
- A hozzászóláshoz be kell jelentkezni
> Az a "visszakacsacsőr" olyat tesz, mintha a program ezt csinálta volna: openat(STDIN_FILENO,"asciiart.txt",O_RDONLY)
Jogos, a redirectről megint megfelejtkeztem, de ettől függetlenül ez még mindig egy külső függőség (a shelltől), míg amit én írtam az UNIX-okon kívül is működik, ott is ahol ez az operátor nincs.
> Ki hitte volna? ;)
> De ilyen van:
> buffer[j+1]=0;
> puts(&buffer[i]);
> Ami kb. megfelel a "print substr(buffer,i+1,j-i+1)" awk kifejezésnek.
Volt a mondatnak egy második fele is: "meg amúgy is, azzal nem sokra megyünk, mert a terminál bal oldalán kell "lebegtetni" a kicopyzott oszlopokat".
> Hááát, ezt 10M alatt nem fogod megtudni. :-D
> Az ilyen "írok-olvasok" feldolgozások általában egyenletes sebességgel futnak. Ugyanez több szálon sem problematikus. A feladat akkor kezdődik, ha sok adattal, több szálon, bonyolult feldolgozást és indexelést is kell végezni. Ilyenkor a legfontosabb a feladathoz hangolt diszk alrendszer és az io buffer (per stream) mérete. Jelentős diszk terhelésnél érdemes úgy hangolni a rendszert, hogy a több szál kicsivel hosszabb ideig fusson, mint az egy. ;)
Tekintve, hogy itt az egész file valószínűleg belefér egy szektorba, így kb. maradhatunk annál, hogy elméletileg gyorsabb az egyszerre beolvasós, gyakorlatilag meg kb. nem az. Illetve elhanyagolhatóan pici mértékben.
> A retroplatform alatt mit értsek? (C64 nem ér!)
Dehogy, nem arra gondoltam, hanem annál azért erősebb gépre, pl. Amiga, vagy 286-os PC.
- A hozzászóláshoz be kell jelentkezni
de ettől függetlenül ez még mindig egy külső függőség (a shelltől), míg amit én írtam az UNIX-okon kívül is működik, ott is ahol ez az operátor nincs.
Mottó: Ha valaki a szilikonmellével dicsekszik, az olyan, mintha azzal dicsekedne, hogy hibátlanul elfingotta a Für Elise-t. :-)
- Ha ez megnyugtat, PIC18 platformon (8 bit) 64MHz-es órajellel is megcsinálom gyorsabban.
- Nem *nix-ra néhány abszolút üdítő kivétellel nem írtam programot már 25 éve. Windowsra is cygwin alatt írok. ;)
Tekintve, hogy itt az egész file valószínűleg belefér egy szektorba, így kb. maradhatunk annál, hogy elméletileg gyorsabb az egyszerre beolvasós, gyakorlatilag meg kb. nem az. Illetve elhanyagolhatóan pici mértékben.
Ha ez egy "versenyprogram" vagy benchmark akar lenni...annak elég picinke.
Viszont a teljes rendszer ismerete nélkül még egy ilyen kis feladatról sem lehet semmit biztosan állítani. Példa:
Az asciiart.txt 2626 bájt hosszú. Lehet olyan fs alattad, ahol - ennél a méretnél még - az inode tárolja a dir+file elemet. De olyan is, ahol nem, ráadásul 1k logikai blokkmérettel (itt legyen 2 szektor) dolgozik. Az első 1, a második 4 fs műveletet igényel. Közben meg ott csücsül a világ leglassabb entitása a printf(). A második esetben már jobban jársz, ha nem várod ki a teljes beolvasást! Ennek ellenére majdnem mindegy, mert a cache tényleg mindent visz.
Fokozzuk egy kicsit a feszültséget! Van egy rendszerem, amely >80M processzt futtat naponta. A CPU upgrade előtt olyan nagy volt a terhelés, hogy az 5 perc periódusidőt meg kellett növelni, hogy a kiosztott feladatok le tudjanak futni. Utána annyira fürge lett, hogy az 5 perc lejárta előtt végezhet a feladatokkal, aztán pihen egy kicsit. Ok, tehát gyorsabb. És mi van akkor, ha ez a pici program pont az 5 perces periódus elején fut, amikor csúcsterhelés van? (A rendszer diszket is használ! ;)) Na, ilyenkor meg akár a cache is kiürülhet két olvasás között.
A "semmi" futásidejű programokat nem lehet shellben for ciklussal megmérni, mert a process indítás, a script futásidő, stb. akármennyi is lehet. Majdnem azt írtam, hogy szór, de inkább laza a korreláció. ;)
Fogtam a parancsot és csináltan egy N sor hosszú scriptet, azaz lineáris végrehajtást. Ugyanazon a rendszeren a futtató shell függvényében (csh, bash, ksh) 1:2 arányban változhat a végrehajtási idő.
Másik rendszeren a két program futásideje 1:1,1 arányú volt, lineárisan meg 1,1:1.
Még a user és system idők változását és arányait magyarázni sem nagyon lehet, nemhogy megérteni!
Nem is csoda, mert a "real 0m0.002s" értéket a méréstechnikában úgy hívják: zaj. :-D
Ha sok zajt összeadsz, akkor is zajt kapsz.
Szóval ilyet beépített hardver timerrel lehet mérni, ami <1us felbontást tesz lehetővé. A POWER architektúra rendelkezik ilyennel, de mintha 15 éve a linux/Intel viszonylatban olvastam volna hasonlót, bár azt nem használtam. Ezzel az eszközzel olyat is meg tudtam mérni, hogy a memóriába töltött adatbázis három tábájának átlagos lekérdezési ideje 120, 160 és 220ns.
Ennél a kis C programnál a mérési pontokat a main() elején és végén kell elhelyezni. A kapott eredményhez már hozzá lehet adni a programindítás idejét. Hasonlóan mérhető hardverrel is, ha van egy bit gpio. A mérés elején bebillented a bitet, a végén vissza. A jelenséget 1500 forintos logikai analizátorral pontosan meg tudood mérni, esetleg szkóppal.
Retro gépeim: Atlas 604 (100MHz PowerPc 604, 1996), Alix 1D (AMD Geode LX800), bookpc (VIA C3 1GHz), p615 (POWER 4+ 1GHz, 2004)
Ebből most mértem egyet az Alix/FreeBSD 10.3 konfiguráción: 10m40s, tehát a mérésednél 5x lassabb.
Ja, és folyamatosan értem mi a feladat, de asszem már régen túlnőttünk rajta. ;)
- A hozzászóláshoz be kell jelentkezni
> Mottó: Ha valaki a szilikonmellével dicsekszik, az olyan, mintha azzal dicsekedne, hogy hibátlanul elfingotta a Für Elise-t. :-)
Másik mottó: Jobb későn, mint sárgadinnye.
> - Ha ez megnyugtat, PIC18 platformon (8 bit) 64MHz-es órajellel is megcsinálom gyorsabban.
Minél gyorsabban? Az asztali gépemen futó programnál? Vagy ha átírom C64-re, annál?
> - Nem *nix-ra néhány abszolút üdítő kivétellel nem írtam programot már 25 éve. Windowsra is cygwin alatt írok. ;)
És? Én meg próbálom úgy megírni mindenemet, hogy minél hordozhatóbb legyen.
> Ha ez egy "versenyprogram" vagy benchmark akar lenni...annak elég picinke.
Mármint hogy mi? A topicnyitót kérdezd, hogy mi ez, én csak egy lehetséges megoldást adtam neki.
> Viszont a teljes rendszer ismerete nélkül még egy ilyen kis feladatról sem lehet semmit biztosan állítani. ... Ennek ellenére majdnem mindegy, mert a cache tényleg mindent visz.
...
> A "semmi" futásidejű programokat nem lehet shellben for ciklussal megmérni, mert a process indítás, a script futásidő, stb. akármennyi is lehet. Majdnem azt írtam, hogy szór, de inkább laza a korreláció. ;)
Ezért kell baromi sokszor megmérni: egy bizonyos mérésszám felett kijön a különbség a két program között, ugyanis a többi, ami azonos, ha adott pillanatban külső behatások (clock, load, stb.) miatt más időt is ad, sok mérés után az összesítés konvergálni fog a valódihoz.
> Nem is csoda, mert a "real 0m0.002s" értéket a méréstechnikában úgy hívják: zaj. :-D
> Ha sok zajt összeadsz, akkor is zajt kapsz.
És a különbség lesz az, ami nem zaj.
> Szóval ilyet beépített hardver timerrel lehet mérni, ami <1us felbontást tesz lehetővé. A POWER architektúra rendelkezik ilyennel, de mintha 15 éve a linux/Intel viszonylatban olvastam volna hasonlót, bár azt nem használtam. Ezzel az eszközzel olyat is meg tudtam mérni, hogy a memóriába töltött adatbázis három tábájának átlagos lekérdezési ideje 120, 160 és 220ns.
> Ennél a kis C programnál a mérési pontokat a main() elején és végén kell elhelyezni. A kapott eredményhez már hozzá lehet adni a programindítás idejét. Hasonlóan mérhető hardverrel is, ha van egy bit gpio. A mérés elején bebillented a bitet, a végén vissza. A jelenséget 1500 forintos logikai analizátorral pontosan meg tudood mérni, esetleg szkóppal.
Egy mai rendszeren, SMP, multitaszking és változó órajel van, még a programon belül mérve is kaphatsz eltérő eredményeket. Nem a hardveres timer miatt, hanem mert nem biztos, hogy mindig ugyanannyi idő lesz, amíg lefut a program. Épp nem kapott időt, épp lejjebb volt az órajel, whatever.
> Ebből most mértem egyet az Alix/FreeBSD 10.3 konfiguráción: 10m40s, tehát a mérésednél 5x lassabb.
És melyiket mérted meg? Az egybe behúzóst, vagy a soronkénti olvasóst?
> Ja, és folyamatosan értem mi a feladat, de asszem már régen túlnőttünk rajta. ;)
Hát nem gyengén.
- A hozzászóláshoz be kell jelentkezni