你见过的最核心的优化是什么?

时间:2008-10-22 15:25:56

标签: optimization

我不是在谈论算法的东西(例如使用quicksort而不是bubblesort),而我不是在讨论像循环展开之类的简单事情。

我在谈论核心的东西。与Tiny Teensy ELFThe Story of Mel一样;实际上是demoscene中的所有内容,等等。

12 个答案:

答案 0 :(得分:28)

我曾经写过一个暴力RC5密钥搜索,一次处理两个密钥,第一个密钥使用整数流水线,第二个密钥使用SSE流水线,两个密钥在指令级交错。然后将其与管理程序结合使用,该程序在系统中的每个核心上运行代码实例。总的来说,代码运行速度比原始C版本快25倍。

答案 1 :(得分:15)

在我使用的一个(这里未命名的)视频游戏引擎中,他们重写了模型导出工具(将Maya网格转换成游戏加载的东西),这样就不会只发出数据,它实际上会发出精确的微指令流,这些微指令是渲染特定模型所必需的。它使用遗传算法来找到在最小循环次数内运行的遗传算法。也就是说,给定模型的数据格式实际上是一个完美优化的子程序,用于渲染该模型。因此,在屏幕上绘制网格意味着将其加载到内存中并分支到其中。

(这不适用于PC,而是适用于具有与CPU分离且并行的向量单元的控制台。)

答案 2 :(得分:10)

在DOS的早期,当我们使用软盘进行所有数据传输时,也存在病毒。病毒感染不同计算机的一种常见方法是将病毒引导程序复制到插入的floppydisc的引导程序中。当用户将floppydisc插入另一台计算机并重新启动而不记得移除软盘时,病毒就会运行并感染硬盘启动器,从而永久感染主机PC。我被感染的特殊烦人的病毒被称为“Form”,为了对抗这一点,我写了一个具有以下功能的自定义软盘bootsector:

  • 验证主机硬盘的引导扇区,确保它没有被感染。
  • 验证软盘引导扇区和 确保它没有被感染。
  • 从中移除病毒的代码 如果它被感染了硬盘。
  • 复制防病毒的代码 bootsector到另一张软盘如果一个 按下了特殊键。
  • 启动硬盘的代码,如果一切都是 好吧,没有发现感染。

这是在bootsector的程序空间中完成的,大约440字节:)

我的伙伴最大的问题是显示的非常神秘的消息,因为我需要所有代码空间。它就像“FFVD RM?”,意思是“检测到FindForm病毒,删除?”

我很满意这段代码。优化是程序大小,而不是速度。汇编中有两种完全不同的优化。

答案 3 :(得分:9)

我最喜欢的是通过整数运算的浮点反平方根。这是关于如何存储浮点值并且可以更快执行的一个很酷的小黑客(即使执行1 /结果比股票标准平方根函数更快)或者产生比标准方法更准确的结果。

在c / c ++中,代码是:(来自维基百科)

float InvSqrt (float x)
{
    float xhalf = 0.5f*x;
    int i = *(int*)&x;
    i = 0x5f3759df - (i>>1);   // Now this is what you call a real magic number
    x = *(float*)&i;
    x = x*(1.5f - xhalf*x*x);
    return x;
}

答案 4 :(得分:8)

几年前,我用65816汇编语言为Apple IIgs写了一个基于磁贴的游戏引擎。这是一台相当慢的机器,“在金属上”编程是哄骗可接受性能的虚拟要求。

为了快速更新图形屏幕,必须将堆栈映射到屏幕,以便使用一些特殊指令,允许用户仅在5个机器周期内更新4个屏幕像素。这并不是特别奇妙,在IIgs Tech Note #70中有详细描述。硬核位是我必须组织代码以使其足够灵活以成为通用库,同时仍保持最大速度。

我将图形屏幕分解为扫描线并创建了一个246字节的代码缓冲区来插入专门的65816操作码。需要246个字节,因为图形屏幕的每条扫描线宽80字,每端需要1个附加字以便平滑滚动。推送有效地址(PEA)指令占用3个字节,因此3 *(80 + 1 + 1)= 246个字节。

通过跳转到对应于屏幕右边缘的246字节代码缓冲区内的地址并将BRanch Always(BRA)指令修补到紧随最左边的单词的代码中来呈现图形屏幕字。 BRA指令采用带符号的8位偏移量作为参数,因此只是具有跳出代码缓冲区的范围。

即使这不是太困难,但真正的核心优化也在这里。我的图形引擎实际上支持两个独立的背景图层和动画图块,使用不同的3字节代码序列,具体取决于模式:

  1. 背景1使用推送有效地址(PEA)指令
  2. 背景2使用加载间接索引(LDA($ 00),y)指令后跟推送(PHA)
  3. 动画磁贴使用加载直接页索引(LDA $ 00,x)指令后跟推送(PHA)
  4. 关键限制是65816寄存器(X和Y)都用于引用数据,不能修改。此外,直接页面寄存器(D)是基于第二背景的原点设置的,并且不能改变;数据库寄存器设置为保存第二背景的像素数据的数据库,不能更改;堆栈指针(S)映射到图形屏幕,因此不可能跳转到子程序并返回。

    鉴于这些限制,我需要快速处理即将被推入堆栈的单词混合的情况,即一半来自背景1,一半来自后台2。解决方案是交换记忆速度。因为正常寄存器的所有都在使用中,所以我只能使用程序计数器(PC)寄存器。我的解决方案如下:

    1. 定义一个代码片段,在与代码缓冲区相同的64K程序库中进行混合
    2. 为82个单词
    3. 中的每个单词创建此代码的副本
    4. 存在1-1对应关系,因此代码片段的返回可以是硬编码地址
    5. 完成!我们有一个硬编码的子程序,它不会影响CPU寄存器。
    6. 这是实际的代码片段

      code_buff:   PEA $0000            ; rightmost word (16-bits = 4 pixels)
                   PEA $0000            ; background 1
                   PEA $0000            ; background 1
                   PEA $0000            ; background 1
                   LDA (72),y           ; background 2
                   PHA
                   LDA (70),y           ; background 2
                   PHA
                   JMP word_68          ; mix the data
      word_68_rtn: PEA $0000            ; more background 1
                   ...
                   PEA $0000
                   BRA *+40             ; patched exit code
      
                   ...                  
      word_68:     LDA (68),y           ; load data for background 2
                   AND #$00FF           ; mask
                   ORA #$AB00           ; blend with data from background 1
                   PHA
                   JMP word_68_rtn      ; jump back
      word_66:     LDA (66),y
                   ...
      

      最终结果是一个接近最佳的阻击器,具有最小的开销,并且在具有1 MB / s内存总线的2.5 MHz CPU上以320x200的速度输出超过15帧/秒。

答案 5 :(得分:7)

非常生物学优化

快速背景: DNA核苷酸三联体(A,C,G和T)编码氨基酸,这些氨基酸与蛋白质结合在一起,这些蛋白质构成了大多数生物的大部分。

通常,每种不同的蛋白质需要单独的DNA三联体序列(其“基因”)来编码其氨基酸 - 例如,长度为30,40和50的3种蛋白质总共需要90 + 120 + 150 = 360个核苷酸。然而,在病毒中,空间是非常宝贵的 - 所以一些病毒与不同基因的DNA序列重叠,使用6个可能的“阅读框架”用于DNA到蛋白质的事实平移(即从可被3整除的位置开始;从用剩余1除以3的位置开始;或从用余数2除3的位置开始;再次相同,但反过来读取序列。)

为了进行比较:尝试编写一个x86汇编语言程序,其中300字节函数doFoo()从偏移量0x1000开始......另一个200字节函数doBar()从偏移量0x1001开始! (我为这次比赛提出一个名字:你比乙肝更聪明吗?

那是硬核空间优化!

更新:指向更多信息的链接:

答案 6 :(得分:5)

迈克尔·阿布拉什的“Zen of Assembly Language”有一些漂亮的东西,但我承认我不记得我头脑中的具体细节。

实际上,似乎Abrash写的所有内容都有一些漂亮的优化内容。

答案 7 :(得分:3)

斯大林计划编译器在这方面非常疯狂。

答案 8 :(得分:3)

我曾经看到一个switch语句,其中有很多空case s,交换机头部的注释说了一些内容:

  

添加了从未被命中的case语句,因为如果有多于N个的情况,编译器只会将开关转换为跳转表

我忘了N是什么。这是Windows的源代码leaked in 2004

答案 9 :(得分:2)

我已经参考了英特尔(或AMD)架构参考,看看有什么说明。 movsx - 使用符号扩展移动非常棒,可以将小符号值移动到大空格中,例如,在一条指令中。

同样,如果你知道你只使用16位值,但是你可以访问所有的EAX,EBX,ECX,EDX等 - 那么你有8个非常快的值位置 - 只需将寄存器旋转16位即可访问其他值。

答案 10 :(得分:1)

使用定制硬件生成候选密钥的EFF DES cracker(他们制作的硬件可能证明是关键不是解决方案,但无法证明关键是解决方案)然后进行了测试一个更传统的代码。

答案 11 :(得分:1)

由波兰团队制造的FSG 2.0包装机,专门用于包装用装配制成的可执行文件。如果包装组件不够令人印象深刻(假设几乎尽可能低),它配备的装载机是158字节并且功能齐全。如果您尝试使用类似UPX之类的任何程序集打造.exe,它会向您抛出NotCompressableException;)