【问题标题】:Why does this program take exponentially longer with sizes of greater than 50?为什么这个程序在大小超过 50 的情况下花费的时间呈指数增长?
【发布时间】:2015-11-18 01:49:19
【问题描述】:

所以我正在为类编写一个 ARM 程序集快速排序方法。除了复杂性没有意义之外,我大部分都理解了。

我们将它与我们制作的另一种冒泡排序方法进行比较,它在具有 1 个参数和 10 个参数的示例中表现更好。但是我什至无法比较 100 参数测试,因为它花费的时间太长......我什至无法让它做 75,但 50 在几秒钟内完成。

这就是我所拥有的,

qsort:  @ Takes three parameters:
    @   a:     Pointer to base of array a to be sorted (arrives in r0)
    @   n:  number of elements in the array (arrives in r1)

    stmfd   sp!, {r4, r6, lr}     @ Save r4 and r6 for caller
    mov     r6, r1                @ r6 <- right
    mov r2, #0                    @ r2 <- left
qsort_tailcall_entry:
    sub     r7, r6, r2            @ If right - left <= 1 (already sorted),
    cmp     r7, #1
    ldmlefd sp!, {r4, r6, pc}     @ Return, restoring r4 and r6
    ldr     r7, [r0, r2, asl #2]  @ r7 <- a[left], gets pivot element
    add     r1, r2, #1            @ l <- left + 1
    mov     r4, r6                @ r <- right
partition_loop:
    ldr     r3, [r0, r1, asl #2]  @ r3 <- a[l]
    cmp     r3, r7                @ If a[l] <= pivot_element,
    addle   r1, r1, #1            @ ... increment l, and
    ble     partition_test        @ ... continue to next iteration.
    sub     r4, r4, #1            @ Otherwise, decrement r,
    ldr     r8, [r0, r4, asl #2]  @ ... and swap a[l] and a[r].
    str     r8, [r0, r1, asl #2]
    str     r3, [r0, r4, asl #2]
partition_test:
    cmp     r1, r4                @ If l < r,
    blt     partition_loop        @ ... continue iterating.
partition_finish:
    sub     r1, r1, #1            @ Decrement l
    ldr     r3, [r0, r1, asl #2]  @ Swap a[l] and pivot
    str     r3, [r0, r2, asl #2]
    str     r7, [r0, r1, asl #2]
    bl      qsort                 @ Call self recursively on left part,
                                  @  with args a (r0), left (r2), r (r2),
                                  @  also preserves r4 and r6
    mov     r2, r4
    b       qsort_tailcall_entry  @ Tail-call self on right part,
                                  @  with args a (r0), l (r2), right (r6)

谁能帮助我理解为什么它需要这么长时间并且呈指数级增长?我能做些什么来解决它?

【问题讨论】:

  • 至少它的评论很好,我们在这里看不到太多(+1)......你如何为一个小数组(如 3 个项目)单步执行它,看看它是否有效预期的事情?
  • 你在什么上面运行它,它有多少可用内存? “递归算法对足够大的数据很有趣”通常意味着你已经搞砸了。
  • 会的。我对这种设置方式的灵感来自许多在线资源,因此我试图跟踪我在 cmets 中的位置。编辑:是的,我在 rasp pi 上运行它,所以这可能是问题所在。
  • 来吧,100个元素? RPi 应该绰绰有余...
  • 对我来说仍然是清晨,但是,使用快速排序最糟糕的事情是选择第一个元素作为枢轴并尝试对已经排序的数组进行排序。为了进行性能比较,我还会编写一个 C 版本,看看它的性能如何,看看问题是否出在 arm 实现中,看起来并不明显。

标签: assembly arm


【解决方案1】:

我没有研究过你的逻辑,但在你的程序中添加了一些调试脚本,可能会对你有所帮助。包装器是一个独立的程序。

/* qsort1.s */

@ ---- Added ----
.data

tstdat:
    .word 9, 9, 9, 9, 9

array:
    .word 3, 7, 8, 5, 2, 1, 9, 5, 4
len:
    .word ((len - array) /4)

.balign 4
format:
    .asciz " %2d  %2d  %2d  %2d  %2d  %2d  %2d  %2d  %2d  |  %2d  %2d  %2d\n"


.text

.global main

Print:
    push  {r0-v7, lr}
    mrs   v7,  cpsr
    push  {v7, v8}
    push  {r2, r6, r7}
    ldr   r7,  =array
    ldm   r7,  {r1-r9}
    push  {r4-r9}
    ldr   r0,  =format
    bl    printf
    add   sp,  #36
    pop   {v7, v8}
    msr   cpsr_f, v7
    pop   {r0-v7, pc}

main:

    ldr     r0,  =array
    ldr     r1,  =len
    ldr     r1,  [r1]

    push    {r3-r11, lr}
    bl      Print
    bl      qsort
    bl      Print
    pop     {r3-r11, pc}

@ ---------------------------

qsort:  @ Takes three parameters:
    @   a:     Pointer to base of array a to be sorted (arrives in r0)
    @   n:  number of elements in the array (arrives in r1)

    stmfd   sp!, {r4, r6, lr}     @ Save r4 and r6 for caller
    mov     r6, r1                @ r6 <- right
    mov     r2, #0                @ r2 <- left
qsort_tailcall_entry:
    sub     r7, r6, r2            @ If right - left <= 1 (already sorted),
@   bl      Print                @ <---- Added
    cmp     r7, #1
    ldmlefd sp!, {r4, r6, pc}     @ Return, restoring r4 and r6
    ldr     r7, [r0, r2, asl #2]  @ r7 <- a[left], gets pivot element
    add     r1, r2, #1            @ l <- left + 1
    mov     r4, r6                @ r <- right
partition_loop:
    ldr     r3, [r0, r1, asl #2]  @ r3 <- a[l]
    cmp     r3, r7                @ If a[l] <= pivot_element,
    addle   r1, r1, #1            @ ... increment l, and
    ble     partition_test        @ ... continue to next iteration.
    sub     r4, r4, #1            @ Otherwise, decrement r,
    ldr     r8, [r0, r4, asl #2]  @ ... and swap a[l] and a[r].
    str     r8, [r0, r1, asl #2]
    str     r3, [r0, r4, asl #2]
    bl      Print                @ <---- Added
partition_test:
    cmp     r1, r4                @ If l < r,
    blt     partition_loop        @ ... continue iterating.
partition_finish:
    sub     r1, r1, #1            @ Decrement l
    ldr     r3, [r0, r1, asl #2]  @ Swap a[l] and pivot
    str     r3, [r0, r2, asl #2]
    str     r7, [r0, r1, asl #2]
    bl      qsort                 @ Call self recursively on left part,
                                  @  with args a (r0), left (r2), r (r2),
                                  @  also preserves r4 and r6
    mov     r2, r4
    b       qsort_tailcall_entry  @ Tail-call self on right part,
                                  @  with args a (r0), l (r2), right (r6)

这里是输出的开始。左边是要排序的数组,右边是r2、r6和r7。这个数组来自https://en.wikipedia.org/wiki/Quicksort (https://upload.wikimedia.org/wikipedia/commons/a/af/Quicksort-diagram.svg)。

pi@RPi:~/pgm $ mym qsort1
as -o qsort1.o qsort1.s
gcc -o qsort1 qsort1.o

./qsort1; echo $?
  3   7   8   5   2   1   9   5   4  |  2126935836  66296   0 (before pgm)
  3   4   8   5   2   1   9   5   7  |   0   9   3
  3   5   8   5   2   1   9   4   7  |   0   9   3
  3   9   8   5   2   1   5   4   7  |   0   9   3
  3   1   8   5   2   9   5   4   7  |   0   9   3
  3   1   2   5   8   9   5   4   7  |   0   9   3    
 ...
 (  array to be sorted             )    r2   r4  r7

【讨论】:

  • 如果您使用 gcc 链接,则无需“手动”组装。只需gcc -o qsort1 qsort1.S。 (即使您不需要 C 预处理器,也可以将 .S 用于您的手写 asm 源代码,因为 .s 可能会在编译器和反汇编程序没有警告的情况下被破坏。)
  • 如果您解释要打印的列是什么,而不是期望人们阅读您未注释的代码,这将是一个更好的答案。
  • @Peter Cordes。谢谢你的建议。我对我组装和链接程序的方式感到满意。
【解决方案2】:

要修复快速排序导致超长的问题,解决方法是使用“Linux qsort”对数字进行排序。

@ http://www.tutorialspoint.com/c_standard_library/c_function_qsort.htm
@
@ http://man7.org/linux/man-pages/man3/qsort.3.html
@
@ void qsort(void *base, size_t nmemb, size_t size,
@            int (*compar)(const void *, const void *));
@
@      qsort(r0 =array, r1 =numelements, r2 = 4 bytes,
@            r0=(r3 =cmpfunc){ r0=[r0] - r1=[r1] })

cmpfunc:
    ldr   r0,  [r0]
    ldr   r1,  [r1]
    sub   r0,  r0, r1
    mov   pc,  lr

qsort_setup:
    ldr   r0,  =array
    ldr   r1,  =numbytes
    lsr   r1,  #2
    mov   r2,  #4
    ldr   r3,  =cmpfunc

    bl    qsort

使用 urandom 数的完整演示程序。

/* qsort14.s */

.data

@ See /usr/include/arm-linux-gnueabihf/asm/unistd.h
@ See /usr/include/arm-linux-gnueabihf/bits/fcntl-linux.h

    .equ create,     8
         .equ Mode, 0644       @ -rw-r--r--
    .equ open,       5
         .equ Rd,   00
         .equ Wr,   01
         .equ RdWr, 02
         .equ Apnd, 02000
    .equ read,       3
    .equ write,      4
    .equ close,      6
    .equ sync,       36
    .equ exit,       1
    .equ sfile,      187

    .equ numbytes,  (64 * 4)     @ Has to be multiples
@   .equ numbytes,  (128 * 4)    @  of 8 times 4.
@   .equ numbytes,  (512 * 4) 

.balign 4
array: .skip numbytes

.balign 4
dir_file:
    .asciz "/dev/urandom"

.balign 4
Open:
    .word dir_file, RdWr | Apnd, open

.balign 4
Read:
    .word array, numbytes, read

.balign 4
hex3ff:
    .word 0x3ff

.balign 4
format:
    .asciz " %4u  %4u  %4u  %4u  %4u  %4u  %4u  %4u\n"

.balign 4
fmt:
    .asciz "\n"

@ ---------------------------

.text

.global main

Prt_arr:
    push  {r0-r12, lr}
    mrs   r12,  cpsr
    push  {r11, r12}
    mov   r0,  #0xa
    bl    putchar
    ldr   r12, =numbytes
    lsr   r12, #5
    mov   r11, #0
endprt:
    ldr   r0,  =array
    add   r0,  r11
    ldm   r0,  {r1-r8}
    push  {r4-r12}
    ldr   r0,  =format
    bl    printf
    pop   {r4-r12}
    add   r11, #32
    subs  r12, #1
    bne   endprt
    pop   {r11, r12}
    msr   cpsr_f, r12
    pop   {r0-r12, lr}
    mov   pc, lr

@ ---- Program starts here ----
@ ---- Read random numbers ----

main:
    push  {r4-r12, lr}

    ldr   r3, =Open            @ load address
    ldm   r3, {r0, r1, r7}     @ load registers
    svc   #0                   @ OS opens urandom file
    mov   r4, r0               @ save fd in r4

    ldr   r3, =Read            @ load address
    ldm   r3, {r1, r2, r7}     @ load registers
    svc   #0                   @ OS reads urandom file

    mov   r0, r4               @ move fd in r0
    mov   r7, #close           @ num for close
    svc   #0                   @ OS closes urandom file

@ ---- Fix array so numbers are 999 or less ----

    ldr   r10, =array
    ldr   r1,  =numbytes
    sub   r1,  #4
    ldr   r2,  =hex3ff
    ldr   r2,  [r2]
fix:
    ldr   r0,  [r10, r1]
    and   r0,  r2
    cmp   r0,  #1000
    subge r0,  #1000
    lslge r0,  r0, #5
    str   r0,  [r10, r1]
    subs  r1,  #4
    bpl   fix

@ ---- Print unsorted array ----

    bl    Prt_arr
    b     qsort_setup

@ http://www.tutorialspoint.com/c_standard_library/c_function_qsort.htm
@
@ http://man7.org/linux/man-pages/man3/qsort.3.html
@
@ void qsort(void *base, size_t nmemb, size_t size,
@            int (*compar)(const void *, const void *));
@
@      qsort(r0 =array, r1 =numelements, r2 = 4 bytes,
@            r0=(r3 =cmpfunc){ r0=[r0] - r1=[r1] })

cmpfunc:
    ldr   r0,  [r0]
    ldr   r1,  [r1]
    sub   r0,  r0, r1
    mov   pc,  lr

qsort_setup:
    ldr   r0,  =array
    ldr   r1,  =numbytes
    lsr   r1,  #2
    mov   r2,  #4
    ldr   r3,  =cmpfunc

    bl    qsort

End:
    bl    Prt_arr
    mov   r0,  #0xa
    bl    putchar

    pop    {r4-r12, lr}
    bx     lr

.end

【讨论】:

    猜你喜欢
    • 2020-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-24
    • 2020-01-05
    • 1970-01-01
    • 2022-10-19
    • 1970-01-01
    相关资源
    最近更新 更多