Scarpet的奇葩代碼優(yōu)化方法

作者:祿存,圖片為堆排序算法,來自網絡,侵刪
前往TIS論壇,享受更好的Markdown渲染體驗 https://forum.tis.world/topic/136/scarpet的奇葩代碼優(yōu)化方法
看過本文并不代表你就不用去把數據結構與算法啃下來了!
字多不看
字多不看,你咋不上天呢
少用return,+=是個好東西,以上
引言
上一篇文章發(fā)布后,我主要做了兩件事:一是寫了個Heap數據結構;第二個就是明確了一下Carpet的腳本語言應該叫Scarpet。再有就是拿著一個測試時候發(fā)現的bug去小小的錘了一下作者,順便學到了點優(yōu)化。
寫出Heap算法以后,發(fā)生了一件奇妙的事情:同樣是一萬次的添加元素并排序,時間復雜度是O(log N)的堆竟然比O(N)慢了不少。后來在經由discord的Kayleigh大佬向gnembon交bug的時候,學到了優(yōu)化方法:把return和if去掉
heap_get(heap_name, index) -> return(element(var(heap_name), index));
heap_get_left_index(index) -> return(index * 2 + 1);
heap_get_right_index(index) -> return(index * 2 + 2);
heap_get_parent_index(index) -> return(if (index == 0, null, floor((index - 1) / 2)));
變成了這樣的代碼
heap_get(heap_name, index) -> element(var(heap_name), index);
heap_get_left_index(index) -> index * 2 + 1;
heap_get_right_index(index) -> index * 2 + 2;
heap_get_parent_index(index) -> if (index == 0, -1, floor((index - 1) / 2));
關于var()的和特殊情況傳引用的方法之后再說。這里只是把return語句去除,讓函數自動返回最后一條語句的輸出——效率真的提高了很多!
猜測
既然“萬物皆函數”,那么有理由相信return()語句的也是函數,而且復雜度不一般。這種反直覺的特性應該還有許多,例如:
自加語句:a += b?和 a = a + b
衛(wèi)語句,例如?https://blog.csdn.net/jw903/article/details/45506777 這個例子
if內/外賦值的區(qū)別:a = if (x > y, 'yes', 'no') 和 if (x > y, a = 'yes', a = 'no') 的區(qū)別
return 和 if 在加或不加的時的耗時影響
幾種迭代語句,loop, while, for
整數和浮點數的運算
預先聲明變量
變量類型的更改
實驗
針對不同的測試內容設置兩條略微不同的語句,每條語句實驗三次,收集耗時數據
自加自減
/script run a = 0; loop (100000000, a += 1000); - 11s, 10s, 11s
/script run a = 0; loop (100000000, a = a + 1000); - 18s, 19s, 18s
衛(wèi)語句
/script run?
equals(n1, n2) -> (
????if(n1 == n2, return(0));?
????if(n1 < n2, return(-1));?
????return(1)
);?
loop (5000000, equals(floor(rand(2)), floor(rand(2)))); - 43s, 41s, 41s
/script run?
equals(n1, n2) -> (
????if(n1 == n2,?
????????return(0),?
????????if(n1 < n2,?
????????????return(-1),?
????????????return(1)
????????)
????)
);?
loop (5000000, equals(floor(rand(2)), floor(rand(2)))); - 34s, 37s, 37s
賦值在if內和if外
/script run a = ''; loop (100000000, a = if(floor(rand(2)) > 1, 'y', 'n')); - 42s, 43s, 43s
/script run a = ''; loop (100000000, if(floor(rand(2)) > 1, 'a = y', a = 'n')); - 45s, 42s, 42s
return和if
/script run a() -> (return(rand(1000))); loop(1000000, a()); - 5.207s, 5.193s, 5.322s
/script run a() -> (rand(1000));? ? ? ? ?loop(1000000, a()); - 0.153s, 0.137s, 0.146s
/script run a() -> if (rand(2) - 1 > 0, 1, 0); loop (100000000, a()); - 44s, 45s, 42s
/script run a() -> rand(2) - 1 > 0; loop (100000000, a()); - 38s, 39s, 39s
幾種迭代語句
/script run loop (500000000, 0); - 27s, 25s, 26s
/script run while (true, 500000000, 0); - 41s, 43s, 41s
/script run for (range(500000000), 0); - 38s, 39s, 42s
整數與浮點數
/script run global_l1 = l(); global_l2 = l(); loop (1000000, global_l1 += floor(rand(100)); global_l2 += rand(100)); 0;
/script run loop(30, for (global_l1, a = _^_)); - 12s, 15s, 15s
/script run loop(30, for (global_l2, a = _^_)); - 12s, 13s, 13s
預先聲明變量
/script run loop(10000000, a = 0; a = 5; a = sqrt(a)) - 7.037s, 7.154s, 7.776s
/script run loop(10000000, a = 5; a = sqrt(a)) - 4.539s, 5.270s, 5.040s
變量類型更改
/script run loop(20000000, a = l(); a = 10) - 11s, 12s, 12s
/script run loop(20000000, a = l(); b = 10) - 12s, 12s, 12s
數據分析
自加語句:a+=b的速度比 a=a+b快將近一倍
衛(wèi)語句:不用衛(wèi)語句稍微好一點,但是不明顯
if內/外賦值:看不出明顯區(qū)別
return 和 if :return加了會多花不少時間,if添加前后差別不大
幾種迭代語句:loop最快,while只比for慢一點,后兩者差別不大
整數和浮點數的運算:整數反而慢一些,但是不明顯
預先聲明變量:會花費和正常賦值差不多的時間,
變量類型的更改:看不出明顯區(qū)別
總結
能用a+=b的時候就不要手賤去寫a=a+b;寧愿用麻煩的 if 嵌套也少用用 return?這一大公害;loop是個好東西,整數和浮點數也許不用那么擔心了,沒必要的預先聲明變量就寫在注釋里吧……
如有不滿,歡迎自己測試以后錘作者