Aşağıdaki testi yaptım ve sistemimde ortaya çıkan fark, ikinci senaryo için yaklaşık 100 kat daha uzun.
Dosyam, strace çıktısı bigfile
$ wc -l bigfile.log
1617000 bigfile.log
Senaryo
xtian@clafujiu:~/tmp$ cat p1.sh
tail -n 1000000 bigfile.log | grep '"success": true' | wc -l
tail -n 1000000 bigfile.log | grep '"success": false' | wc -l
xtian@clafujiu:~/tmp$ cat p2.sh
log=$(tail -n 1000000 bigfile.log)
echo "$log" | grep '"success": true' | wc -l
echo "$log" | grep '"success": true' | wc -l
Aslında grep için hiçbir eşleşmem yok bu yüzden son boruya hiçbir şey yazılmıyor wc -l
İşte zamanlamalar:
xtian@clafujiu:~/tmp$ time bash p1.sh
0
0
real 0m0.381s
user 0m0.248s
sys 0m0.280s
xtian@clafujiu:~/tmp$ time bash p2.sh
0
0
real 0m46.060s
user 0m43.903s
sys 0m2.176s
Bu yüzden iki senaryoyu yine strace komutuyla çalıştırdım.
strace -cfo p1.strace bash p1.sh
strace -cfo p2.strace bash p2.sh
İşte izlerinin sonuçları:
$ cat p1.strace
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
97.24 0.508109 63514 8 2 waitpid
1.61 0.008388 0 84569 read
1.08 0.005659 0 42448 write
0.06 0.000328 0 21233 _llseek
0.00 0.000024 0 204 146 stat64
0.00 0.000017 0 137 fstat64
0.00 0.000000 0 283 149 open
0.00 0.000000 0 180 8 close
...
0.00 0.000000 0 162 mmap2
0.00 0.000000 0 29 getuid32
0.00 0.000000 0 29 getgid32
0.00 0.000000 0 29 geteuid32
0.00 0.000000 0 29 getegid32
0.00 0.000000 0 3 1 fcntl64
0.00 0.000000 0 7 set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00 0.522525 149618 332 total
Ve p2.strace
$ cat p2.strace
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
75.27 1.336886 133689 10 3 waitpid
13.36 0.237266 11 21231 write
4.65 0.082527 1115 74 brk
2.48 0.044000 7333 6 execve
2.31 0.040998 5857 7 clone
1.91 0.033965 0 705681 read
0.02 0.000376 0 10619 _llseek
0.00 0.000000 0 248 132 open
...
0.00 0.000000 0 141 mmap2
0.00 0.000000 0 176 126 stat64
0.00 0.000000 0 118 fstat64
0.00 0.000000 0 25 getuid32
0.00 0.000000 0 25 getgid32
0.00 0.000000 0 25 geteuid32
0.00 0.000000 0 25 getegid32
0.00 0.000000 0 3 1 fcntl64
0.00 0.000000 0 6 set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00 1.776018 738827 293 total
analiz
Şaşırtıcı olmayan bir şekilde, her iki durumda da çoğu zaman bir işlemin tamamlanmasını beklemekle harcanır, ancak p2 p1'den 2.63 kat daha fazla bekler ve diğerlerinin de belirttiği gibi, p2.sh'de geç başlıyorsunuz.
Şimdi unutun waitpid
, %
sütunu yoksayın ve her iki izdeki saniye sütununa bakın.
En büyük zaman p1, zamanının çoğunu muhtemelen anlaşılır bir şekilde okumak için harcıyor, çünkü okunacak büyük bir dosya var, ancak p2, p1'den daha 28.82 kat daha uzun zaman harcıyor. - bash
bu kadar büyük bir dosyayı bir değişkene okumayı beklemiyor ve muhtemelen bir anda arabellek okuyor, satırlara bölerek ve sonra başka bir tane alıyor.
p2 okuma sayısı 705k vs p1 için 845'tir, her bir okuma bir çekirdekli alana ve tekrar dışarı bir içerik geçişi gerektirir. Okuma ve bağlam anahtarlarının sayısının yaklaşık 10 katı.
Yazma süresi p2 , yazma işleminde p1'den 41.93 kat daha fazla zaman harcıyor
yazma sayısı p1, p2, 42k ve 21k'dan daha çok yazma yapar, ancak çok daha hızlıdır.
Muhtemelen kuyruk yazma tamponlarının tersine echo
satırların sonucu grep
.
Dahası , p2 okunandan daha fazla zaman harcıyor, p1 tam tersi!
Diğer faktörbrk
Sistem çağrılarının sayısına bakın : p2, okumadan 2.42 kat daha uzun zaman harcıyor! P1 (kayıt bile olmaz). brk
Programın adres alanını genişletmesi gerektiğinden, başlangıçta yeterince tahsis edilmemiş olması gerekir, bunun nedeni muhtemelen büyük olasılıkla bu dosyayı değişkene okumak zorunda kalması ve bu kadar büyük olmasını beklememesidir. dosya çok büyük olur, işe yaramaz bile.
tail
Muhtemelen oldukça verimli bir dosya okuyucudur, çünkü yapmak için tasarlandığı şey budur, muhtemelen dosyayı sıkıştırır ve satır sonları için tarar, böylece çekirdeğin I / O'yu optimize etmesine izin verir. bash hem okuma hem de yazma için harcanan zaman kadar iyi değildir.
p2, 44ms ve 41ms arasında harcar clone
ve execv
bu p1 için ölçülebilir bir miktar değildir. Muhtemelen okuma ve değişken kuyruktan yaratma bash.
Sonunda, Toplamlar p1 ~ 150k sistem çağrılarını vs p2 740k (4.93 kat daha fazla) gerçekleştirir.
Bekleme süresini ortadan kaldırarak, p1 sistem çağrılarını yürütmek için 0.014416 saniye harcar, p2 0.439132 saniye (30 kez daha uzun).
Bu nedenle, p2, kullanıcı alanındaki zamanın çoğunu, sistem çağrılarının tamamlanmasını ve çekirdeğin hafızayı regüle etmesini beklemek dışında hiçbir şey yapmadan geçirdiği, p1'in daha fazla yazma yaptığı, ancak daha verimli olduğu ve önemli ölçüde daha az sistem yüküne neden olduğu ve dolayısıyla daha hızlı olduğu anlaşılmaktadır.
Sonuç
Bir bash betiği yazarken bellekten kodlama konusunda endişelenmeye asla çalışmam, bu verimli olmaya çalışmadığınızı söylemek anlamına gelmez.
tail
Ne yaptığını yapmak için tasarlanmıştır, muhtemelen memory maps
dosyayı okumak için verimlidir ve çekirdeğin I / O'yu optimize etmesine izin verir.
Sorununuzu optimize etmenin daha iyi bir yolu, ilk önce grep
'' başarı '': 'satırlar için olabilir ve sonra gerçekleri ve yanlışları saymak, grep
tekrardan kaçınmak için bir sayma seçeneği vardır wc -l
, hatta daha da iyisi, kuyruğu awk
kesmek ve gerçekleri saymak aynı anda falses. p2 sadece uzun sürmekle kalmaz, aynı zamanda bellek brks ile karıştırılırken sisteme yük ekler.