使用的编译器:MASM8.0和Visual Studio 2008
我们主要通过C++和汇编各自构建一个功能完全相同的链表结构,其中这个链表的功能有:
1. 声明结构
2. 构建链表
3. 对链表进行冒泡排序
4. 对链表进行复制操作
5. 计算从1-5所花费的毫秒数这里就是我们链表所要做的事情,同样的,我们链表采用的是指针链表,单向,非双向,考虑到汇编实现起来会有很多障碍,所以我们尽量让代码简洁些,这样子能够更好的从C++翻译到Assembly里面.好的,那我么现在就开始吧.首先,我们要先构造一个数组,这个数组我决定用汇编编译器来生成,反正代码之前都已经写好过了:
The Code:
.code
main PROC
call randomize
mov ecx,20
L1:
mov eax,1000
call RandomRange
call Writedec
call crlf
loop L1
exit
main ENDP
END main
所生成的随机数组:773 270 206 705 532 160 689 18 291 876 405 935 989 950 102 564 30 235 288 613
通过刚才的代码我们直接生成了一组没有重复过的二十个的数字,接下来我们就要用这些数字来进行一下所有的操作. 首先,我们先构建C++大约的框架,和汇编大约的框架.这里可能会有所不通,但是这只是一开始的设想,接下来还是会改的的
The code of C++
// 链表测试项目.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
struct LinkofData
{
int temp;
LinkofData *next;
};
void main()
{
}
void CreateTheLink()
{
}
void SortTheLink()
{
}
void CopyTheLink()
{
}
int TheTimeToPass()
{
}
接下来完善代码
FULL Code of C++
// 链表测试项目.cpp : 定义控制台应用程序的入口点。
//
//-------------------------------------------------------------------------预编译区
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
#include "time.h"
#include <iostream>
using namespace std;
//-------------------------------------------------------------------------声明结构
struct LinkofData
{
int temp;
LinkofData *next;
};
//-------------------------------------------------------------------------链表构造函数
void CreateTheLink(int a[],LinkofData *linklist)
{
linklist->temp = NULL;
linklist->next = NULL;
for (int i=0;i<20;i++)
{
LinkofData *linklist1=new LinkofData;
linklist1->temp=a[i];
linklist1->next=NULL;
linklist->next=linklist1;
linklist=linklist1;
}
}
//-------------------------------------------------------------------------冒泡排序函数
void SortTheLink(LinkofData *linklist)
{
LinkofData *o,*p,*q;
for (int i=0;i<19;i++)
{
o=linklist;
for (int j=0;j<19;j++)
{
if ((o->next->temp) > (o->next->next->temp))
{
p=o->next;
q=o->next->next;
p->next=q->next;
q->next=p;
o->next=q;
o=o->next;
}
else
{
o=o->next;
}
}
}
}
//-------------------------------------------------------------------------复制函数
void CopyTheLink(const LinkofData *link1,LinkofData *link2)
{
LinkofData *p;
p=link1->next;
LinkofData *Topoflink2;
Topoflink2=link2;
link2->temp=NULL;
link2->next=NULL;
while(p->next != NULL)
{
LinkofData *templink2=new LinkofData;
templink2->temp=p->temp;
link2->next=templink2;
link2=link2->next;
p=p->next;
}
link2->next=NULL;
link2=Topoflink2;
}
//-------------------------------------------------------------------------显示链表
void DisplayTheLinklist(const LinkofData *Linklist)
{
LinkofData *temp;
temp=Linklist->next;
for(int i=0;i<20;i++)
{
cout<<temp->temp;
temp=temp->next;
cout<<"->";
}
}
//-------------------------------------------------------------------------主函数区
void main()
{
long start,finish;
double dtime;
start=clock();
int a[20]={773,270,206,705,532,160,689,18,291,876,405,935,989,950,102,564,30,235,288,613};
LinkofData *Linklist1;
Linklist1 = new LinkofData;
LinkofData *Linklist2;
Linklist2 = new LinkofData;
for (int i=0;i<200000;i++)
{
CreateTheLink(a,Linklist1);
SortTheLink(Linklist1);
CopyTheLink(Linklist1,Linklist2);
}
finish=clock();
dtime=(double)(finish-start);
cout<<dtime;
system("pause");
}
程序大概的流程图
FUll Code of Assembly:
TITLE Creating a Linked List (List.asm)
;// This program shows how the STRUC directive
;// and the REPT directive can be combined to
;// create a linked list at assembly time.
;// Last update: 11/8/02
;//仿真要求:要根据指针域来寻址
INCLUDE Irvine32.inc
CreateLinkList proto
SortLinkList proto
CopyLinkList proto
ListNode STRUCT
NodeData DWORD ?
NextPtr DWORD ?
ListNode ENDS
TotalNodeCount = 22
NULL = 0
Counter = 0
.data
TheNum DWORD 0,773,270,206,705,532,160,689,18,291,876,405,935,989,950,102,564,30,235,288,613
LinkedList ListNode TotalNodeCount dup(<0,0>)
LinkedList1 ListNode TotalNodeCount dup(<0,0>)
temp dword ?
tempq dword ?
tempp dword ?
StartTimer dword ?
.code
main PROC
call getmseconds
mov StartTimer,eax
repeat 200000
call CreateLinkList
call SortLinkList
call CopyLinkList
endm
call getmseconds
sub eax,StartTimer
call WriteDec
call crlf
;//--------------------------------------------开始遍历链表
mov esi,OFFSET LinkedList
NextNode:
; Check for the tail node.
mov eax,(ListNode PTR [esi]).NextPtr
cmp eax,NULL
je quit1
; Display the node data.
mov eax,(ListNode PTR [esi]).NodeData
call WriteDec
call Crlf
; Get pointer to next node.
mov esi,(ListNode PTR [esi]).NextPtr
jmp NextNode
quit1:
;//-------------------------------------------链表遍历完毕
call waitmsg
exit
main ENDP
CreateLinkList PROC
push ebp
mov ebp,esp
;//--------------------------------------------构建链表
push ecx
push edi
push eax
push esi
mov ecx,21
mov edi,offset LinkedList
mov esi,0
LW:
mov eax,dword ptr TheNum[esi]
mov (ListNode ptr [edi]).NodeData,eax
mov eax,edi
add eax,type ListNode
mov (ListNode ptr [edi]).NextPtr,eax
add edi,type ListNode
add esi,type TheNum
LOOP LW
push esi
push eax
push edi
push ecx
;//--------------------------------------------链表构建完毕
mov esp,ebp
pop ebp
ret
CreateLinkList ENDP
SortLinkList PROC
push ebp
mov ebp,esp
;//--------------------------------------------冒泡排序开始
;// 这里用到了 edi 头寻址 ecx 循环控制 ebx,edx,
push edi
push ecx
push eax
push ebx
push edx
push esi
mov edi,offset LinkedList
mov ecx,19
;//根据定理,这里的offset Linkedlist永远是指向一个固定位置的
maopao1:
push ecx
mov ecx,19
mov eax,edi
maopao2:
mov ebx,(ListNode PTR [eax]).NextPtr
mov edx,(ListNode PTR [ebx]).NextPtr
mov esi,(ListNode PTR [ebx]).NodeData
cmp esi,(ListNode PTR [edx]).NodeData
jb allhavetodo
bigger:;//-----------------------------------进行对换
mov (ListNode PTR [eax]).NextPtr,edx
push eax
mov eax,(ListNode PTR [edx]).NextPtr
mov (ListNode PTR [ebx]).NextPtr,eax
pop eax
mov (ListNode PTR [edx]).NextPtr,ebx
;//-----------------------------------对换结束
allhavetodo:
mov eax,(ListNode PTR [eax]).NextPtr
;//-----------------------------------上面的eax指向eax的下一个地址
loop maopao2
pop ecx
loop maopao1
push esi
push edx
push ebx
push eax
push ecx
push edi
mov esp,ebp
pop ebp
ret
;//--------------------------------------------冒泡排序结束
SortLinkList ENDP
CopyLinkList PROC
push ebp
mov ebp,esp
;//--------------------------------------------链表复制开始
;// edi为LinkedList首地址 ebx为LinkedList1首地址 eax为临时变量
push edi
push ebx
push eax
mov edi,offset LinkedList
mov ebx,offset LinkedList1
Copytwo:
mov eax,(ListNode PTR [edi]).NextPtr
cmp eax,NULL
je quit
mov eax,(ListNode ptr [edi]).NodeData
mov (ListNode ptr [ebx]).NodeData,eax
mov eax,ebx
add eax,type ListNode
mov (ListNode ptr [ebx]).NextPtr,eax
add edi,type ListNode
add ebx,type ListNode
jmp Copytwo
quit:
pop eax
pop ebx
pop edi
mov esp,ebp
pop ebp
ret
;//--------------------------------------------链表复制结束
CopyLinkList ENDP
END main
FUll Code2 of Assembly:
TITLE Creating a Linked List (List.asm)
;// This program shows how the STRUC directive
;// and the REPT directive can be combined to
;// create a linked list at assembly time.
;// Last update: 11/8/02
;//仿真要求:要根据指针域来寻址
INCLUDE Irvine32.inc
CreateLinkList proto
SortLinkList proto
CopyLinkList proto
ListNode STRUCT
NodeData DWORD ?
NextPtr DWORD ?
ListNode ENDS
TotalNodeCount = 22
NULL = 0
Counter = 0
.data
TheNum DWORD 0,773,270,206,705,532,160,689,18,291,876,405,935,989,950,102,564,30,235,288,613
LinkedList ListNode TotalNodeCount dup(<0,0>)
LinkedList1 ListNode TotalNodeCount dup(<0,0>)
temp dword ?
tempq dword ?
tempp dword ?
StartTimer dword ?
.code
main PROC
call getmseconds
mov StartTimer,eax
repeat 200000
call CreateLinkList
call SortLinkList
call CopyLinkList
endm
call getmseconds
sub eax,StartTimer
call WriteDec
call crlf
;//--------------------------------------------开始遍历链表
mov esi,OFFSET LinkedList
NextNode:
; Check for the tail node.
mov eax,(ListNode PTR [esi]).NextPtr
cmp eax,NULL
je quit1
; Display the node data.
mov eax,(ListNode PTR [esi]).NodeData
call WriteDec
call Crlf
; Get pointer to next node.
mov esi,(ListNode PTR [esi]).NextPtr
jmp NextNode
quit1:
;//-------------------------------------------链表遍历完毕
call waitmsg
exit
main ENDP
CreateLinkList PROC
push ebp
mov ebp,esp
;//--------------------------------------------构建链表
push ecx
push edi
push eax
push esi
mov ecx,21
mov edi,offset LinkedList
mov esi,0
LW:
mov eax,dword ptr TheNum[esi]
mov (ListNode ptr [edi]).NodeData,eax
mov eax,edi
add eax,type ListNode
mov (ListNode ptr [edi]).NextPtr,eax
add edi,type ListNode
add esi,type TheNum
LOOP LW
push esi
push eax
push edi
push ecx
;//--------------------------------------------链表构建完毕
mov esp,ebp
pop ebp
ret
CreateLinkList ENDP
SortLinkList PROC
push ebp
mov ebp,esp
;//--------------------------------------------冒泡排序开始
;// 这里用到了 edi 头寻址 ecx 循环控制 ebx,edx,
push edi
push ecx
push eax
push ebx
push edx
push esi
mov edi,offset LinkedList
mov ecx,19
;//根据定理,这里的offset Linkedlist永远是指向一个固定位置的
maopao1:
push ecx
mov ecx,19
mov eax,edi
maopao2:
mov ebx,eax
add ebx,type ListNode
mov edx,ebx
add edx,type ListNode
mov esi,(ListNode PTR [ebx]).NodeData
cmp esi,(ListNode PTR [edx]).NodeData
jb allhavetodo
bigger:;//-----------------------------------进行对换
push eax
push esi
mov eax,(ListNode PTR [ebx]).NodeData
mov esi,(ListNode PTR [edx]).NodeData
mov (ListNode PTR [ebx]).NodeData,esi
mov (ListNode PTR [edx]).NodeData,eax
pop esi
pop eax
;//-----------------------------------对换结束
allhavetodo:
add eax,type ListNode
;//-----------------------------------上面的eax指向eax的下一个地址
loop maopao2
pop ecx
loop maopao1
pop esi
pop edx
pop ebx
pop eax
pop ecx
pop edi
mov esp,ebp
pop ebp
ret
;//--------------------------------------------冒泡排序结束
SortLinkList ENDP
CopyLinkList PROC
push ebp
mov ebp,esp
;//--------------------------------------------链表复制开始
;// edi为LinkedList首地址 ebx为LinkedList1首地址 eax为临时变量
push edi
push ebx
push eax
mov edi,offset LinkedList
mov ebx,offset LinkedList1
Copytwo:
mov eax,(ListNode PTR [edi]).NextPtr
cmp eax,NULL
je quit
mov eax,(ListNode ptr [edi]).NodeData
mov (ListNode ptr [ebx]).NodeData,eax
mov eax,ebx
add eax,type ListNode
mov (ListNode ptr [ebx]).NextPtr,eax
add edi,type ListNode
add ebx,type ListNode
jmp Copytwo
quit:
pop eax
pop ebx
pop edi
mov esp,ebp
pop ebp
ret
;//--------------------------------------------链表复制结束
CopyLinkList ENDP
END main
汇编也跟我们这里用到的是相同的函数
1. 构造函数
2. 排序函数
3. 复制函数
同样的,这里给出构造函数所使用的
CreateLinkList:
这里用到的有ecx,edi,eax,esi
Ecx用来做循环变量控制
Edi 用来进行传递链表首地址
Esi用来进行递增控制
SortLinkList:
这里用到的有ecx,eax,edi,ebx,edx,esi
其中:
Ecx用来进行循环变量控制
由于ecx 要用在两层变量循环控制里面,所以当进入另外一层的时候,我们必须把ecx压栈
然后在里层循环结束的时候ecx再出栈,这样子就能保证正常的二层循环了
Edi用来传递链表首地址
Eax用来临时传递链表首地址
Edx,ebx,esi都用来进行临时交换数据的空间
CopyLinkList:
这里用到的有edi,ebx,eax.
不用ecx的原因是因为我们的循环控制换成的比较来控制是否跳出循环
Edi 用来传递链表1的临时地址
Ebx用来传递链表2 的临时地址
Eax用来传递链表1的当前地址的下一个指针
关于汇编版本1的代码和版本2的代码的差别:
在说差别之前,我们先来说下汇编的链表跟C++的链表的具体差别
首先,汇编的链表在一开始我们就已经申请了N个地址的空间,这个空间是连续的,固定的,所以我们就能够知道链表的下一个地址是什么,并且通过数字可以直接寻址得到,不用花费太多心血.
但是C++的链表空间是动态分配的,动态分配有个好处,那么就是链表的长度可以无限长,所以汇编的链表可以说是静态链表,伪链表,真数组,而C++的链表就是名副其实的链表了
下面我们通过图示来证明这一点
这是我们汇编一开始申请的链表并且已经初始化完毕
这是我们C++一开始申请的链表并且已经初始化完毕
这是我们汇编代码1所进行的其中一个排序找错
这是汇编代码2的所进行的其中一种排序
大家仔细斟酌下就能发现其中的差别了.
下面,我们通过汇编形成了三个文件
通过比较可得,C++编译器生成的文件很小,我也不知道为什么突然一下子汇编的要达到3.6兆,这个留给高手解决吧.
那么接下来的还有的准备就是调整下电脑的功率模式:
在处理器最省电模式下:
文件名称
|
C++LINK.exe
|
Assembly_LINK1.exe
|
Assembly_LINK2.exe
|
所用时间
|
|
|
|
百分比
|
100
|
15.75%
|
15.44%
|
在最高性能下:
文件名称
|
C++LINK.exe
|
Assembly_LINK1.exe
|
Assembly_LINK2.exe
|
所用时间
|
|
|
|
百分比
|
100%
|
15.34%
|
14.65%
|
通过对比可得,程序确实是在完全计算之后才得出结论的,因为这里的CPU功率在高和低的状态下得出的时间是完全不一样的,高性能状态下,时间缩短了几近一倍,通过对比也可发现,汇编的速度已经快接近C++编译出来的十倍左右了,那么到了这里,也差不多已经接近尾声了,探讨结果已经出来,汇编显示出了让人折服的速度,这正是汇编所具有的优势,虽然我不知道为什么编译出来的文件体积如此之大,但是尝试后还是发现了,当我循环20000遍的体积跟循环10000是差了一倍左右的,这可能跟汇编编译器的工作原理有关,由于查不出答案,所以我的猜测应该是汇编里面的循环的每一遍都拷贝了一边,所以……答案是怎样的呢?可以下期探讨,如果我的出来的话.