我使用高级语言(Python,C#,VBA,VB.NET)编程已有大约10年的时间,而对于“幕后”所发生的事情,我完全不了解。
我想知道学习汇编的好处是什么,对作为程序员的我有什么帮助?您能否提供给我一个资源,向我展示我在高级代码中编写的代码与汇编中发生的代码之间的确切联系?
for
通过在循环之外声明变量来避免想到优化循环。示例
我使用高级语言(Python,C#,VBA,VB.NET)编程已有大约10年的时间,而对于“幕后”所发生的事情,我完全不了解。
我想知道学习汇编的好处是什么,对作为程序员的我有什么帮助?您能否提供给我一个资源,向我展示我在高级代码中编写的代码与汇编中发生的代码之间的确切联系?
for
通过在循环之外声明变量来避免想到优化循环。示例
Answers:
因为您会了解它是如何工作的。
归结为,我们用C#或Python编写的所有内容都需要转换为计算机可以执行的一系列基本操作。在类,泛型和列表理解方面很容易想到计算机,但是这些仅存在于我们的高级编程语言中。
我们可以想到看起来很不错但不能很好地转换为低级工作方式的语言构造。通过了解它的真正工作原理,您将更好地理解事情为什么以它们的方式工作。
通常,它将使您更好地理解“幕后”以及指针如何工作以及寄存器变量和体系结构的含义(内存分配和管理,参数传递(按值/按引用)等)。
快速浏览C,这是怎么回事?
#include <stdio.h>
main()
{
puts("Hello World.");
return(0);
}
进行编译gcc -S so.c
并查看以下程序集的输出so.s
:
$ cat so.s
.file "so.c"
.section .rodata
.LC0:
.string "Hello World."
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
movl $.LC0, (%esp)
call puts
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3"
.section .note.GNU-stack,"",@progbits
so.c
用于处理stackoverflow问题的标准文件(例如我有so.py
,so.awk
等等),以快速测试事物。So.S .. :)
gcc -O -c -g -Wa,-ahl=so.s so.c
可以看到每行C代码的汇编输出。这使得了解正在发生的事情变得容易一些。
5:so.c
以找到的第5行的代码so.c
。
我认为您在这里找到的答案是:http : //www.codeproject.com/Articles/89460/Why-Learn-Assembly-Language
文章引用:
尽管这是事实,但您可能不会发现自己在汇编中编写下一个客户的应用程序,但是学习汇编仍然有很多好处。如今,汇编语言主要用于直接硬件操纵,访问专用处理器指令或解决关键性能问题。典型用途是设备驱动程序,低级嵌入式系统和实时系统。
事实是,高级语言变得越复杂,并且写入的ADT(抽象数据类型)越多,支持这些选项的开销就越大。在.NET实例中,可能是膨胀的MSIL。想象一下,如果您知道MSIL。这是汇编语言的亮点。
汇编语言与程序员一样接近于处理器,因此精心设计的算法非常出色-汇编对于速度优化非常有用。一切都与性能和效率有关。汇编语言使您可以完全控制系统资源。就像组装线一样,您编写代码以将单个值压入寄存器,直接处理内存地址以检索值或指针。
用汇编语言编写是要确切地了解处理器和内存如何协同工作以“使事情成真”。请注意,汇编语言是神秘的,应用程序源代码的大小比高级语言大得多。但是请不要误会,如果您愿意花费时间和精力进行装配,那么您会变得更好,并且会在现场脱颖而出。
另外,我会推荐这本书,因为它具有简化的计算机体系结构版本:计算系统简介:从位和闸到C和其他,2 / e Yale N. Patt,德克萨斯大学奥斯汀分校的Sanjay J. Patel,伊利诺伊大学香槟分校
以我的拙见,它没有太大帮助。
我曾经非常了解x86组装。当我的课程中出现汇编时,它会有所帮助,在面试中它会出现一次,并帮助我证明编译器(Metrowerks)正在生成不良代码。计算机的实际运行方式令人着迷,而学习它使我在智力上更加丰富。当时玩起来也很有趣。
但是,当今的编译器比几乎任何代码段上的任何人都更擅长生成汇编。除非您正在编写编译器或检查编译器在做正确的事情,否则您可能会通过学习它来浪费时间。
我承认,C ++程序员仍然有用地提出的许多问题都是通过了解汇编来解决的。例如:我应该使用堆栈变量还是堆变量?我应该按值或const引用传递?但是,我认为在几乎所有情况下,这些选择都应该基于代码的可读性而不是节省计算时间。(例如,只要您想将变量限制为作用域,就使用堆栈变量。)
我的卑微建议是专注于真正重要的技能:软件设计,算法分析和问题解决。凭着开发大型项目的经验,您的直觉将得到改善,这比了解装配(我认为)要多得多。
您应该熟悉所使用系统中的一个“更深层次”。一口气跳得太远也不错,但可能没有人们期望的那样有用。
使用高级语言的程序员应该学习较低级别的语言(C是一个很好的选择)。当您告诉计算机实例化一个对象,创建一个哈希表或一个集合时,无需一路进行汇编就可以了解幕后的情况,但是您应该能够编写代码他们。
对于Java程序员,学习一些C语言可以帮助您进行内存管理,传递参数。用C编写一些扩展的Java库将有助于理解何时使用Set的实现(您要散列还是树?)。在线程环境中处理char *将有助于理解为什么String是不可变的。
进入下一个层次... AC程序员应该对汇编有一定的了解,并且汇编类型(通常在嵌入式系统商店中可以找到)在理解门级的东西方面可能做得很好。那些与盖茨合作的人应该了解一些量子物理学。而且那些量子物理学家,他们仍然在努力弄清楚下一个抽象是什么。
如果您精通一门语言,那么您至少应该对技术的基础知识至少低一个抽象级别。
为什么?当出现问题时,对基本机制的了解使调试奇怪的问题变得容易得多,自然可以编写效率更高的代码
以Python(/ CPython)为例,如果您开始遇到怪异的崩溃或性能下降,则有关如何调试C代码的知识可能非常有用,这与引用计数内存管理方法的知识相同。这也将帮助您知道何时/是否将某些内容编写为C扩展,依此类推...
在这种情况下要回答您的问题,汇编的知识确实并不能帮助经验丰富的Python开发人员(抽象步骤太多了-用Python完成的任何操作都会导致许多汇编指令)
..但是,如果您有C的经验,那么了解“下一级”(汇编)确实很有用。
同样,如果您使用的是CoffeScript,那么了解Javascript非常有用。如果您正在使用Clojure,则对Java / JVM的了解很有用。
这个想法在编程语言之外也可以使用-如果您使用的是Assembly,那么熟悉底层硬件的功能是一个好主意。如果您是一名Web设计人员,那么最好了解如何实现Web应用程序。如果您是汽车修理工,那么了解一些物理知识是一个好主意
编写一个小的c程序,然后反汇编输出。就这样。但是,为增加或减少为操作系统的利益而添加的“内务处理”代码做好准备。
汇编可以帮助您了解引擎盖下发生的事情,因为它直接处理内存,处理器寄存器等。
如果您真的想裸奔而又没有使操作系统复杂化的所有事情,请尝试使用汇编语言对Arduino进行编程。
没有确切的答案,因为程序员并非全部。您是否需要知道潜伏在下面的东西?如果是这样,然后学习它。您只是出于好奇而只想学习它吗?如果是这样,然后学习它。如果这对您没有实际的好处,那为什么要麻烦呢?仅仅为了开车就需要机械师的知识水平吗?机械师是否仅需要在汽车上工作就需要工程师的知识水平?这是一个严肃的类比。技工可以是一个非常出色的生产技工,而无需深入了解工程师所掌握的车辆。音乐也一样。您是否真的想把旋律,和声和节奏的复杂性变成一个好的歌手或演奏者?不会。一些非常有才华的音乐家不会读一点乐谱,更不用说告诉您Dorian和Lydian模式之间的区别了。如果您愿意,可以,但是不需要。如果您是一名Web开发人员,那么我可以想到的程序集没有实际用途。如果您是在嵌入式系统中或真的很特殊的环境中,则可能有必要,但如果确实如此,您就会知道。
这是乔尔采用非高级语言的这些价值:http : //www.joelonsoftware.com/articles/ThePerilsofJavaSchools.html
实际上,最适合您的可能是一个(据我所知)在任何地方都不存在的类:该类将机器/汇编语言和存储寻址概念的简要概述与编译器构造的导览结合在一起,代码生成和运行时环境。
问题是,使用高级,远离硬件的语言(如C#或Python),您并不会真正欣赏这样一个事实,即您所做的每一步都会变成成百上千的机器指令,而您却没有往往无法理解几行高级语言如何导致大量存储空间的访问和修改。您所需要的不只是精确地了解“幕后花絮”所发生的事情,而是需要了解正在发生的事情的范围以及对所发生事情的类型的一般概念。
我对这个问题的回答相对较新。现有的答案涵盖了我过去所说的内容。实际上,这仍然是最重要的答案 -“赞赏高级编程中的结构”,但这是我认为值得一提的特例。
根据引用研究的Jeff Atwood博客文章,了解分配是理解编程的关键问题。学习者程序员可能会理解该符号仅表示计算机遵循的步骤以及这些步骤所引起的原因,或者会因误导数学方程式等而永久困惑。
好吧,如果您了解6502汇编程序的以下内容...
LDA variable
CLC
ADC #1
STA variable
那真的只是步骤。然后,当您学习将其转换为赋值语句时...
variable = variable + 1;
您不需要与数学方程式产生误导性的类比-您已经有了一个正确的心理模型可以将其映射到。
编辑 -当然,如果您得到的解释LDA variable
基本上是ACCUMULATOR = variable
,而这恰恰是您从某些教程和参考资料中获得的解释,那么您最终会回到起点,这对您毫无帮助。
我学习了6502汇编程序作为第二语言,第一种是Commodore Basic,那时我并没有真正学到很多东西-部分是因为学习的东西很少,而且因为汇编程序看起来比以前有趣得多。一部分是时间,部分是因为我是14岁的怪胎。
我不建议做我想做的事情,但是我想知道,用一种非常简单的汇编语言学习一些非常简单的示例是否可能是学习高级语言的一个有价值的入门。
除非您是编译器作者,或者需要高度优化的知识(例如数据处理算法),否则学习汇编代码将不会给您带来任何好处。
编写和维护用汇编语言编写的代码非常困难,因此,即使您非常了解汇编语言,除非没有其他方法,否则也不要使用它。
“ 针对SSE进行优化:案例研究 ”一文显示了组装后可以执行的操作。作者设法将算法从100个周期/向量优化到17个周期/向量。
由于细节(寄存器分配等)的原因,汇编语言的编写不会给您神奇的速度提高,您可能会编写有史以来最简单的算法。
此外,对于现代(读取-在70-80年代后设计的)处理器组装,您将无法获得足够的详细信息来了解正在发生的事情(也就是说-在大多数处理器上)。就调度指令而言,现代的PU(CPU和GPU)非常复杂。了解汇编(或伪汇编)的基础知识将有助于理解计算机体系结构的书籍/课程,这些书籍/课程将提供更多的知识(缓存,无序执行,MMU等)。通常,您不需要了解复杂的ISA就可以理解它们(MIPS 5是非常流行的IIRC)。
为什么要了解处理器?它可以使您更多地了解正在发生的事情。假设您以幼稚的方式编写矩阵乘法:
for i from 0 to N
for j from 0 to N
for k from 0 to N
A[i][j] += B[i][k] + C[k][j]
对于您的目的,它可能“足够好”(如果它是4x4矩阵,则无论如何都可以编译为矢量指令)。但是,在编译大量数组时,会有一些非常重要的程序-如何对其进行优化?如果您使用汇编语言编写代码,则可能会有所改善(除非您像大多数人那样做-同样以幼稚的方式,未充分利用寄存器,不断地将数据加载/存储到内存中,实际上程序速度比HL语言慢) 。
但是,您可以反转线并神奇地获得性能(为什么?我将其保留为“家庭作业”)-IIRC取决于大型矩阵的各种因素,甚至可能达到10倍。
for i from 0 to N
for k from 0 to N
for j from 0 to N
A[i][j] += B[i][k] + C[k][j]
话虽如此-正在进行的编译器工作能够做到这一点(石墨用于gcc,波利用于使用LLVM的任何程序)。他们甚至有能力将其转换为(抱歉-我正在写对内存的阻止):
for i from 0 to N
for K from 0 to N/n
for J from 0 to N/n
for kk from 0 to n
for jj from 0 to n
k = K*n + kk
j = J*n + jj
A[i][j] += B[i][k] + C[k][j]
总而言之-了解程序集的基础知识可以使您从处理器设计中挖掘各种“细节”,从而可以编写更快的程序。了解RISC / CISC或VLIW /矢量处理器/ SIMD / ...体系结构之间的差异可能会很好。但是,我不会从x86开始,因为它们往往非常复杂(可能也是ARM)-知道什么是寄存器等,恕我直言足以启动。
通常,对于调试而言,这非常重要。当系统在指令中间中断并且错误没有意义时,您该怎么办?只要您只使用安全的代码,.NET语言的问题就不那么多了-系统几乎总是使您免受幕后操作的困扰。
简而言之,我认为答案是因为如果您学习汇编,可以做更多的事情。学习汇编可以访问嵌入式设备编程,安全性渗透和规避,逆向工程和系统编程等领域,如果您不了解汇编器,则很难进行这些工作。
至于学习它以提高程序性能,这在应用程序编程中值得怀疑。在大多数情况下,在达到此优化级别之前,有很多事情需要首先关注,例如优化磁盘和网络上的I / O访问,优化构建GUI的方式,选择正确的算法,最大化所有核心,如果要以最好的硬件来运行,就可以购买并从解释语言转换为编译语言。除非您要为其他最终用户创建软件,否则与程序员的小时工资相比,硬件是便宜的,尤其是在云可用性方面。
同样,在遇到总线故障,退出或返回代码库以在写完最后一个版本一年后更改代码后,还必须权衡提高程序执行速度和代码的可读性。
我会建议学习算法:排序,链表,二叉树,哈希等。
另请参阅Lisp,请参阅《计算机程序的结构和解释》 groups.csail.mit.edu/mac/classes/6.001/abelson-sussman-lectures该视频课程将教您所有需要了解的内容,包括算法(如何根据一些原始命令,一个Lisp原语和一些汇编程序挑衅)。
最后,如果您必须学习汇编程序,请学习一种简单的程序,例如ARM(它在x86上使用的设备大约多于4倍)。
答案是,仅因为所使用的语言必须在最后解释或编译为汇编器即可。无论是语言还是机器。
语言的设计源自CPU的工作方式。低级程序更多,高级程序更少。
最后,我要说的不仅是您需要了解很少的汇编程序,而且还需要学习汇编程序,从而了解CPU体系结构。
一些示例:许多Java程序员不了解为什么它不起作用,甚至不知道在运行时会发生什么。
String a = "X";
String b = "X";
if(a==b)
return true;
如果您知道一些汇编程序,您将始终知道存储位置的内容与“指向”该位置的指针变量中的数字不同。
更糟糕的是,即使在已出版的书籍中,您也会读到类似JAVA原语那样通过值和对象传递引用的情况,这是完全错误的。Java中的所有参数都是按值传递的,Java不能将对象传递给函数,只能将指针传递给值。
如果您现在正在汇编程序,这显然是怎么回事,如果不是,它的解释太复杂了,以至于大多数作者都只是在撒谎。
当然,这些内容的影响很小,但稍后可能会给您带来真正的麻烦。如果您知道汇编程序不是问题,那么如果不是,则表示您将进行漫长的调试工作。
String a = "X"; String b = "X"; if( a==b) return true;
实际上== true
,从which 开始是因为String interning
编译器执行了某种操作。所有其他Java语句也是错误的。Java没有指针,它有它的引用不一样的东西。而且,这些都不以任何方式与汇编程序有关。Java按值传递基元,并按值传递引用。Java没有指针,因此它无法传递任何指针。同样,所有与了解ASM无关。