Mã máy x86-16 (BubbleSort int8_t), 20 19 byte
mã máy x86-64 / 32 (JumpDownSort) 21 19 byte
Thay đổi:
Cảm ơn @ ped7g cho lodsb
/ cmp [si],al
ý tưởng và đặt nó cùng với việc tăng / đặt lại con trỏ mà tôi đang xem xét. Không cần al
/ ah
cho phép chúng tôi sử dụng gần như cùng một mã cho các số nguyên lớn hơn.
Thuật toán mới (nhưng có liên quan), nhiều thay đổi triển khai: Bubbly SelectionSort cho phép triển khai x86-64 nhỏ hơn cho byte hoặc dwords; hòa vốn trên x86-16 (byte hoặc từ). Cũng tránh được lỗi về kích thước = 1 mà BubbleSort của tôi có. Xem bên dưới.
Nó chỉ ra rằng Sắp xếp lựa chọn Bubbly của tôi với các giao dịch hoán đổi mỗi khi bạn tìm thấy một min mới đã là một thuật toán được biết đến, JumpDown Sort. Nó được đề cập trong Bubble Sort: Phân tích thuật toán khảo cổ học (tức là cách Bubble Sort trở nên phổ biến mặc dù hút).
Sắp xếp các số nguyên có chữ ký 8 bit tại chỗ . (Chưa ký là cùng kích thước mã, chỉ cần thay đổi jge
thành a jae
). Bản sao không phải là một vấn đề. Chúng tôi trao đổi bằng cách sử dụng xoay 16 bit bằng 8 (với đích bộ nhớ).
Bubble Sort hút hiệu năng , nhưng tôi đã đọc rằng đó là một trong những cách nhỏ nhất để thực hiện trong mã máy. Điều này có vẻ đặc biệt đúng khi có các thủ thuật đặc biệt để hoán đổi các yếu tố liền kề. Đây là lợi thế duy nhất của nó, nhưng đôi khi (trong các hệ thống nhúng ngoài đời thực) đó là đủ lợi thế để sử dụng nó cho các danh sách rất ngắn.
Tôi đã bỏ qua việc chấm dứt sớm mà không có giao dịch hoán đổi . Tôi đã sử dụng vòng lặp BubbleSort "được tối ưu hóa" của Wikipedia để tránh nhìn vào các n − 1
mục cuối cùng khi chạy n
lần thứ-3, vì vậy bộ đếm vòng ngoài là giới hạn trên của vòng lặp bên trong.
Danh sách NASM ( nasm -l /dev/stdout
) hoặc nguồn đơn giản
2 address 16-bit bubblesort16_v2:
3 machine ;; inputs: pointer in ds:si, size in in cx
4 code ;; requires: DF=0 (cld)
5 bytes ;; clobbers: al, cx=0
6
7 00000000 49 dec cx ; cx = max valid index. (Inner loop stops 1 before cx, because it loads i and i+1).
8 .outer: ; do{
9 00000001 51 push cx ; cx = inner loop counter = i=max_unsorted_idx
10 .inner: ; do{
11 00000002 AC lodsb ; al = *p++
12 00000003 3804 cmp [si],al ; compare with *p (new one)
13 00000005 7D04 jge .noswap
14 00000007 C144FF08 rol word [si-1], 8 ; swap
15 .noswap:
16 0000000B E2F5 loop .inner ; } while(i < size);
17 0000000D 59 pop cx ; cx = outer loop counter
18 0000000E 29CE sub si,cx ; reset pointer to start of array
19 00000010 E2EF loop .outer ; } while(--size);
20 00000012 C3 ret
22 00000013 size = 0x13 = 19 bytes.
đẩy / bật cx
xung quanh vòng lặp bên trong có nghĩa là nó chạy với cx
= outs_cx xuống 0.
Lưu ý rằng đó rol r/m16, imm8
không phải là một lệnh 8086, nó đã được thêm vào sau (186 hoặc 286), nhưng đây không phải là mã 8086, chỉ là 16 bit x86. Nếu SSE4.1 phminposuw
có ích, tôi sẽ sử dụng nó.
Phiên bản 32 bit này (vẫn hoạt động trên số nguyên 8 bit nhưng với con trỏ / bộ đếm 32 bit) là 20 byte (bật tiền tố kích thước toán hạng rol word [esi-1], 8
)
Lỗi: size = 1 được coi là size = 65536, vì không có gì ngăn chúng ta vào bên ngoài do / while với cx = 0. (Bạn thường sử dụng jcxz
cho điều đó.) Nhưng may mắn thay, JumpDown Sort 19 byte là 19 byte và không có vấn đề đó.
Phiên bản gốc x86-16 20 byte (không có ý tưởng của Ped7g). Bỏ qua để tiết kiệm không gian, xem lịch sử chỉnh sửa cho nó với một mô tả.
Hiệu suất
Cửa hàng / tải lại chồng chéo một phần (trong xoay vòng đích-bộ nhớ) gây ra một gian hàng chuyển tiếp cửa hàng trên các CPU x86 hiện đại (ngoại trừ theo thứ tự Atom). Khi một giá trị cao đang sủi bọt lên trên, độ trễ thêm này là một phần của chuỗi phụ thuộc mang theo vòng lặp. Lưu trữ / tải lại hút ở vị trí đầu tiên (như độ trễ chuyển tiếp lưu trữ 5 chu kỳ trên Haswell), nhưng một gian hàng chuyển tiếp mang đến cho nó nhiều hơn 13 chu kỳ. Thực hiện ngoài trật tự sẽ gặp khó khăn trong việc che giấu điều này.
Xem thêm: Stack Overflow: sắp xếp bong bóng để sắp xếp chuỗi cho phiên bản này với cách triển khai tương tự, nhưng với việc ra sớm khi không cần hoán đổi. Nó sử dụng xchg al, ah
/ mov [si], ax
để hoán đổi, dài hơn 1 byte và gây ra tình trạng đăng ký một phần trên một số CPU. (Nhưng nó vẫn có thể tốt hơn xoay vòng bộ nhớ, cần tải lại giá trị). Nhận xét của tôi có một số gợi ý ...
x86-64 / x86-32 JumpDown Sắp xếp, 19 byte (sắp xếp int32_t)
Có thể gọi từ C bằng cách sử dụng quy ước gọi Hệ thống V x86-64 là
int bubblyselectionsort_int32(int dummy, int *array, int dummy, unsigned long size);
(return value = max (mảng [])).
Đây là https://en.wikipedia.org/wiki/Selection_sort , nhưng thay vì nhớ vị trí của phần tử min, hãy trao đổi ứng viên hiện tại vào mảng . Khi bạn đã tìm thấy min (unsort_region), hãy lưu nó vào cuối khu vực được sắp xếp, như Sắp xếp lựa chọn bình thường. Điều này phát triển các khu vực được sắp xếp theo một. (Trong mã, rsi
chỉ đến một điểm cuối của khu vực được sắp xếp; lodsd
tiến bộ và mov [rsi-4], eax
lưu trữ lại tối thiểu vào đó.)
Tên Jump Down Sort được sử dụng trong Bubble Sort: Phân tích thuật toán khảo cổ . Tôi đoán sắp xếp của tôi thực sự là một loại Jump Up, bởi vì các yếu tố cao nhảy lên, để lại phía dưới được sắp xếp, không phải là kết thúc.
Thiết kế trao đổi này dẫn đến phần chưa sắp xếp của mảng kết thúc theo thứ tự sắp xếp ngược, dẫn đến nhiều giao dịch hoán đổi sau này. (Bởi vì bạn bắt đầu với một ứng cử viên lớn, và tiếp tục nhìn thấy các ứng cử viên thấp hơn và thấp hơn, vì vậy bạn tiếp tục trao đổi.) Tôi gọi nó là "bong bóng" mặc dù nó di chuyển các yếu tố theo hướng khác. Cách nó di chuyển các phần tử cũng hơi giống một kiểu sắp xếp ngược. Để xem nó hoạt động, sử dụng GDB display (int[12])buf
, đặt điểm dừng trên loop
hướng dẫn bên trong và sử dụng c
(tiếp tục). Nhấn quay lại để lặp lại. (Lệnh "display" nhận được GDB để in toàn bộ trạng thái mảng mỗi khi chúng ta đạt điểm dừng).
xchg
với mem có một lock
tiền tố ngầm làm cho điều này thêm chậm. Có lẽ là về một thứ tự cường độ chậm hơn so với trao đổi tải / cửa hàng hiệu quả; xchg m,r
là một thông lượng trên 23c trên Skylake, nhưng tải / lưu trữ / Mov với một tmp reg để trao đổi hiệu quả (reg, mem) có thể thay đổi một yếu tố trên mỗi đồng hồ. Nó có thể là một tỷ lệ tồi tệ hơn trên CPU AMD khi loop
hướng dẫn nhanh và sẽ không làm tắc nghẽn vòng lặp bên trong, nhưng các lỗi nhánh sẽ vẫn là một nút cổ chai lớn vì các giao dịch hoán đổi là phổ biến (và trở nên phổ biến hơn khi vùng không được sắp xếp trở nên nhỏ hơn ).
2 Address ;; hybrib Bubble Selection sort
3 machine bubblyselectionsort_int32: ;; working, 19 bytes. Same size for int32 or int8
4 code ;; input: pointer in rsi, count in rcx
5 bytes ;; returns: eax = max
6
7 ;dec ecx ; we avoid this by doing edi=esi *before* lodsb, so we do redundant compares
8 ; This lets us (re)enter the inner loop even for 1 element remaining.
9 .outer:
10 ; rsi pointing at the element that will receive min([rsi]..[rsi+rcx])
11 00000000 56 push rsi
12 00000001 5F pop rdi
13 ;mov edi, esi ; rdi = min-search pointer
14 00000002 AD lodsd
16 00000003 51 push rcx ; rcx = inner counter
17 .inner: ; do {
18 ; rdi points at next element to check
19 ; eax = candidate min
20 00000004 AF scasd ; cmp eax, [rdi++]
21 00000005 7E03 jle .notmin
22 00000007 8747FC xchg [rdi-4], eax ; exchange with new min.
23 .notmin:
24 0000000A E2F8 loop .inner ; } while(--inner);
26 ; swap min-position with sorted position
27 ; eax = min. If it's not [rsi-4], then [rsi-4] was exchanged into the array somewhere
28 0000000C 8946FC mov [rsi-4], eax
29 0000000F 59 pop rcx ; rcx = outer loop counter = unsorted elements left
30 00000010 E2EE loop .outer ; } while(--unsorted);
32 00000012 C3 ret
34 00000013 13 .size: db $ - bubblyselectionsort_int32
0x13 = 19 bytes long
Mã kích thước tương tự cho int8_t
: sử dụng lodsb
/ scasb
, AL
và thay đổi các [rsi/rdi-4]
đến -1
. Mã máy tương tự hoạt động ở chế độ 32 bit cho các phần tử 8/32 bit. Chế độ 16 bit cho các phần tử 8/16 bit cần được xây dựng lại với các độ lệch được thay đổi (và chế độ địa chỉ 16 bit sử dụng mã hóa khác nhau). Nhưng vẫn còn 19 byte cho tất cả.
Nó tránh được sự khởi đầu dec ecx
bằng cách so sánh với phần tử mà nó vừa tải trước khi tiếp tục. Ở lần lặp cuối cùng của vòng lặp bên ngoài, nó tải phần tử cuối cùng, kiểm tra xem nó có nhỏ hơn chính nó không, sau đó đã xong. Điều này cho phép nó hoạt động với size = 1, trong đó BubbleSort của tôi không thành công (coi nó là size = 65536).
Tôi đã thử nghiệm phiên bản này (bằng GDB) bằng cách sử dụng trình gọi này: Dùng thử trực tuyến! . Bạn có thể chạy nó trên TIO, nhưng tất nhiên không có trình gỡ lỗi hoặc in. Tuy nhiên, _start
lệnh gọi nó thoát với exit-status = lớn nhất = 99, vì vậy bạn có thể thấy nó hoạt động.
[7 2 4 1] -> [4 2 3 1]
. Ngoài ra, danh sách CSV có thể nằm trong dấu ngoặc không? Ngoài ra, định dạng đầu vào cụ thể rất phù hợp với một số ngôn ngữ và xấu cho những ngôn ngữ khác. Điều này làm cho phân tích cú pháp đầu vào là một phần lớn cho một số bài nộp và không cần thiết cho những người khác.