求次冪是個老生常談的話題了,在任何一門語言裏都是最基礎的部分。在這裏主要以JS爲例。
首先,最容易想到的是暴力法,也就是連續乘上n個x;但是這個的性能顯然很差,我們一般不會考慮這麼寫。而且,爲什麼不用內置函數呢?調內置函數沒什麼難度,隨便舉個例子吧:
var myPow = function(x, n) {
return Math.pow(x, n);
};
確實,這個沒什麼難度,並且內置函數一般得到了充分的優化,性能也是很有保障的。但是,內置函數一定就快嗎?還能進一步優化嗎?還有沒有別的方法?這些問題是值得思考的。
這是利用數學性質來進行計算的,基於一個很簡單的思路,:
var myPow = function (x, n) {
if (n == 0) return 1;
let tmp = Math.exp(n * Math.log(Math.abs(x)));
if (x < 0 && n % 2 == 1) tmp *= -1;
return tmp;
};
而這個用的是快速冪乘法,需要注意的是,要用Math.trunc
而不是Math.floor
,因爲Math.floor
是向下取整,在面對負數的時候也會向下取整,導致無限遞歸;而Math.trunc
則是截斷,相當於向0取整:
function pow(x, n) {
if (n == 0) {
return 1;
}
const tmp = pow(x, Math.trunc(n / 2));
return n % 2 == 0 ? tmp * tmp : tmp * tmp * x;
}
var myPow = function (x, n) {
return n > 0 ? pow(x, n) : 1 / pow(x, n);
};
我們已經看到了其他的寫法,按理來說,到這裏已經可以結束了,但是我發現,在執行次數很少的情況下,手寫的代碼反而可能比原生的快,這就很奇怪了。所以我打算看看V8的源碼,能不能看出點東西來:
CodeStubAssembler::Node* MathBuiltinsAssembler::MathPow(Node* context,
Node* base,
Node* exponent) {
Node* base_value = TruncateTaggedToFloat64(context, base);
Node* exponent_value = TruncateTaggedToFloat64(context, exponent);
Node* value = Float64Pow(base_value, exponent_value);
return ChangeFloat64ToTagged(value);
}
Node* Float64Pow(Node* a, Node* b) {
return AddNode(machine()->Float64Pow(), a, b);
}
Node* Float64Exp(Node* a) { return AddNode(machine()->Float64Exp(), a); }
Node* Float64Log(Node* a) { return AddNode(machine()->Float64Log(), a); }
得,話都說到這份上了,用的是硬件自帶的運算,咱也沒轍。所以只能進一步深入,看看能不能從彙編層面看出點東西來,用d8編譯一下,看看輸出的結果。
內容很長,放到最後面了。簡單說一下結論:在執行次數很少的情況下(我這裏測試大概是10000次以下),原生代碼可能不會成爲所謂的“熱點”,也就是不會被V8編譯成彙編指令;而手寫的代碼會被編譯成彙編指令。所以,在這個情況下,是有可能存在手寫比原生快的情況的;而隨着執行次數的增加,原生代碼也會變成彙編指令,並且條數更少,自然也就更快了。
附錄
原生
Instructions (size = 460)
00000061000C2E60 0 488d1df9ffffff REX.W leaq rbx,[rip+0xfffffff9]
00000061000C2E67 7 483bd9 REX.W cmpq rbx,rcx
00000061000C2E6A a 7418 jz 00000061000C2E84 <+0x24>
00000061000C2E6C c 48ba6800000000000000 REX.W movq rdx,0000000000000068
00000061000C2E76 16 49bae0c0d5cdfb7f0000 REX.W movq r10,00007FFBCDD5C0E0 (Abort) ;; off heap target
00000061000C2E80 20 41ffd2 call r10
00000061000C2E83 23 cc int3l
00000061000C2E84 24 8b59d0 movl rbx,[rcx-0x30]
00000061000C2E87 27 4903dd REX.W addq rbx,r13
00000061000C2E8A 2a f6430701 testb [rbx+0x7],0x1
00000061000C2E8E 2e 740d jz 00000061000C2E9D <+0x3d>
00000061000C2E90 30 49baa0f9cacdfb7f0000 REX.W movq r10,00007FFBCDCAF9A0 (CompileLazyDeoptimizedCode) ;; off heap target
00000061000C2E9A 3a 41ffe2 jmp r10
00000061000C2E9D 3d 55 push rbp
00000061000C2E9E 3e 4889e5 REX.W movq rbp,rsp
00000061000C2EA1 41 56 push rsi
00000061000C2EA2 42 57 push rdi
00000061000C2EA3 43 48ba4200000000000000 REX.W movq rdx,0000000000000042
00000061000C2EAD 4d 4c8b15c4ffffff REX.W movq r10,[rip+0xffffffc4]
00000061000C2EB4 54 41ffd2 call r10
00000061000C2EB7 57 cc int3l
00000061000C2EB8 58 4883ec10 REX.W subq rsp,0x10
00000061000C2EBC 5c 488975b0 REX.W movq [rbp-0x50],rsi
00000061000C2EC0 60 488b55d0 REX.W movq rdx,[rbp-0x30]
00000061000C2EC4 64 f6c201 testb rdx,0x1
00000061000C2EC7 67 0f8516010000 jnz 00000061000C2FE3 <+0x183>
00000061000C2ECD 6d 81fa002d3101 cmpl rdx,0x1312d00
00000061000C2ED3 73 0f8c0b000000 jl 00000061000C2EE4 <+0x84>
00000061000C2ED9 79 488b45d8 REX.W movq rax,[rbp-0x28]
00000061000C2EDD 7d 488be5 REX.W movq rsp,rbp
00000061000C2EE0 80 5d pop rbp
00000061000C2EE1 81 c20800 ret 0x8
00000061000C2EE4 84 48b9b93a240861000000 REX.W movq rcx,0000006108243AB9 ;; object: 0x006108243ab9 <Object map = 0000006108280A09>
00000061000C2EEE 8e 8b4903 movl rcx,[rcx+0x3]
00000061000C2EF1 91 4903cd REX.W addq rcx,r13
00000061000C2EF4 94 8b495b movl rcx,[rcx+0x5b]
00000061000C2EF7 97 49ba0000000001000000 REX.W movq r10,0000000100000000
00000061000C2F01 a1 4c3bd1 REX.W cmpq r10,rcx
00000061000C2F04 a4 7715 ja 00000061000C2F1B <+0xbb>
00000061000C2F06 a6 48ba0200000000000000 REX.W movq rdx,0000000000000002
00000061000C2F10 b0 4c8b1561ffffff REX.W movq r10,[rip+0xffffff61]
00000061000C2F17 b7 41ffd2 call r10
00000061000C2F1A ba cc int3l
00000061000C2F1B bb bff13f2408 movl rdi,0000000008243FF1 ;; (compressed) object: 0x006108243ff1 <JSFunction pow (sfi = 00000061081C7729)>
00000061000C2F20 c0 3bf9 cmpl rdi,rcx
00000061000C2F22 c2 0f85c7000000 jnz 00000061000C2FEF <+0x18f>
00000061000C2F28 c8 488bca REX.W movq rcx,rdx
00000061000C2F2B cb d1f9 sarl rcx, 1
00000061000C2F2D cd 83c101 addl rcx,0x1
00000061000C2F30 d0 0f80c5000000 jo 00000061000C2FFB <+0x19b>
00000061000C2F36 d6 493b6560 REX.W cmpq rsp,[r13+0x60] (external value (StackGuard::address_of_jslimit()))
00000061000C2F3A da 0f8713000000 ja 00000061000C2F53 <+0xf3>
00000061000C2F40 e0 e940000000 jmp 00000061000C2F85 <+0x125>
00000061000C2F45 e5 660f1f840000000000 nop
00000061000C2F4E ee 6690 nop
00000061000C2F50 f0 488bca REX.W movq rcx,rdx
00000061000C2F53 f3 81f980969800 cmpl rcx,0x989680
00000061000C2F59 f9 0f8d17000000 jge 00000061000C2F76 <+0x116>
00000061000C2F5F ff 488bd1 REX.W movq rdx,rcx
00000061000C2F62 102 83c201 addl rdx,0x1
00000061000C2F65 105 0f809c000000 jo 00000061000C3007 <+0x1a7>
00000061000C2F6B 10b 493b6560 REX.W cmpq rsp,[r13+0x60] (external value (StackGuard::address_of_jslimit()))
00000061000C2F6F 10f 77df ja 00000061000C2F50 <+0xf0>
00000061000C2F71 111 e942000000 jmp 00000061000C2FB8 <+0x158>
00000061000C2F76 116 48b80008000000000000 REX.W movq rax,0000000000000800
00000061000C2F80 120 e958ffffff jmp 00000061000C2EDD <+0x7d>
00000061000C2F85 125 48894da8 REX.W movq [rbp-0x58],rcx
00000061000C2F89 129 33c0 xorl rax,rax
00000061000C2F8B 12b 48bea10d240861000000 REX.W movq rsi,0000006108240DA1 ;; object: 0x006108240da1 <NativeContext[247]>
00000061000C2F95 135 48bbd0ec24cffb7f0000 REX.W movq rbx,00007FFBCF24ECD0 ;; external reference (Runtime::StackGuard)
00000061000C2F9F 13f 488bd0 REX.W movq rdx,rax
00000061000C2FA2 142 488bfe REX.W movq rdi,rsi
00000061000C2FA5 145 49ba00a8edcdfb7f0000 REX.W movq r10,00007FFBCDEDA800 (CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit) ;; off heap target
00000061000C2FAF 14f 41ffd2 call r10
00000061000C2FB2 152 488b4da8 REX.W movq rcx,[rbp-0x58]
00000061000C2FB6 156 eb9b jmp 00000061000C2F53 <+0xf3>
00000061000C2FB8 158 488955a8 REX.W movq [rbp-0x58],rdx
00000061000C2FBC 15c 488b1dd4ffffff REX.W movq rbx,[rip+0xffffffd4]
00000061000C2FC3 163 33c0 xorl rax,rax
00000061000C2FC5 165 48bea10d240861000000 REX.W movq rsi,0000006108240DA1 ;; object: 0x006108240da1 <NativeContext[247]>
00000061000C2FCF 16f 4c8b15d1ffffff REX.W movq r10,[rip+0xffffffd1]
00000061000C2FD6 176 41ffd2 call r10
00000061000C2FD9 179 488b55a8 REX.W movq rdx,[rbp-0x58]
00000061000C2FDD 17d e96effffff jmp 00000061000C2F50 <+0xf0>
00000061000C2FE2 182 90 nop
00000061000C2FE3 183 49c7c500000000 REX.W movq r13,0x0
00000061000C2FEA 18a e851f00300 call 0000006100102040 ;; eager deoptimization bailout
00000061000C2FEF 18f 49c7c501000000 REX.W movq r13,0x1
00000061000C2FF6 196 e845f00300 call 0000006100102040 ;; eager deoptimization bailout
00000061000C2FFB 19b 49c7c502000000 REX.W movq r13,0x2
00000061000C3002 1a2 e839f00300 call 0000006100102040 ;; eager deoptimization bailout
00000061000C3007 1a7 49c7c503000000 REX.W movq r13,0x3
00000061000C300E 1ae e82df00300 call 0000006100102040 ;; eager deoptimization bailout
00000061000C3013 1b3 49c7c504000000 REX.W movq r13,0x4
00000061000C301A 1ba e821f00700 call 0000006100142040 ;; lazy deoptimization bailout
00000061000C301F 1bf 49c7c505000000 REX.W movq r13,0x5
00000061000C3026 1c6 e815f00700 call 0000006100142040 ;; lazy deoptimization bailout
00000061000C302B 1cb 90 nop
手寫
Instructions (size = 580)
0000028E000C2F20 0 488d1df9ffffff REX.W leaq rbx,[rip+0xfffffff9]
0000028E000C2F27 7 483bd9 REX.W cmpq rbx,rcx
0000028E000C2F2A a 7418 jz 0000028E000C2F44 <+0x24>
0000028E000C2F2C c 48ba6800000000000000 REX.W movq rdx,0000000000000068
0000028E000C2F36 16 49bae0c0d5cdfb7f0000 REX.W movq r10,00007FFBCDD5C0E0 (Abort) ;; off heap target
0000028E000C2F40 20 41ffd2 call r10
0000028E000C2F43 23 cc int3l
0000028E000C2F44 24 8b59d0 movl rbx,[rcx-0x30]
0000028E000C2F47 27 4903dd REX.W addq rbx,r13
0000028E000C2F4A 2a f6430701 testb [rbx+0x7],0x1
0000028E000C2F4E 2e 740d jz 0000028E000C2F5D <+0x3d>
0000028E000C2F50 30 49baa0f9cacdfb7f0000 REX.W movq r10,00007FFBCDCAF9A0 (CompileLazyDeoptimizedCode) ;; off heap target
0000028E000C2F5A 3a 41ffe2 jmp r10
0000028E000C2F5D 3d 55 push rbp
0000028E000C2F5E 3e 4889e5 REX.W movq rbp,rsp
0000028E000C2F61 41 56 push rsi
0000028E000C2F62 42 57 push rdi
0000028E000C2F63 43 48ba4200000000000000 REX.W movq rdx,0000000000000042
0000028E000C2F6D 4d 4c8b15c4ffffff REX.W movq r10,[rip+0xffffffc4]
0000028E000C2F74 54 41ffd2 call r10
0000028E000C2F77 57 cc int3l
0000028E000C2F78 58 4883ec10 REX.W subq rsp,0x10
0000028E000C2F7C 5c 488975b0 REX.W movq [rbp-0x50],rsi
0000028E000C2F80 60 488b55d0 REX.W movq rdx,[rbp-0x30]
0000028E000C2F84 64 f6c201 testb rdx,0x1
0000028E000C2F87 67 0f8576010000 jnz 0000028E000C3103 <+0x1e3>
0000028E000C2F8D 6d 81fa002d3101 cmpl rdx,0x1312d00
0000028E000C2F93 73 0f8c0b000000 jl 0000028E000C2FA4 <+0x84>
0000028E000C2F99 79 488b45d8 REX.W movq rax,[rbp-0x28]
0000028E000C2F9D 7d 488be5 REX.W movq rsp,rbp
0000028E000C2FA0 80 5d pop rbp
0000028E000C2FA1 81 c20800 ret 0x8
0000028E000C2FA4 84 48b9b93a24088e020000 REX.W movq rcx,0000028E08243AB9 ;; object: 0x028e08243ab9 <Object map = 0000028E08280A09>
0000028E000C2FAE 8e 8b7903 movl rdi,[rcx+0x3]
0000028E000C2FB1 91 4903fd REX.W addq rdi,r13
0000028E000C2FB4 94 448b472f movl r8,[rdi+0x2f]
0000028E000C2FB8 98 49ba0000000001000000 REX.W movq r10,0000000100000000
0000028E000C2FC2 a2 4d3bd0 REX.W cmpq r10,r8
0000028E000C2FC5 a5 7715 ja 0000028E000C2FDC <+0xbc>
0000028E000C2FC7 a7 48ba0200000000000000 REX.W movq rdx,0000000000000002
0000028E000C2FD1 b1 4c8b1560ffffff REX.W movq r10,[rip+0xffffff60]
0000028E000C2FD8 b8 41ffd2 call r10
0000028E000C2FDB bb cc int3l
0000028E000C2FDC bc 8b7f43 movl rdi,[rdi+0x43]
0000028E000C2FDF bf 4c8b15d4ffffff REX.W movq r10,[rip+0xffffffd4]
0000028E000C2FE6 c6 4c3bd7 REX.W cmpq r10,rdi
0000028E000C2FE9 c9 7712 ja 0000028E000C2FFD <+0xdd>
0000028E000C2FEB cb 488b15d7ffffff REX.W movq rdx,[rip+0xffffffd7]
0000028E000C2FF2 d2 4c8b153fffffff REX.W movq r10,[rip+0xffffff3f]
0000028E000C2FF9 d9 41ffd2 call r10
0000028E000C2FFC dc cc int3l
0000028E000C2FFD dd 8b490b movl rcx,[rcx+0xb]
0000028E000C3000 e0 4c8b15b3ffffff REX.W movq r10,[rip+0xffffffb3]
0000028E000C3007 e7 4c3bd1 REX.W cmpq r10,rcx
0000028E000C300A ea 7712 ja 0000028E000C301E <+0xfe>
0000028E000C300C ec 488b15b6ffffff REX.W movq rdx,[rip+0xffffffb6]
0000028E000C3013 f3 4c8b151effffff REX.W movq r10,[rip+0xffffff1e]
0000028E000C301A fa 41ffd2 call r10
0000028E000C301D fd cc int3l
0000028E000C301E fe 41b909412408 movl r9,0000000008244109 ;; (compressed) object: 0x028e08244109 <JSFunction abs (sfi = 0000028E081C7919)>
0000028E000C3024 104 443bc9 cmpl r9,rcx
0000028E000C3027 107 0f85e2000000 jnz 0000028E000C310F <+0x1ef>
0000028E000C302D 10d b9493f2408 movl rcx,0000000008243F49 ;; (compressed) object: 0x028e08243f49 <JSFunction log (sfi = 0000028E081C7639)>
0000028E000C3032 112 3bcf cmpl rcx,rdi
0000028E000C3034 114 0f85e1000000 jnz 0000028E000C311B <+0x1fb>
0000028E000C303A 11a b9bd3e2408 movl rcx,0000000008243EBD ;; (compressed) object: 0x028e08243ebd <JSFunction exp (sfi = 0000028E081C7571)>
0000028E000C303F 11f 413bc8 cmpl rcx,r8
0000028E000C3042 122 0f85df000000 jnz 0000028E000C3127 <+0x207>
0000028E000C3048 128 488bca REX.W movq rcx,rdx
0000028E000C304B 12b d1f9 sarl rcx, 1
0000028E000C304D 12d 83c101 addl rcx,0x1
0000028E000C3050 130 0f80dd000000 jo 0000028E000C3133 <+0x213>
0000028E000C3056 136 493b6560 REX.W cmpq rsp,[r13+0x60] (external value (StackGuard::address_of_jslimit()))
0000028E000C305A 13a 0f8713000000 ja 0000028E000C3073 <+0x153>
0000028E000C3060 140 e940000000 jmp 0000028E000C30A5 <+0x185>
0000028E000C3065 145 660f1f840000000000 nop
0000028E000C306E 14e 6690 nop
0000028E000C3070 150 488bca REX.W movq rcx,rdx
0000028E000C3073 153 81f980969800 cmpl rcx,0x989680
0000028E000C3079 159 0f8d17000000 jge 0000028E000C3096 <+0x176>
0000028E000C307F 15f 488bd1 REX.W movq rdx,rcx
0000028E000C3082 162 83c201 addl rdx,0x1
0000028E000C3085 165 0f80b4000000 jo 0000028E000C313F <+0x21f>
0000028E000C308B 16b 493b6560 REX.W cmpq rsp,[r13+0x60] (external value (StackGuard::address_of_jslimit()))
0000028E000C308F 16f 77df ja 0000028E000C3070 <+0x150>
0000028E000C3091 171 e942000000 jmp 0000028E000C30D8 <+0x1b8>
0000028E000C3096 176 48b80008000000000000 REX.W movq rax,0000000000000800
0000028E000C30A0 180 e9f8feffff jmp 0000028E000C2F9D <+0x7d>
0000028E000C30A5 185 48894da8 REX.W movq [rbp-0x58],rcx
0000028E000C30A9 189 33c0 xorl rax,rax
0000028E000C30AB 18b 48bea10d24088e020000 REX.W movq rsi,0000028E08240DA1 ;; object: 0x028e08240da1 <NativeContext[247]>
0000028E000C30B5 195 48bbd0ec24cffb7f0000 REX.W movq rbx,00007FFBCF24ECD0 ;; external reference (Runtime::StackGuard)
0000028E000C30BF 19f 488bd0 REX.W movq rdx,rax
0000028E000C30C2 1a2 488bfe REX.W movq rdi,rsi
0000028E000C30C5 1a5 49ba00a8edcdfb7f0000 REX.W movq r10,00007FFBCDEDA800 (CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit) ;; off heap target
0000028E000C30CF 1af 41ffd2 call r10
0000028E000C30D2 1b2 488b4da8 REX.W movq rcx,[rbp-0x58]
0000028E000C30D6 1b6 eb9b jmp 0000028E000C3073 <+0x153>
0000028E000C30D8 1b8 488955a8 REX.W movq [rbp-0x58],rdx
0000028E000C30DC 1bc 488b1dd4ffffff REX.W movq rbx,[rip+0xffffffd4]
0000028E000C30E3 1c3 33c0 xorl rax,rax
0000028E000C30E5 1c5 48bea10d24088e020000 REX.W movq rsi,0000028E08240DA1 ;; object: 0x028e08240da1 <NativeContext[247]>
0000028E000C30EF 1cf 4c8b15d1ffffff REX.W movq r10,[rip+0xffffffd1]
0000028E000C30F6 1d6 41ffd2 call r10
0000028E000C30F9 1d9 488b55a8 REX.W movq rdx,[rbp-0x58]
0000028E000C30FD 1dd e96effffff jmp 0000028E000C3070 <+0x150>
0000028E000C3102 1e2 90 nop
0000028E000C3103 1e3 49c7c500000000 REX.W movq r13,0x0
0000028E000C310A 1ea e831ef0300 call 0000028E00102040 ;; eager deoptimization bailout
0000028E000C310F 1ef 49c7c501000000 REX.W movq r13,0x1
0000028E000C3116 1f6 e825ef0300 call 0000028E00102040 ;; eager deoptimization bailout
0000028E000C311B 1fb 49c7c502000000 REX.W movq r13,0x2
0000028E000C3122 202 e819ef0300 call 0000028E00102040 ;; eager deoptimization bailout
0000028E000C3127 207 49c7c503000000 REX.W movq r13,0x3
0000028E000C312E 20e e80def0300 call 0000028E00102040 ;; eager deoptimization bailout
0000028E000C3133 213 49c7c504000000 REX.W movq r13,0x4
0000028E000C313A 21a e801ef0300 call 0000028E00102040 ;; eager deoptimization bailout
0000028E000C313F 21f 49c7c505000000 REX.W movq r13,0x5
0000028E000C3146 226 e8f5ee0300 call 0000028E00102040 ;; eager deoptimization bailout
0000028E000C314B 22b 49c7c506000000 REX.W movq r13,0x6
0000028E000C3152 232 e8e9ee0700 call 0000028E00142040 ;; lazy deoptimization bailout
0000028E000C3157 237 49c7c507000000 REX.W movq r13,0x7
0000028E000C315E 23e e8ddee0700 call 0000028E00142040 ;; lazy deoptimization bailout
0000028E000C3163 243 90 nop