最初的 C 算法几乎和 VALID C 中的一样慢。
如果您坚持使用 C,那么请尝试使用“while”循环而不是“for”:
int i = 0;
while (i< MAX)
{
// operate on the string
i++;
}
这几乎是您可以用 C 编写的最快的一维字符串操作循环,此外,如果您可以强制编译器使用“register”关键字将 i 放入寄存器中,但我被告知这几乎总是被忽略现代编译器。
同样搜索一个固定大小的数组来判断是否为空非常浪费,而且0也不是空的,它是数组中的值。
一个更好的速度解决方案是使用一个动态数组(int* piBuffer)和一个存储当前大小的变量(unsigned int uiBufferSize),当数组为空时指针为NULL,uiBufferSize为0。一个将这两个作为受保护成员变量的类。还可以轻松地为动态数组编写一个模板,该模板将存储 32 位值,原始类型或指针,对于原始类型,实际上没有任何方法可以测试“空”(我将其解释为“未定义”),但是您当然可以定义 0 来表示可用条目。对于数组指针,您应该将所有条目初始化为 NULL,并在刚刚释放该内存时将条目设置为 NULL。 NULL 确实意味着“什么都没有”,所以这是表示空的非常方便的方式。不应该在真正复杂的算法中使用动态调整大小的数组,至少在开发阶段不应该,有太多事情可能出错。至少应该首先使用 STL 容器(或经过良好测试的替代方案)实现算法,然后当代码工作时,可以将测试容器交换为简单的动态数组(如果您可以避免过于频繁地调整数组大小,则代码将更快,更安全。
对于复杂而酷的代码,一个更好的解决方案是根据您的需要使用 std::vector 或 std::map(或任何容器类 STL、本土或第 3 方),但查看您的代码我会说std::vector 就足够了。 STL 容器是模板,所以它们也应该很快。使用 STL Container 来存储对象指针(始终存储对象指针而不是实际对象,为每个条目复制整个对象会真正搞乱您的执行速度)和用于更多基本数据(位图、声音等)的动态数组,即原始类型。一般。
我是通过学习x86汇编语言手册独立提出REPE SCASW方案的,我同意使用这个字符串操作指令的例子是最快的。另一个具有单独比较、跳转等指令的汇编示例几乎肯定要慢得多(但仍然比初始 C 代码快得多,所以仍然是一篇好文章),因为字符串操作是所有现代 CPU 上优化程度最高的操作之一,他们甚至可能有自己的逻辑电路(有人知道吗?)。
REPE SCASD 不需要获取新指令,也不需要增加指令指针,这只是像我这样的汇编新手可以想出的东西,最重要的是硬件优化,字符串操作很关键对于几乎所有类型的现代软件,特别是多媒体应用程序(复制 PCM 声音数据、未压缩的位图数据等),因此每次设计新的 80x86 芯片时,优化这些指令必须是非常高的优先级。
我将它用于一种新颖的 2d 精灵碰撞算法。
它说我不能发表意见,因此请考虑以下客观评估:现代编译器(UNMANAGED C/C++,几乎所有其他内容都是托管代码,而且速度非常慢)非常擅长优化,但无法避免的是,对于非常具体的任务,编译器会生成冗余代码。可以查看编译器输出的程序集,这样就不必完全从头开始翻译复杂的算法,尽管这样做(对某些人来说)很有趣,而且用困难的方式编写代码更有价值,但是无论如何,使用“for”循环的算法,特别是关于字符串操作,通常可以非常显着地优化,因为 for 循环会生成大量代码,这通常是不需要的,例如:
for (int i = 1000; i>0; i--) DoSomething();如果编译器不是很聪明(可能是),这行会生成 6-10 行汇编,但优化的汇编版本可以是:
mov cx, 1000
_DoSomething:
// loop code....or call Func, slower but more readable
loop _DoSomething
那是 2 行,它与 C 行完全相同(它使用寄存器而不是内存地址,这要快得多,但可以说这与 C 行不完全相同,但这是语义) ,这个例子的优化程度取决于现代编译器的优化程度,我对此一无所知,但是基于以最少和更快的装配线实现算法为目标的算法分析通常效果很好,我有非常好的结果,首先在 C/C++ 中实现算法而不关心优化,然后在汇编中翻译和优化它。每条 C 行变成多条流水线的事实往往使得一些优化非常明显,而且一些指令比其他指令更快:
INC DX ; is faster than:
ADD DX,1 ;if ADD DX,1 is not just replaced with INC DX by the assembler or the CPU
LOOP ; is faster than manually decreasing, comparing and jumping
REPxx STOSx/MOVSx/LODSx is faster than using cmp, je/jne/jea etc and loop
JMP or conditional jumping is faster than using CALL, so in a loop that is executed VERY frequently (like rendering), including functions in the code so it is accessible with "local" jumps can also boost performance.
最后一点与这个问题非常相关,快速字符串操作。
所以这篇文章并不是漫无边际的。
最后,以在典型执行中需要最少跳转次数的方式设计汇编算法。
也不要费心优化不经常调用的代码,使用分析器查看最常调用的代码,然后从它开始,任何被调用少于每秒 20 次的代码(完成速度比1000 ms/20) 真的不值得优化。查看未与计时器等同步并在完成后立即再次执行的代码。另一方面,如果你的渲染循环可以在一台普通机器上完成 100+ FPS,那么优化它在经济上没有意义,但是真正的编码人员喜欢编码而不关心经济性,他们将 AppStart() 方法优化为 100 % 组装,即使它只被调用一次 :) 或者使用 az 旋转矩阵将俄罗斯方块旋转 90 度 :P 任何这样做的人都很棒!
如果有人有一些建设性的更正,这不是很伤人,那么我很想听听,我几乎完全自己编码,所以我并没有真正受到任何影响。我曾经花钱请一位优秀的加拿大游戏开发人员教我的 Direct3d,虽然我可以轻松地读一本书,但与另一位在某些领域比我水平略高的程序员互动很有趣。
总体而言,感谢您提供的优质内容。我想我会去回答一些简单的问题,回馈一下。