Why aren't programs written in Assembly more often?
似乎主流观点认为汇编编程需要更长时间并且比C等更高级别的语言更难编程。因此,似乎建议或假设出于这些原因更好地编写更高级别的语言并且为了更好的便携性。
最近我一直在写x86汇编,我突然意识到这些原因可能不是真的,除了可移植性。也许这更多的是熟悉并且知道如何很好地编写装配。我还注意到汇编中的编程与HLL中的编程完全不同。也许一个优秀且经验丰富的汇编程序员可以像经验丰富的C程序员用C语言一样轻松快速地编写程序。
也许是因为汇编程序设计与HLL完全不同,因此需要不同的思维,方法和方法,这使得为不熟悉的程序编程看起来很尴尬,因此给它编写程序的坏名称。
如果可移植性不是问题,那么真的,C会对NASM这样的好汇编程序有什么影响?
编辑:
只是要指出。在汇编时编写时,不必只是在指令代码中编写。您可以使用宏和过程以及您自己的约定来进行各种抽象,以使程序更加模块化,更易于维护和更易于阅读。这是熟悉如何编写良好汇编的地方。
Hell?,我是编译器。
我在阅读这句话时只扫描了数千行代码。我根据您将花费数年时间进行的大量学术研究,使用数百种不同的优化技术浏览了数百种优化单行产品的可能性。当我将三行循环转换为数千条指令以使其更快时,我不会感到任何尴尬,甚至不是轻微的ick。我不遗余力地进行大量的优化或做最肮脏的技巧。如果你不想要我,也许一两天,我会按照你喜欢的方式行事。我可以随时转换我正在使用的方法,甚至不用改变代码的一行。我甚至可以向您展示您的代码在汇编,不同处理器架构和不同操作系统以及不同的汇编约定中的外观(如果您愿意)。是的,几秒钟内完成。因为,你知道,我可以;而且你知道,你做不到。
附:哦,顺便说一句,你没有使用你编写的一半代码。我帮了你一个忙,把它丢了。
与高级语言相比,ASM的易读性差,并且不能真正维护。
此外,ASM开发人员比其他更流行的语言(例如C)少得多。
此外,如果您使用更高级别的语言并且新的ASM指令可用(例如SSE),您只需要更新编译器,您的旧代码就可以轻松地使用新指令。
如果下一个CPU有两倍的寄存器怎么办?
与此问题相反的是:编译器提供了哪些功能?
我怀疑你能否/希望/优化你的ASM比
我已经为6502,Z80,6809和8086芯片编写了大量的汇编程序。一旦C编译器可用于我正在处理的平台,我就会停止这样做,并且立即变得至少提高了10倍。大多数优秀的程序员出于合理的原因使用他们使用的工具。
我喜欢使用汇编语言编程,但是需要更多的代码才能完成与高级语言相同的操作,并且代码行和错误之间存在直接关联。 (这是几十年前在神话人月中所解释的。)
可以将C视为"高级装配",但要比上面几步更快,你就处于一个不同的世界。在C#中,你不要三思而后行:
1 | foreach (string s in listOfStrings) { /* do stuff */ } |
这将是几十个,也许是数百行代码的汇编,每个程序员实现它将采取不同的方法,而下一个人出现将不得不弄明白。因此,如果您认为(尽可能多)程序主要是为其他人阅读而编写的,那么程序集的可读性就低于典型的HLL。
编辑:我积累了一个用于常见任务的个人代码库,以及用于实现类C控件结构的宏。但是当我成为常态时,我在90年代碰壁了。在常规事物上花费了太多时间。
几年前ASM必不可少的最后一项任务是编写代码来对抗恶意软件。没有用户界面,所以这是没有膨胀的所有有趣的部分。
除了其他人的可读性,可维护性,更短的代码,因此更少的错误,以及更容易的答案,我还将添加一个额外的原因:
程序速度。
是的,在汇编中,您可以手动调整代码以利用每个最后一个周期并使其尽可能快地完成。但谁有时间?如果你编写一个不完全愚蠢的C程序,编译器将很好地为你做优化。可能需要手动完成至少95%的优化,而不必担心跟踪其中的任何优化。这里肯定有一个90/10的规则,其中最后5%的优化将最终占用95%的时间。那为什么要这么麻烦?
如果一个平均生产程序说100k行代码,并且每行约8-12个汇编指令,那将是100万个汇编指令。
即使您可以以合适的速度手动编写所有这些(请记住,它的代码是您必须编写的8倍),如果您想要更改某些功能会发生什么?几个星期前你在这100万条指令中理解你所写的东西是一场噩梦!没有模块,没有类,没有面向对象的设计,没有框架,没有任何东西。即使是最简单的事情,您必须编写的类似外观代码的数量也是令人生畏的。
此外,您几乎不能像高级语言那样优化代码。例如,由于您描述了您的意图,而不仅是您的代码,而您只编写代码,因此汇编程序无法对您的代码执行任何值得注意的优化。你写的是你得到的,相信我,你不能可靠地优化你编写它时修补和修补的100万条指令。
好吧,我在"过去"写过很多集会,我可以向你保证,当我用高级语言编写程序时,我的工作效率会更高。
在汇编时编写时,不必只是在指令代码中编写。您可以使用宏和过程以及您自己的约定来进行各种抽象,以使程序更加模块化,更易于维护和更易于阅读。
所以你基本上说的是,通过熟练使用复杂的汇编程序,你可以使你的ASM代码越来越接近C(或者你自己发明的另一种低级语言),直到最终你只是和C程序员一样高效。
这是否回答你的问题? ;-)
我没有说这句话:我已经使用了这样的汇编程序和系统编程。更好的是,汇编程序可以定位虚拟处理器,并且单独的转换器为目标平台编译汇编程序的输出。与LLVM的IF一样,但在其早期形式中约会10年。因此具有可移植性,以及为特定目标合并器编写例程以满??足效率的能力。
使用该汇编程序的编写与C一样高效,并且与GCC-3(在我参与的时候)相比,汇编程序/翻译器生成的代码大致同样快,通常更小。规模非常重要,该公司的程序员很少,并愿意在新员工做任何有用的事情之前教新员工。我们得到了备份,那些不了解汇编程序的人(例如客户)可以编写C并使用相同的调用约定等编译它来使用相同的虚拟处理器,以便它能够整齐地连接。所以这感觉就像是一场微不足道的胜利。
这就是开发汇编技术,库等等多年的工作。不可否认的是,如果它只是针对一个架构,那么其中大部分都是可移植的,那么全能的全能舞蹈汇编器就会容易得多。
总结:你可能不喜欢C,但这并不意味着使用C的努力大于提出更好的东西的努力。
合理水平的汇编程序能力是一项非常有用的技能,特别是如果你在任何系统级别或嵌入式编程中工作,而不是因为你必须编写那么多的汇编程序,但是因为有时理解这个盒子真的在做什么很重要。如果您对汇编器概念和问题没有一个低级别的了解,那么这可能非常困难。
然而,至于在汇编程序中实际编写大量代码,有几个原因并没有做太多。
-
根本没有(几乎)需要。除了非常早期的系统初始化以及隐藏在C函数或宏中的一些汇编程序片段之外,所有可能曾经用汇编语言编写的非常低级的代码都可以用C或C ++编写而没有任何困难。
-
高级语言(甚至C和C ++)中的代码将功能集中到更少的行中,并且有大量研究表明错误的数量与源代码行数相关。也就是说,在汇编程序和C中解决的同样问题将在汇编程序中出现更多错误,因为它更长。同样的论点促使人们转向更高级别的语言,如Perl,Python等。
-
在汇编程序中编写,你必须处理问题的每个方面,从详细的内存布局,指令选择,算法选择,堆栈管理等。更高级别的语言将所有这些都从你身边带走,这就是为什么这么多密集的LOC条款。
从本质上讲,上述所有内容都与汇编程序与C或其他语言中可用的抽象级别有关。汇编程序强迫您完成所有自己的抽象,并通过自己的自律来维护它们,其中任何中级语言(如C语言,尤其是更高级语言)为您提供开箱即用的抽象,以及能够相对容易地创建新的。
作为一名将大部分时间都花在嵌入式编程领域的开发人员,我认为汇编远非死亡/过时的语言。存在一定的接近金属级别的编码(例如,在驱动程序中),有时无法在更高级别的语言中准确或有效地表达。我们在汇编程序中编写了几乎所有的硬件接口例程。
话虽这么说,这个汇编代码被包装,以便它可以从C代码调用,并被视为库。出于多种原因,我们不会在程序集中编写整个程序。首要的是便携性;我们的代码库用于使用不同体系结构的多个产品,我们希望最大化可以在它们之间共享的代码量。其次是开发人员熟悉。简而言之,学校不像过去那样教授装配,而且我们的开发人员在C方面的工作效率远远高于装配方式。此外,我们有可用于我们的C代码的各种"附加功能"(如库,调试器,静态分析工具等),这些代码不适用于汇编语言代码。即使我们想编写一个纯汇编程序,我们也无法编写,因为几个关键的硬件库只能作为C库使用。从某种意义上说,这是一个鸡/蛋问题。由于没有尽可能多的库和开发/调试工具,人们被驱逐出组装,但是libs / tools并不存在,因为没有足够的人使用程序集来保证创建它们的努力。
最后,几乎任何语言都有时间和地点。人们使用他们最熟悉和最富有成效的东西。在程序员的汇编程序中可能总会有一个位置,但是大多数程序员会发现他们可以用更高级别的语言编写代码,这种语言在很短的时间内几乎同样有效。
组装在不同的微处理器之间不可移植。
我们不再去外面的浴室,或者为什么我们不说拉丁语或亚拉姆语。
技术出现并使事情变得更容易和更容易获得。
编辑 - 为了停止犯罪的人,我删除了某些单词。
为什么?简单。
比较一下:
1 2 3 4 5 6 7 8 9 10 | for (var i = 1; i <= 100; i++) { if (i % 3 == 0) Console.Write("Fizz"); if (i % 5 == 0) Console.Write("Buzz"); if (i % 3 != 0 && i % 5 != 0) Console.Write(i); Console.WriteLine(); } |
同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | .locals init ( [0] int32 i) L_0000: ldc.i4.1 L_0001: stloc.0 L_0002: br.s L_003b L_0004: ldloc.0 L_0005: ldc.i4.3 L_0006: rem L_0007: brtrue.s L_0013 L_0009: ldstr"Fizz" L_000e: call void [mscorlib]System.Console::Write(string) L_0013: ldloc.0 L_0014: ldc.i4.5 L_0015: rem L_0016: brtrue.s L_0022 L_0018: ldstr"Buzz" L_001d: call void [mscorlib]System.Console::Write(string) L_0022: ldloc.0 L_0023: ldc.i4.3 L_0024: rem L_0025: brfalse.s L_0032 L_0027: ldloc.0 L_0028: ldc.i4.5 L_0029: rem L_002a: brfalse.s L_0032 L_002c: ldloc.0 L_002d: call void [mscorlib]System.Console::Write(int32) L_0032: call void [mscorlib]System.Console::WriteLine() L_0037: ldloc.0 L_0038: ldc.i4.1 L_0039: add L_003a: stloc.0 L_003b: ldloc.0 L_003c: ldc.i4.s 100 L_003e: ble.s L_0004 L_0040: ret |
它们的特征相同。
第二个甚至不是汇编程序,而是.NET IL(中间语言,类似于Java的字节码)。第二次编译将IL转换为本机代码(即几乎是汇编程序),使其更加密码化。
我猜甚至x86(_64)上的ASM在你通过利用编译器难以优化的指令获得大量收益的情况下也是有意义的。例如,x264使用大量的asm进行编码,速度提升很大。
我确信有很多原因,但我能想到的两个原因很简单
其中一个早期发现(你可以在布鲁克斯的神话人月中找到它,这是从20世纪60年代的经验中得到的)是人们在一种语言中或多或少的生产力,在每天调试的代码行中。这显然不是普遍真实的,并且可以在被推得太远时打破,但对于布鲁克斯时代的高级语言来说通常是正确的。
因此,提高工作效率的最快方法是使用一个单独的代码行做得更多的语言,这确实有用,至少对于像FORTRAN和COBOL这样复杂的语言,或者给出一个更现代的例子C.
可移植性总是一个问题 - 如果不是现在,至少最终。编程行业每年花费数十亿美元来移植旧软件,这些软件在编写时"显然"没有任何可移植性问题。
随着程序集变得不那么普遍,出现了一个恶性循环:随着高级语言的成熟,汇编语言指令集的构建不仅为程序员提供了方便,而且为了方便编译器而构建。
所以现在,实际上,可能很难做出正确的决定,例如,您应该使用哪些寄存器或哪些指令稍微有效。编译器可以使用启发式算法来确定哪些权衡可能具有最佳收益。我们可以通过较小的问题思考并找到可能超过我们现在非常复杂的编译器的局部优化,但是在一般情况下,一个好的编译器在第一次尝试时会比一个优秀的程序员可能会做得更好。最终,像约翰亨利一样,我们可能会击败机器,但我们可能会严重焚烧自己到达那里。
我们的问题现在也完全不同了。 1986年,我试图弄清楚如何通过在屏幕上放置几百个像素的小程序来获得更高的速度;我希望动画不那么生涩。汇编语言的公平案例。现在我想弄清楚如何用抵押合同语言和服务商政策来表示抽象,我宁愿读一些看起来接近商业人士所说语言的东西。与LISP宏不同,汇编宏不会在规则方面强制执行,因此即使你能够在一个好的汇编器中得到一些合理接近DSL的东西,它也会容易出现各种各样的怪癖。如果我在Ruby,Boo,Lisp,C#甚至F#中编写相同的代码,都会导致我的问题。
如果您的问题很容易以高效的汇编语言表达,那么,您的权力就更大了。
与其他人所说的大部分内容相同。
在C发明之前的美好时光,当唯一的高级语言是像COBOL和FORTRAN这样的东西时,有许多事情在没有求助于汇编程序的情况下是不可能完成的。这是获得全方位灵活性,能够访问所有设备等的唯一方法。但是C被发明了,几乎所有可能在装配中的任何东西都可能在C中。我写了很少的装配,因为然后。
也就是说,我认为对于新程序员学习用汇编语言编写来说,这是一个非常有用的练习。不是因为他们实际上会使用它太多,而是因为那样你才能理解计算机内部真正发生的事情。我已经看到很多编程错误和程序员的低效代码,他们显然不知道比特,字节和寄存器到底发生了什么。
我现在已经在装配编程大约一个月了。我经常在C中编写一段代码,然后将其编译为汇编以帮助我。也许我没有利用C编译器的全部优化功能,但似乎我的C asm源包含不必要的操作。所以我开始看到一个好的C编译器的表现优于一个好的汇编编码器并不总是如此。
无论如何,我的装配程序如此之快。而且我使用汇编越多,我写出代码的时间就越少,因为它真的不那么难。关于组装易读性差的评论也是不正确的。如果您正确地标记您的程序并在需要进一步详细说明时进行注释,则应该全部设置。事实上,程序员对程序集更清晰,因为他们正在看到处理器级别正在发生的事情。我不了解其他程序员,但对我来说,我喜欢知道发生了什么,而不是在某种黑盒子里。
据说编译器的真正优势在于编译器可以理解模式和关系,然后在源中的适当位置自动编码。一个流行的例子是C ++中的虚函数,它要求编译器优化映射函数指针。但是编译器仅限于执行编译器的制造商允许编译器执行的操作。这导致程序员有时不得不诉诸于他们的代码做奇怪的事情,增加编码时间,当他们可以通过汇编完成。
就个人而言,我认为市场大力支持高级语言。如果汇编语言是当今唯一存在的语言,那么他们的编程人数将减少约70%,谁知道我们的世界会在哪里,可能早在90年代。更高级别的语言吸引更广泛的人群。这允许更多的程序员供应来构建我们世界所需的基础设施。像中国和印度这样的发展中国家受益于像Java这样的语言。这些国家将快速发展其IT基础设施,人们将变得更加互联。所以我的观点是高级语言很受欢迎,不是因为它们产生了优越的代码,而是因为它们有助于满足世界市场的需求。
人们似乎忘记了另一个方向。
你为什么一开始在Assembler写作?为什么不用真正的低级语言编写程序呢?
代替
1 2 3 4 | mov eax, 0x123 add eax, 0x456 push eax call printInt |
你也可以写
1 2 3 4 | B823010000 0556040000 50 FF15..... |
这有很多优点,你知道你的程序的确切大小,你可以重复使用指令的值作为其他指令的输入,你甚至不需要汇编程序来编写它,你可以使用任何文本编辑器...
而你更喜欢Assembler的原因,是其他人喜欢C的原因......
C对一个好的宏汇编程序的作用是语言C.类型检查。循环结构。自动堆栈管理。 (几乎)自动变量管理。汇编程序中的动态内存技术是一个巨大的痛苦。正确地执行链接列表与C相比可能是可怕的,或者更好的是列出foo.insert()。和调试 - 好吧,没有比较容易调试的比赛。 HLL在那里赢得了胜利。
我编写了近一半的汇编程序,这让我很容易在assmebler中思考。它帮助我看到C编译器正在做什么,这再次帮助我编写C编译器可以有效处理的代码。用C语言编写的经过深思熟虑的例程可以编写,只需要一点点工作就可以输出你想要的汇编程序 - 并且它是可移植的!出于跨平台的原因,我已经不得不将一些较旧的asm例程重写回C,这并不好玩。
不,我会坚持使用C并处理偶然的??性能轻微下降与我从HLL获得的生产力时间。
我只能回答为什么我个人不经常在汇编中编写程序,主要原因是这样做更乏味。此外,我认为如果不立即注意,更容易弄错。例如,您可能会改变在一个例程中使用寄存器的方式,但忘记在一个地方更改它。它组装好了,你可能要到很晚才注意到。
也就是说,我认为仍有有效的装配用途。例如,我有许多非常优化的汇编程序用于处理大量数据,使用SIMD并遵循偏执的"每一点都是神圣的"[引用V.Stob]方法。 (但请注意,天真的程序集实现通常比编译器为您生成的更糟糕。)
C是一个宏汇编程序!这是最好的!
它可以做几乎所有装配都可以,它可以是便携式的,并且在大多数罕见的情况下,它无法做某事你仍然可以使用嵌入式汇编代码。这只留下了一小部分程序,你绝对需要在汇编中编写,而只需要汇编。
更高级别的抽象和可移植性使得大多数人在C语言中编写系统软件更有价值。虽然如果你在编写某些程序时花费了大量的时间和金钱,现在可能不需要可移植性,你可能不想限制自己在将来你将能够使用它。
我现在正在学习comp org中的汇编,虽然它很有趣,但写入它也是非常低效的。你必须在头脑中保留更多细节以使事情正常工作,并且编写相同的东西也更慢。例如,C ++中一个简单的6行循环可以等于18行或更多的汇编。
就个人而言,它很有趣,可以了解硬件级别的工作情况,并让我更加了解计算的工作原理。
$$$
一家公司聘请开发人员帮助将代码转换为$$$。生成有用代码的速度越快,公司将代码转换为$$$的速度就越快。
更高级别的语言通常更好地生成大量有用的代码。这并不是说集会没有它的位置,因为有些时候和地方没有别的办法。
因为它总是那样:时间过去和好事也消失了:(
但是当你编写asm代码时,它与编写高级代码时的感觉完全不同,尽管你知道它的效率要低得多。这就像你是一个画家:你可以随心所欲地画出你喜欢的任何东西,完全没有限制(好吧,只有CPU功能)...这就是我喜欢它的原因。遗憾的是这种语言消失了。但是,虽然有人仍记得并编码,但它永远不会死!
当您将汇编语言与C语言比较高级语言进行比较时,HLL的优势甚至更大。 Java或Python或Ruby。例如,这些语言具有垃圾收集:无需担心何时释放大量内存,并且由于过早释放而没有内存泄漏或错误。
正如其他人之前提到的,任何工具存在的原因都在于它的工作效率。由于HLL可以完成与许多asm代码行相同的工作,我想组装被其他语言取代是很自然的。对于接近硬件的摆弄 - 根据语言,C和其他变体的内联汇编。
Paul Carter博士在PC汇编语言中说
"...a better understanding of how
computers really work at a lower level
than in programming languages like
Pascal. By gaining a deeper
understanding of how computers work,
the reader can often be much more
productive developing software in
higher level languages such as C and
C++. Learning to program in assembly
language is an excellent way to
achieve this goal."
我们在大学课程中介绍了装配。它有助于清除概念。但是我怀疑我们中的任何人都会在汇编中编写90%的代码。今天深入的装配知识有多相关?
翻阅这些答案,我敢打赌9/10的响应者从未使用过汇编。
这是一个古老的问题,经常出现,你会得到相同的,大多是错误的答案。如果它不是为了便携性,我仍然会自己做所有的装配。即便如此,我在C中的编码几乎就像我在汇编时所做的那样。
不同之处在于 - 汇编程序是一种代码艺术,是一个适当的艺术家手中的好画。你比糟糕的编译器代码更聪明?如果你是,使用它或用c和汇编器一起照顾你的画。
呵呵,我是一个数据流系统。这个我正在运行的应用程序,充满了各种各样的组件。它是一个分布式应用程序,它位于3台计算机上,位于功能强大的x86和两个较小的ARM上。大多数组件都是用C ++编写的,但是在ASM中为x86编写了一个关键的组件。此外,大多数组件有几种变体:针对不同的处理器进行编译,还有一些组件具有特殊的GPU版本。遗憾与否,我有一个脚本组件(一个包装器组件调用脚本),它打印报告,但每年只打印一次。它甚至不会伤害它只是一个脚本,一个缓慢的脚本。
作为智能数据流应用程序,我知道,我的架构仅适用于某些任务,其中信号/数据通过图形流动,例如自动化,视频/图像处理,音频处理(所有合成器使用数据流)但幸运的是,这些应用领域是非常大马力的,优化是必不可少的。
我非常确定,在美好的一天会出现几个其他架构,这也是关于优化(以及其他使得编程更容易等等),并且它们将能够覆盖更多或其他领域,我,数据流可以'吨。
因此,"C vs ASM"主题不是一个真正的困境。这就像争论数字或模拟合成器更好(正如我所提到的,我已经融入了合成器)。我建议,做好音乐。或听。随你。 C不是与ASM相对的。无论如何,我从未见过由ASM攻击过的C程序,反之亦然。
抱歉英语不好,尽管我不是一个新的技术,但我并不广为人知,我还是个孩子。有前途的。来吧,看看我的个人资料!