作者erspicu (.)
看板Soft_Job
标题Re: [讨论] 写三元判断式code review被打枪
时间Mon Dec 26 23:04:05 2022
※ 引述《unixxxx (皓皓)》之铭言:
: 标题: Re: [讨论] 写三元判断式code review被打枪
: 时间: Sat Dec 17 03:51:38 2022
: 很多Javascript 高手都是用 switch 取代
"特定"情况下的确是好方式
举个例子 以前我在调校能时候有用过这种方式 这是c#的code部分节录
void Mem_w(ushort address, byte value)
{
if (address < 0x2000) NES_MEM[address & 0x7ff] = value;
else if (address < 0x4020) IO_write(address, value);
else if (address < 0x6000) MapperRouterW_ExpansionROM(address,value);
else if (address < 0x8000) MapperRouterW_RAM(address, value);
else MapperRouterW_PRG(address, value);
}
这个是一个非常频繁被呼叫的method 如果address 大於等於0x8000
其实前面就浪费好几次计算在判断式上
因此改成 功能上是相等的
static void Mem_w(ushort address, byte value)
{
switch (address & 0xf000)
{
case 0:
case 0x1000:
NES_MEM[address & 0x7ff] = value;
break;
case 0x2000:
case 0x3000:
case 0x4000:
IO_write(address, value);
break;
case 0x5000:
MapperObj.MapperW_ExpansionROM(address, value);
break;
case 0x6000:
case 0x7000:
MapperObj.MapperW_RAM(address, value);
break;
case 0x8000:
case 0x9000:
case 0xa000:
case 0xb000:
case 0xc000:
case 0xd000:
case 0xe000:
case 0xf000:
MapperObj.MapperW_PRG(address, value);
break;
}
}
但再简化快速下去还有大招
初始化执行一次就好
static void init_function()
{
mem_write_fun = new Action<ushort, byte>[0x10000];
for (int address = 0; address < 0x10000; address++)
{
if (address < 0x2000) mem_write_fun[address] =
new Action<ushort, byte>((addr, val) => { NES_MEM[addr & 0x7ff] = val; });
else if (address < 0x4020) mem_write_fun[address] =
new Action<ushort,byte>(IO_write);
else if (address < 0x6000) mem_write_fun[address] =
new Action<ushort, byte>(MapperObj.MapperW_ExpansionROM);
else if (address < 0x8000) mem_write_fun[address] =
new Action<ushort, byte>(MapperObj.MapperW_RAM);
else mem_write_fun[address] =
new Action<ushort, byte>(MapperObj.MapperW_PRG);
}
}
之後读取
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void Mem_w(ushort address, byte value)
{
mem_write_fun[address](address, value);
}
实际上跟switch原理很相似 但直接把mapper的实作 哪个address对应啥动作
直接写到记忆址array去了
我是不知道你所谓的javascript高手用switch替代判断式是碰到啥议题
但特定议题用改用switch 甚至自己刻死路由效能是会快非常多
以前有笔记在这边...
https://dotblogs.com.tw/enet/2017/01/21/emu_optimization_1
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 39.15.64.117 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/Soft_Job/M.1672067047.A.133.html
※ 编辑: erspicu (39.15.64.117 台湾), 12/26/2022 23:04:55
※ 编辑: erspicu (39.15.64.117 台湾), 12/26/2022 23:13:24
1F:→ MoonCode: ... 12/26 23:47
2F:→ MoonCode: 快不快还是 benchmark 或看编译出来什麽东西吧 12/26 23:48
有用FPS测过 提升很大 看到前面有人推文提到 ARRAY LOOKUP最快
其实就是这种方式
※ 编辑: erspicu (39.15.64.117 台湾), 12/26/2022 23:52:39
3F:推 MoonCode: 12/27 00:01
4F:推 jason222333: 推 12/27 06:32
5F:推 DarkIllusion: 好奇问 为什麽这里switch-case会比多重if-else快? 12/27 06:48
6F:→ DarkIllusion: 以及 为什麽这里用function map比switch-case快? 12/27 06:50
7F:推 DarkIllusion: 如果频繁判断address大於等於0x8000 那这判断放前面 12/27 06:55
没人知道 ADDRESS到底是落在哪个区间多 这就问题
8F:→ DarkIllusion: 为什麽不会是个好的解法? 12/27 06:56
9F:推 Jichang: 这不是大一计概就会教的 if else 是循序判断 switch 是ju 12/27 06:58
10F:→ Jichang: mp map是hash 但是好的compiler可以直接最佳化if else 12/27 06:58
11F:推 DarkIllusion: 抱歉 我比较没有计概常识 12/27 07:01
最终结果其实是多重因素下复合产生的 所以与其问为什麽 最快其实就是跑看看才准
当然要从理论层出发谈谈也不是不行 但谈理论有时候实际就会是出乎意料
至於 if else -> switch -> array lookup
case多时候为啥比较快 循序判断本身就是计算成本
至於switch跟array lookup其实精神上是类似做法
就一个查表动作 不过还是得实际测一下 有时候这也跟实际case有关系
就所处电脑硬体条件 如果RAM慢到一个程度 慢到一次查表
乾脆让CPU去完成几次计算 还比一次查表快 那就用if else
後来发现一切理论都没那麽可靠 要考量的太多 实测就知道答案
而且实测的答案 说不定在不同硬体条件下又是别的状况
C#会输出IL CODE看IL CODE也不太准 看JIT编译出的东西才知道真正做啥事情
知道真正做啥事情 有时候也要考虑到硬体差异
查表法说白了就是先将答案算出来记录下来 直接查 是存取记忆体ARRAY的COST
计算量大时候 先算计下来会比较快 但如果记忆体存取的COST高於重算几次的CPU计算
那乾脆就重算一次.... 这些都不是非常一定 直接测一测是最好的方式
剩下都纸上谈兵 意料之外的结果会常常发生
12F:→ DarkIllusion: 看来原PO用的是没那麽好的compiler :( 12/27 07:07
微软在C#输出的优化应该已经是标竿了
※ 编辑: erspicu (39.15.64.117 台湾), 12/27/2022 12:48:33
13F:→ leolarrel: 单纯补充一下,这应该不适合在C. 刚用gcc 看组语内容, 12/27 13:44
14F:→ leolarrel: switch 仍然用比较且跳跃来实作 12/27 13:44
其实就像前面说的 真的都要实际跑看看才准或是看到ASM层去才比较有意义
没有一定的万用通则
15F:推 MyNion: 你下面阵列开了六万多个再用for去跑,真的需要那麽多个? 12/27 15:54
16F:→ MyNion: 因为你的case只有五种,要不要数字进来都/5,这样较省空间 12/27 15:56
※ 编辑: erspicu (39.15.64.117 台湾), 12/27/2022 19:48:40
17F:推 chuegou: 就算是C 还可以问你优化编译有没有开 12/27 22:08
18F:→ peter98: 这种其实没甚麽好讨论的 就算真的这样做有用 也只是单 12/27 22:35
19F:→ peter98: 一case适用 另外 profiling的情况是有规定的 不是随便 12/27 22:35
20F:→ peter98: 跑 上次不知道哪个人开debug mode做profiling 原本想说 12/27 22:36
21F:→ peter98: 甚麽 後来想想还是算了 12/27 22:36
22F:→ peter98: 打错 我是指开IDE去run程式做profiling 12/27 22:38
23F:推 wulouise: 开IDE跟profiling是之间没有关系,除非开debug build 12/28 12:27
24F:→ leolarrel: chuegou, 我用gcc -O0跟-O3测试.你也可以自己试试看 12/28 13:19
25F:推 ku399999: 好奇效能差很多最後是快多少 func功能还是比条件判断 12/30 09:37
26F:→ ku399999: 占更多执行时间才对 12/30 09:38