Neyin olabileceği veya olamayacağı hakkında spekülasyon yapmak yerine, sadece bakalım, olur mu? Ben (gerçi kullanışlı bir C # derleyicisi olmadığı için C ++ kullanmanız gerekecek C # örneğe bakın den VisualMelon ), ama aynı ilkeler bakılmaksızın geçerlidir eminim.
Röportajda karşılaştığınız iki alternatifi dahil edeceğiz. Ayrıca abs
bazı cevapların önerdiği şekilde kullanan bir sürüm de ekleyeceğiz .
#include <cstdlib>
bool IsSumInRangeWithVar(int a, int b)
{
int s = a + b;
if (s > 1000 || s < -1000) return false;
else return true;
}
bool IsSumInRangeWithoutVar(int a, int b)
{
if (a + b > 1000 || a + b < -1000) return false;
else return true;
}
bool IsSumInRangeSuperOptimized(int a, int b) {
return (abs(a + b) < 1000);
}
Şimdi, hiçbir optimizasyon olmadan derleyin: g++ -c -o test.o test.cpp
Şimdi bunun neyi ürettiğini tam olarak görebiliyoruz: objdump -d test.o
0000000000000000 <_Z19IsSumInRangeWithVarii>:
0: 55 push %rbp # begin a call frame
1: 48 89 e5 mov %rsp,%rbp
4: 89 7d ec mov %edi,-0x14(%rbp) # save first argument (a) on stack
7: 89 75 e8 mov %esi,-0x18(%rbp) # save b on stack
a: 8b 55 ec mov -0x14(%rbp),%edx # load a and b into edx
d: 8b 45 e8 mov -0x18(%rbp),%eax # load b into eax
10: 01 d0 add %edx,%eax # add a and b
12: 89 45 fc mov %eax,-0x4(%rbp) # save result as s on stack
15: 81 7d fc e8 03 00 00 cmpl $0x3e8,-0x4(%rbp) # compare s to 1000
1c: 7f 09 jg 27 # jump to 27 if it's greater
1e: 81 7d fc 18 fc ff ff cmpl $0xfffffc18,-0x4(%rbp) # compare s to -1000
25: 7d 07 jge 2e # jump to 2e if it's greater or equal
27: b8 00 00 00 00 mov $0x0,%eax # put 0 (false) in eax, which will be the return value
2c: eb 05 jmp 33 <_Z19IsSumInRangeWithVarii+0x33>
2e: b8 01 00 00 00 mov $0x1,%eax # put 1 (true) in eax
33: 5d pop %rbp
34: c3 retq
0000000000000035 <_Z22IsSumInRangeWithoutVarii>:
35: 55 push %rbp
36: 48 89 e5 mov %rsp,%rbp
39: 89 7d fc mov %edi,-0x4(%rbp)
3c: 89 75 f8 mov %esi,-0x8(%rbp)
3f: 8b 55 fc mov -0x4(%rbp),%edx
42: 8b 45 f8 mov -0x8(%rbp),%eax # same as before
45: 01 d0 add %edx,%eax
# note: unlike other implementation, result is not saved
47: 3d e8 03 00 00 cmp $0x3e8,%eax # compare to 1000
4c: 7f 0f jg 5d <_Z22IsSumInRangeWithoutVarii+0x28>
4e: 8b 55 fc mov -0x4(%rbp),%edx # since s wasn't saved, load a and b from the stack again
51: 8b 45 f8 mov -0x8(%rbp),%eax
54: 01 d0 add %edx,%eax
56: 3d 18 fc ff ff cmp $0xfffffc18,%eax # compare to -1000
5b: 7d 07 jge 64 <_Z22IsSumInRangeWithoutVarii+0x2f>
5d: b8 00 00 00 00 mov $0x0,%eax
62: eb 05 jmp 69 <_Z22IsSumInRangeWithoutVarii+0x34>
64: b8 01 00 00 00 mov $0x1,%eax
69: 5d pop %rbp
6a: c3 retq
000000000000006b <_Z26IsSumInRangeSuperOptimizedii>:
6b: 55 push %rbp
6c: 48 89 e5 mov %rsp,%rbp
6f: 89 7d fc mov %edi,-0x4(%rbp)
72: 89 75 f8 mov %esi,-0x8(%rbp)
75: 8b 55 fc mov -0x4(%rbp),%edx
78: 8b 45 f8 mov -0x8(%rbp),%eax
7b: 01 d0 add %edx,%eax
7d: 3d 18 fc ff ff cmp $0xfffffc18,%eax
82: 7c 16 jl 9a <_Z26IsSumInRangeSuperOptimizedii+0x2f>
84: 8b 55 fc mov -0x4(%rbp),%edx
87: 8b 45 f8 mov -0x8(%rbp),%eax
8a: 01 d0 add %edx,%eax
8c: 3d e8 03 00 00 cmp $0x3e8,%eax
91: 7f 07 jg 9a <_Z26IsSumInRangeSuperOptimizedii+0x2f>
93: b8 01 00 00 00 mov $0x1,%eax
98: eb 05 jmp 9f <_Z26IsSumInRangeSuperOptimizedii+0x34>
9a: b8 00 00 00 00 mov $0x0,%eax
9f: 5d pop %rbp
a0: c3 retq
Biz yığını adreslerden görebilirsiniz (örneğin, -0x4
içinde mov %edi,-0x4(%rbp)
karşı -0x14
in mov %edi,-0x14(%rbp)
) IsSumInRangeWithVar()
yığın 16 ekstra bayt kullanır.
Çünkü IsSumInRangeWithoutVar()
ayırdığı yığında boşluk ara değer kaydetmek için s
bu 2 talimatlar uzun olan bu uygulama ile sonuçlanan, yeniden hesaplamak gerekir.
Komik, ilk önce -1000 ve 1000 saniye ile karşılaştırmak dışında IsSumInRangeSuperOptimized()
çok benziyor IsSumInRangeWithoutVar()
.
Şimdi sadece en temel optimizasyonlarla derlemek bakalım: g++ -O1 -c -o test.o test.cpp
. Sonuç:
0000000000000000 <_Z19IsSumInRangeWithVarii>:
0: 8d 84 37 e8 03 00 00 lea 0x3e8(%rdi,%rsi,1),%eax
7: 3d d0 07 00 00 cmp $0x7d0,%eax
c: 0f 96 c0 setbe %al
f: c3 retq
0000000000000010 <_Z22IsSumInRangeWithoutVarii>:
10: 8d 84 37 e8 03 00 00 lea 0x3e8(%rdi,%rsi,1),%eax
17: 3d d0 07 00 00 cmp $0x7d0,%eax
1c: 0f 96 c0 setbe %al
1f: c3 retq
0000000000000020 <_Z26IsSumInRangeSuperOptimizedii>:
20: 8d 84 37 e8 03 00 00 lea 0x3e8(%rdi,%rsi,1),%eax
27: 3d d0 07 00 00 cmp $0x7d0,%eax
2c: 0f 96 c0 setbe %al
2f: c3 retq
Şuna bakar mısın: Her değişken aynıdır . Derleyici oldukça akıllıca bir şey yapabilir: abs(a + b) <= 1000
eşdeğerdir a + b + 1000 <= 2000
dikkate setbe
yüzden negatif bir sayı çok büyük pozitif sayı olur imzalanmamış karşılaştırma gelmez. lea
Talimat aslında bir talimat bütün bu eklemeler yapmak ve tüm koşullu dallarını ortadan kaldırabilir.
Sorunuzu yanıtlamak için, neredeyse her zaman için en uygun hale getirmek için gereken şey hafıza veya hız değil, okunabilirliktir . Kod okumak, yazmaktan daha zordur ve "optimize etmek" için ayarlanan kod okumak, açık olması için yazılmış kodlardan çok daha zordur. Çoğu zaman, bu "optimizasyonların" ihmal edilebilir olması veya bu durumda performans üzerinde tam olarak sıfır bir etki yaratması gibi.
Takip eden soru, bu kod derlenmiş yerine yorumlanmış bir dilde olduğunda ne değişir? Öyleyse, optimizasyon önemli mi yoksa aynı sonucu veriyor mu?
Ölçelim! Örnekleri Python'a kopyaladım:
def IsSumInRangeWithVar(a, b):
s = a + b
if s > 1000 or s < -1000:
return False
else:
return True
def IsSumInRangeWithoutVar(a, b):
if a + b > 1000 or a + b < -1000:
return False
else:
return True
def IsSumInRangeSuperOptimized(a, b):
return abs(a + b) <= 1000
from dis import dis
print('IsSumInRangeWithVar')
dis(IsSumInRangeWithVar)
print('\nIsSumInRangeWithoutVar')
dis(IsSumInRangeWithoutVar)
print('\nIsSumInRangeSuperOptimized')
dis(IsSumInRangeSuperOptimized)
print('\nBenchmarking')
import timeit
print('IsSumInRangeWithVar: %fs' % (min(timeit.repeat(lambda: IsSumInRangeWithVar(42, 42), repeat=50, number=100000)),))
print('IsSumInRangeWithoutVar: %fs' % (min(timeit.repeat(lambda: IsSumInRangeWithoutVar(42, 42), repeat=50, number=100000)),))
print('IsSumInRangeSuperOptimized: %fs' % (min(timeit.repeat(lambda: IsSumInRangeSuperOptimized(42, 42), repeat=50, number=100000)),))
Python 3.5.2 ile çalıştırın, bu çıktıyı verir:
IsSumInRangeWithVar
2 0 LOAD_FAST 0 (a)
3 LOAD_FAST 1 (b)
6 BINARY_ADD
7 STORE_FAST 2 (s)
3 10 LOAD_FAST 2 (s)
13 LOAD_CONST 1 (1000)
16 COMPARE_OP 4 (>)
19 POP_JUMP_IF_TRUE 34
22 LOAD_FAST 2 (s)
25 LOAD_CONST 4 (-1000)
28 COMPARE_OP 0 (<)
31 POP_JUMP_IF_FALSE 38
4 >> 34 LOAD_CONST 2 (False)
37 RETURN_VALUE
6 >> 38 LOAD_CONST 3 (True)
41 RETURN_VALUE
42 LOAD_CONST 0 (None)
45 RETURN_VALUE
IsSumInRangeWithoutVar
9 0 LOAD_FAST 0 (a)
3 LOAD_FAST 1 (b)
6 BINARY_ADD
7 LOAD_CONST 1 (1000)
10 COMPARE_OP 4 (>)
13 POP_JUMP_IF_TRUE 32
16 LOAD_FAST 0 (a)
19 LOAD_FAST 1 (b)
22 BINARY_ADD
23 LOAD_CONST 4 (-1000)
26 COMPARE_OP 0 (<)
29 POP_JUMP_IF_FALSE 36
10 >> 32 LOAD_CONST 2 (False)
35 RETURN_VALUE
12 >> 36 LOAD_CONST 3 (True)
39 RETURN_VALUE
40 LOAD_CONST 0 (None)
43 RETURN_VALUE
IsSumInRangeSuperOptimized
15 0 LOAD_GLOBAL 0 (abs)
3 LOAD_FAST 0 (a)
6 LOAD_FAST 1 (b)
9 BINARY_ADD
10 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
13 LOAD_CONST 1 (1000)
16 COMPARE_OP 1 (<=)
19 RETURN_VALUE
Benchmarking
IsSumInRangeWithVar: 0.019361s
IsSumInRangeWithoutVar: 0.020917s
IsSumInRangeSuperOptimized: 0.020171s
Python'daki demontaj çok ilginç değil çünkü bytecode "compiler" optimizasyonu için pek bir şey yapmıyor.
Üç fonksiyonun performansı neredeyse aynı. IsSumInRangeWithVar()
Marjinal hız artışı nedeniyle birlikte gitmeye cazip gelebiliriz . Her ne kadar farklı parametreler denediğimi eklesem de timeit
, bazen IsSumInRangeSuperOptimized()
en hızlı şekilde çıktığım için, herhangi bir uygulamanın kendine özgü avantajlarından ziyade farklılıktan sorumlu dış faktörler olabileceğinden şüpheleniyorum.
Bu gerçekten performans açısından kritik bir kodsa, yorumlanmış bir dil sadece çok kötü bir seçimdir. Aynı programı pypy ile çalıştırarak alıyorum:
IsSumInRangeWithVar: 0.000180s
IsSumInRangeWithoutVar: 0.001175s
IsSumInRangeSuperOptimized: 0.001306s
Sadece tercüman ek yükünü ortadan kaldırmak için JIT derlemesi kullanan pypy kullanmak, 1 veya 2 büyüklük sırasına göre performans artışı sağladı. IsSumInRangeWithVar()
Diğerlerinden daha hızlı bir büyüklük sırası olduğunu görmek beni oldukça şaşırttı . Ben de ölçütlerin sırasını değiştirdim ve tekrar koştum:
IsSumInRangeSuperOptimized: 0.000191s
IsSumInRangeWithoutVar: 0.001174s
IsSumInRangeWithVar: 0.001265s
Öyle görünüyor ki, bunu hızlı yapan uygulama ile ilgili hiçbir şey değil, kıyaslama yaptığım sırayla!
Buna daha derinden dalmayı çok isterim, çünkü dürüst olmak gerekirse, bunun neden olduğunu bilmiyorum. Ancak konunun yapıldığına inanıyorum: ara değerin değişken olarak bildirilip bildirilmemesi gibi mikro optimizasyonlar nadiren alakalı değildir. Tercüme edilmiş bir dil veya yüksek derecede optimize edilmiş bir derleyici ile, ilk amaç hala net kod yazmaktır.
Ayrıca optimizasyon gerekebilir Eğer kriter . En iyi optimizasyonların küçük ayrıntılardan değil, daha büyük algoritmik görüntüden geldiğini unutmayın: pypy aynı fonksiyonun tekrar tekrar değerlendirilmesi için cpython'dan daha hızlı bir büyüklük sırası olacak çünkü cpython'dan daha hızlı algoritmalar kullanır (JIT derleyici - yorumlama) programı. Ayrıca dikkate alınması gereken kodlanmış bir algoritma var: B-ağacı üzerinden yapılan bir arama, bağlantılı bir listeden daha hızlı olacaktır.
İş için doğru araçları ve algoritmaları kullandığınızdan emin olduktan sonra , sistemin ayrıntılarına derinlemesine dalmaya hazır olun . Sonuçlar, deneyimli geliştiriciler için bile çok şaşırtıcı olabilir ve bu yüzden değişiklikleri ölçmek için bir kritere sahip olmalısınız.