2012-12-11 51 views
1

A C環路在一個循環中在這個循環中執行了多少條指令?

while(*from) 
    { 
    *to++ = *from++; 
    } 

我覺得我基本上想知道哪些MIPS指令*to++ = *from++;轉化爲。我的結果是指令是14:

$L2: 
    lw $2,12($fp) 
    lb $3,0($2) 
    bne $3,$0,$L4 
    j $L3 
$L4: 
    lw $2,8($fp) 
    addu $3,$fp,12 
    lw $4,0($3) 
    lbu $5,0($4) 
    sb $5,0($2) 
    addu $4,$4,1 
    sw $4,0($3) 
    addu $2,$2,1 
    sw $2,8($fp) 
    j $L2 

我來到這個結論從着眼於完整的C程序:

/* strcpy.c */ 

#include <stdio.h> 
#include <idt_entrypt.h> 

/* C stringcopy */ 

static void str_cpy(char *to, const char *from) 
{ 
    while(*from) 
    { 
    *to++ = *from++; 
    } 
    *to = '\0'; 
} 

int main() 
{ 
    static char* hello = "Hello World!"; 
    static char to[4711] = "blaha blaj blurk bletch"; 
    int Time; 

    printf("Strangen hello ser ut sa har: %s\n", hello); 
    flush_cache();   /* toem cache-minnet */ 
    timer_start();   /* nollstall tidmatning */ 

    str_cpy(to, hello); 

    Time = timer_stop();   /* las av tiden */ 
    printf("Time to copy: %d\n",Time); 
    printf("Och kopian sa har: %s\n", to); 
} 

它來編譯MIPS彙編證明這一點:

.file 1 "strcpy.c" 

# -G value = 8, Cpu = 3000, ISA = 1 
# GNU C version cygnus-2.7.2-970404 (mips-mips-ecoff) compiled by GNU C version cygnus-2.7.2-970404. 
# options passed: -msoft-float 
# options enabled: -fpeephole -ffunction-cse -fkeep-static-consts 
# -fpcc-struct-return -fcommon -fverbose-asm -fgnu-linker -msoft-float 
# -meb -mcpu=3000 

gcc2_compiled.: 
__gnu_compiled_c: 
    .text 
    .align 2 
    .ent str_cpy 
str_cpy: 
    .frame $fp,8,$31  # vars= 0, regs= 1/0, args= 0, extra= 0 
    .mask 0x40000000,-8 
    .fmask 0x00000000,0 
    subu $sp,$sp,8 
    sw $fp,0($sp) 
    move $fp,$sp 
    sw $4,8($fp) 
    sw $5,12($fp) 
$L2: 
    lw $2,12($fp) 
    lb $3,0($2) 
    bne $3,$0,$L4 
    j $L3 
$L4: 
    lw $2,8($fp) 
    addu $3,$fp,12 
    lw $4,0($3) 
    lbu $5,0($4) 
    sb $5,0($2) 
    addu $4,$4,1 
    sw $4,0($3) 
    addu $2,$2,1 
    sw $2,8($fp) 
    j $L2 
$L3: 
    lw $2,8($fp) 
    sb $0,0($2) 
$L1: 
    move $sp,$fp   # sp not trusted here 
    lw $fp,0($sp) 
    addu $sp,$sp,8 
    j $31 
    .end str_cpy 
    .rdata 
    .align 2 
$LC0: 
    .ascii "Hello World!\000" 
    .sdata 
    .align 2 
hello.4: 
    .word $LC0 
    .data 
    .align 2 
to.5: 
    .ascii "blaha blaj blurk bletch\000" 
    .space 4687 
    .rdata 
    .align 2 
$LC1: 
    .ascii "Strangen hello ser ut sa har: %s\n\000" 
    .align 2 
$LC2: 
    .ascii "Time to copy: %d\n\000" 
    .align 2 
$LC3: 
    .ascii "Och kopian sa har: %s\n\000" 
    .text 
    .align 2 
    .globl main 
    .ent main 
main: 
    .frame $fp,32,$31  # vars= 8, regs= 2/0, args= 16, extra= 0 
    .mask 0xc0000000,-4 
    .fmask 0x00000000,0 
    subu $sp,$sp,32 
    sw $31,28($sp) 
    sw $fp,24($sp) 
    move $fp,$sp 
    jal __main 
    la $4,$LC1 
    lw $5,hello.4 
    jal printf 
    jal flush_cache 
    jal timer_start 
    la $4,to.5 
    lw $5,hello.4 
    jal str_cpy 
    jal timer_stop 
    sw $2,16($fp) 
    la $4,$LC2 
    lw $5,16($fp) 
    jal printf 
    la $4,$LC3 
    la $5,to.5 
    jal printf 
$L5: 
    move $sp,$fp   # sp not trusted here 
    lw $31,28($sp) 
    lw $fp,24($sp) 
    addu $sp,$sp,32 
    j $31 
    .end main 

所以我分析了上面的內容,發現在while循環的一個循環中執行的指令數是14。我的推理是否正確?

+0

14似乎是正確的 – SomeWittyUsername

+0

MIPS版本的glibc中的strcpy()版本比這個天真的實現要快得多。它儘可能使用全字操作,而不是使用字節加載和存儲。如果strcpy()對性能至關重要,則應使用gcc爲您提供的版本。 – markgz

回答

2
$L2: 
lw $2,12($fp) ; 12($fp) is 'from' - load it in to $2 
lb $3,0($2) ; read a byte 
bne $3,$0,$L4 ; if it's non-zero, jump into the main loop 
j $L3   ; otherwise exit (this is the while clause) 
$L4: 
lw $2,8($fp) ; 8($fp) is 'to' - load it into $2 
addu $3,$fp,12 ; Load the address of 'from' into $3 
lw $4,0($3) ; Load 'from' into $4 
lbu $5,0($4) ; Read the byte again (this is the = *from) 
sb $5,0($2) ; Store the byte (*to =) 
addu $4,$4,1 ; increment from (from++) 
sw $4,0($3) ; store it back 
addu $2,$2,1 ; increment to (to++) 
sw $2,8($fp) ; store it back 
j $L2   ; do it all again 

因此,13循環中的操作,因爲j $ L3被跳過。但是,正如markgz指出的那樣,MIPS具有分支延遲插槽,可能需要編譯器或彙編器添加nops或切換指令。您應該查看最終代碼的反彙編,以及中間彙編程序輸出。

在這種情況下很可能在初始bne指令後至少會有一個額外的nop,但彙編器可能會重新排序最後的跳轉,而不是用nop填充。因此,如果你看最後的輸出結果,14條指令可能是最好的。

那裏有很多冗餘 - 一半的指令只是加載/存儲回局部變量,而不僅僅是將東西保存在寄存器中。這是調試/未優化版本的典型特徵。

+0

事實上,這是14個操作,因爲在$ 3,$ 0,$ L4'和'j $ L3'之間有一個彙編器生成NOP。跳轉不能合法地位於分支的分支延遲槽中。具有'-O1'的Gcc 4.6.3爲該循環生成7條指令。 – markgz

+0

(似乎默認行爲會填充前兩個分支,並重新排列最後一個分支 - 我相應地修改了我的答案)。 – JasonD

1

看起來正確。

似乎有很多多餘的負載&商店 - 是完全關閉優化?

1

其實在while循環的執行中只有13個操作(操作j $L3僅在while結束時執行)。

1

您在計算中包含測試和條件跳轉指令,這對我來說似乎並不合適。

你已經有了一個分支,代碼太多了。嘗試

while ((*to++ = *from++)); 

我的編譯器(gcc for x86)生成更好的代碼,只有一個條件跳轉。在該體系結構(這似乎擁有更好的尋址模式)是一個編譯爲

xorl %eax, %eax 
.L8: 
    movzbl (%rsi,%rax), %edx 
    movb %dl, (%rdi,%rax) 
    addq $1, %rax 
    testb %dl, %dl 
    jne .L8 
    ret 

所以這裏的內部只有三條指令加上一個初始化,因爲增量循環內只進行一次,而不是兩次。一般來說,當你問這樣的問題時,你必須小心謹慎,(*to++ = *from++)本身並沒有真正的成本,而只是嵌入到周圍的代碼中。