我正在用C语言编写程序,并且需要使其显着更快,因为这是对性能的评估。所以我很好奇汇编代码是否可以在任何级别使C程序更快?如果我用汇编代替部分C代码,是否可以缩短程序的运行时间? (例如巨大的循环)。\
感谢。
答案 0 :(得分:5)
使用汇编替换C代码可以使代码更快当且仅当以下一个或多个条件成立时:
您的编译器生成了可怕的代码。
您忘记启用优化。
您要替换的C代码异常低效。
您正在编写的程序集正在利用编译器无法使用的CPU功能,例如矢量操作或加密加速等任务特定的原语。 (请注意,一些现代编译器也可以自动对代码进行矢量化,尽管并不总是很好。)
如果上述条件均不属实,您将浪费时间。
答案 1 :(得分:1)
简单地在汇编程序中重写C代码极不可能加快速度。事实上,它有更好的机会放慢速度。现代编译器非常擅长生成汇编代码,该汇编代码尽可能高效地表示用C编码的算法。只需确保打开编译器的优化选项。
性能提升最有可能通过各种策略获得,可用C表示:
许多其他技术也可以应用,但如果不了解您的问题,就不可能说出可能适用的内容。
答案 2 :(得分:1)
一个好的编译器会在优化方面做得很好,所以即使你是专家并且愿意花时间为某些任务找到最佳装配,你通常也不会获得太多收益。手写程序集可能对于某些内部循环是值得的,比如在一个游戏中,循环每帧每帧60fps运行一次。但是如果你不太了解CPU,你也可能比编译器更糟糕,因为最佳装配并不总是直观的。现代CPU和内存架构是复杂的野兽。
对于99%的性能问题,请忘记这一点。对于剩下的1%,在进行其他优化之前不要考虑它(见下文)。否则,您很可能没有正确的内循环进行优化。手写装配是最后一步,例如在将其他一切优化到极限后再挤出几个FPS。
相反,对于性能,首先要做的是找到瓶颈:分析和基准测试。还需要知道你所做的任何优化是否真正改善了什么,或者它们是否会使事情变得更糟(优化时并不常见,当你忘记考虑一些细节时)。
然后,提高性能的主要方法是选择正确的(子)算法和数据结构。示例:从插入排序切换到简单快速排序可能是一个巨大的改进。除非对数据进行排序,否则将会受到巨额惩罚。然后,您可以进一步改进快速排序以处理排序数据(通过随机化),在运行时调整算法,如果您知道它已排序,切换到合并排序,等等。这是数百名非常聪明的计算机科学家发明常用算法的数十年努力工作。
然后优化您自己的算法,降低其复杂性,例如使用dynamic programming技术,通过使用正确的数据结构正确组织数据......
答案 3 :(得分:1)
在汇编程序中重新编写内容可以让您的程序更快吗?是。明显更快?这取决于瓶颈的位置。
使用现代处理器,保存说明不一定能节省处理时间。即使涉及更多指令,调度操作以充分利用重叠执行也可能做得更好。规则很复杂,而且(根据我的经验)记录很好,并且因处理器而异......并且可能比人类程序员更适合机器生成指令。处理器用于运行机器生成的代码!更清洁,手工制作的代码可能看起来更漂亮,但可能无法更快地运行: - (
对于关键代码的小片段,人类可以更好地利用特殊目的指令,特别适合于任务的特殊需要。人类在可以利用问题的特殊属性的情况下也可以做得更好。在汇编程序中,人类可能甚至可以推送通用指令以从中获取更多信息。使用分支预测器可以提供帮助,并且人类可以更多地了解代码将要执行的操作,而不是编译器可以从编写的内容中推断出来。类似地,人类可能会更好地将预告提示给缓存管理以进行预读取。简而言之,人们可以(仍然)在专业领域做得更好,因为通用代码生成无法产生最佳结果。
在较大的代码片段中,人类可以通过不受ABI约束而做得更好。人类可能能够将密钥信息分配给多个函数的寄存器,并且有一些函数可以采用他们的参数并以方便呼叫者的方式返回结果,并且不需要在调用之间随时随地改变事物。 。此外,考虑到更好的全局视图,人类可能更善于在内存中分配内容以帮助缓存。简而言之,人类可以(仍然)在更广泛地看待问题时做得更好。
然而,这一切都不会便宜!并且可能需要尝试多种方法来手动优化代码,并进行一些仔细的测量以确保它确实更好。
当然,这都是假设你正在为一个"大"处理器 - 您没有指定。如果您正在编写一个非常小的PIC(比方说),则适用较旧的规则。
当然,在代码优化方面,最古老的规则是:
相信我,作为汇编程序员,我很难说出这些东西!但是,您需要一个特定的问题,以便在精心制作有效的汇编程序代码时花费时间和精力。
答案 4 :(得分:0)
如果您确信您已经尽可能地优化了C级代码,那么您可能需要考虑利用大多数现代微处理器中固有的并行处理能力。您可能需要查看 OpenMP 。
这假设耗费所有程序时间的特定算法是可并行化的......如果你的核心非常硬,你可能会考虑使用OpenCL或CUDA来利用GPU的大规模并行处理能力(假设你的平台有一个) ...)。你在谈论的那个大循环......可能会将问题分开,以便几个for循环可以同时解决问题吗?
通过追求上述目标(如果可能使用您的特定程序),您可能更有可能成功实现目标,而不是试图通过手动优化组装来击败编译器。
答案 5 :(得分:0)
您提出的每个问题的简短回答是"是"。但是,这并不意味着用汇编而不是C编写代码的一部分是值得的。
您应该首先提出并回答一些问题。
你的程序目前是否足够快?
你有资料吗?也就是说,你知道程序中瓶颈的位置吗? 专注于能够为您带来最大收益的领域。
在汇编中编写部分内容之前,是否有任何算法更改(仍在C中) 你能做到这一点可能会加快速度吗?再次,关注将给予的领域 你最有责任感。
使用更快的硬件加快速度是否切合实际?
您了解各种编译器优化设置吗?你使用相关的吗?
您是否分析过编译器生成的内容?
是否有优化空间?
您的优化目标是否切合实际?
如果在这之后你仍然认为你的程序需要一些程序集,那就试试吧。请记住,即使您可能在汇编中编写某些内容,但它并不意味着它会比编译器生成的内容更快。 (毕竟,编译器也会生成程序集。)
答案 6 :(得分:0)
我不同意那些建议通过在程序集中进行挖掘无法改进编译器优化的人。以上关于改进算法的所有建议都是有效的,我认为这是重要的第一步。但是,一旦你有一段代码,你已经在更高级别的语言中尽可能地进行了改进和优化,那么在使用反汇编程序处理代码以查找是否有任何代码时,可能会有一些实用工具。 代码中的瓶颈。
另一个观察点是,同一语言中的不同语言甚至编译器会在可执行文件中生成不同结构的系统代码。如果你的目标是特定的架构,你可能会削减一些脂肪,但你真的必须非常具体地你需要导入哪些例程操作系统期望您做的所有事情,以及禁止您做的事情。
您有一个关于减少运行时大小的有效观点。如果您只使用特定的例程,例如 puts
并且您要包含整个conio
和stdio
,那么您将能够消除相当大的代码块通过将组件引入混合中而不是使用标准库来进行使用。但是,脱离标准合规可能会有问题;当预期的行为开始失败时,用户将能够立即判断您的软件是否实施不当。 (例如通过less
或more
管道程序输出的能力 - 使用BIOS例程可能会更快,但是当你给操作系统冷落时,这种能力就会失败。)