Thuật toán hoán đổi giá trị XOR này vẫn còn được sử dụng hay hữu ích


25

Khi tôi mới bắt đầu làm việc, một lập trình viên biên dịch chương trình máy tính lớn đã chỉ cho tôi cách họ trao đổi thành các giá trị mà không cần sử dụng thuật toán truyền thống:

a = 0xBABE
b = 0xFADE

temp = a
a = b
b = temp

Những gì họ đã sử dụng để trao đổi hai giá trị - từ một bit sang một bộ đệm lớn - là:

a = 0xBABE
b = 0xFADE

a = a XOR b
b = b XOR a
a = a XOR b

hiện nay

b == 0xBABE
a == 0xFADE

trong đó hoán đổi nội dung của 2 đối tượng mà không cần không gian giữ tạm thời thứ ba.

Câu hỏi của tôi là: Thuật toán hoán đổi XOR này có còn được sử dụng không và nó vẫn được áp dụng ở đâu.


50
Nó vẫn được sử dụng rộng rãi để thể hiện và các câu hỏi phỏng vấn xấu; đó là về nó
Michael Borgwardt

4
Đây có phải là một cái gì đó giống như mẹo: a = a + b, b = ab, a = ab?
Pieter B

4
@PieterB vâng, cả hai đều là trường hợp của stackoverflow.com/questions/1826159/iêu
jk.

Vì vậy, ... Bạn lưu một đăng ký nhưng thanh toán với 3 hướng dẫn thêm. Tôi nghĩ rằng phiên bản tạm thời sẽ nhanh hơn.
Billy ONeal

1
Trao đổi giá trị @MSalters không phải là vấn đề trong x86 ngay từ đầu do hướng dẫn xchg. OTOH hoán đổi 2 vị trí bộ nhớ yêu cầu 3 xchss :)
Lấy

Câu trả lời:


41

Khi sử dụng, xorswapcó nguy cơ cung cấp cùng một biến là cả hai đối số cho hàm làm thay đổi biến đã nói do nó xortự biến nó thành tất cả các bit thành 0. Tất nhiên điều này tự nó sẽ dẫn đến hành vi không mong muốn bất kể thuật toán được sử dụng, nhưng hành vi có thể gây ngạc nhiên và không rõ ràng ngay từ cái nhìn đầu tiên.

Theo truyền thống xorswapđã được sử dụng để thực hiện cấp thấp để hoán đổi dữ liệu giữa các thanh ghi. Trong thực tế, có những lựa chọn thay thế tốt hơn cho việc hoán đổi các biến trong các thanh ghi. Ví dụ, x86 của Intel có một XCHGlệnh hoán đổi nội dung của hai thanh ghi. Nhiều lần trình biên dịch sẽ tìm ra ngữ nghĩa của một hàm như vậy (nó hoán đổi nội dung của các giá trị được truyền cho nó) và có thể tự tối ưu hóa nếu cần, vì vậy cố gắng tối ưu hóa một cái gì đó tầm thường như một hàm hoán đổi không thực sự mua cho bạn bất cứ thứ gì trong thực tế. Tốt nhất là sử dụng phương pháp rõ ràng trừ khi có một lý do đã được chứng minh tại sao nó lại kém hơn khi nói xorswap trong miền vấn đề.


18
Nếu cả hai giá trị là như nhau, thì bạn vẫn sẽ có kết quả chính xác. a: 0101 ^ 0101 = 0000; b: 0101 ^ 0000 = 0101; a: 0101 ^ 0000 = 0101;
Bobson

1
Những gì @ GlenH7 nói. -1-> +1.
Bobson

1
Dường như bạn vẫn có dòng về "Khi sử dụng xorswap, có nguy cơ cung cấp cùng một biến là cả hai đối số cho hàm biến ra biến đã nói do nó là xor'd với chính nó biến tất cả các bit thành 0". .. Điều đó không có nghĩa là đã được gỡ bỏ?
Chris

9
@Chris Không, lúc đầu tôi đã viết rằng nếu các giá trị giống hệt như nếu a=10, b=10và nếu bạn làm xorswap(a,b)điều đó sẽ hoạt động và không loại bỏ các biến đó là sai và hiện đã bị xóa. Nhưng nếu bạn đã làm xorswap(a, a)thì asẽ nhận được số 0 mà tôi có nghĩa là ban đầu nhưng thật ngu ngốc. :)
zxcdw

3
Nó khá phù hợp trong một số ngôn ngữ nhất định - những mối nguy hiểm nhỏ như thế gây khó khăn cho việc tìm lỗi trong tương lai khi xử lý hai con trỏ mà bằng cách nào đó đã được đặt để chỉ đến cùng một địa chỉ hơn 100 dòng mà bạn không thấy.
Drake Clarris

14

Chìa khóa cho câu trả lời nằm trong câu hỏi - "làm việc với một lập trình viên biên dịch chương trình máy tính lớn" - trong những ngày trước trình biên dịch. Khi con người thực hiện các hướng dẫn lắp ráp và chế tạo các giải pháp chính xác cho một phần cứng cụ thể (có thể hoặc không thể hoạt động trên một mẫu máy tính khác của cùng một máy tính - các vấn đề như thời gian của ổ cứng và bộ nhớ trống đã ảnh hưởng đến cách mã viết - đọc Câu chuyện của Mel nếu cảm thấy cần phải hoài cổ).

Quay trở lại những ngày đã qua, các thanh ghi và bộ nhớ đều khan hiếm và bất kỳ mẹo nào để không phải xin một byte hoặc từ bộ nhớ khác từ kiến ​​trúc sư trưởng đã được lưu thời gian - cả về cách viết mã và thời gian thực hiện.

Những ngày đó không còn nữa. Thủ thuật hoán đổi hai thứ mà không sử dụng một phần ba là một mẹo. Bộ nhớ và các thanh ghi đều dồi dào trong điện toán hiện đại và con người không viết lắp ráp nữa. Chúng tôi đã dạy tất cả các thủ thuật của chúng tôi cho trình biên dịch của chúng tôi và họ làm việc đó tốt hơn chúng tôi. Rất có thể trình biên dịch đã làm một cái gì đó thậm chí còn tốt hơn những gì chúng ta sẽ làm. Trong hầu hết các trường hợp, đôi khi chúng ta cần phải viết lắp ráp trong một số vòng bên trong của một vòng lặp chặt chẽ vì một số lý do ... nhưng nó không để lưu một thanh ghi hoặc một từ bộ nhớ.

Nó có thể hữu ích một lần nữa nếu một người đang làm việc trong một vi điều khiển đặc biệt hạn chế, nhưng tối ưu hóa một trao đổi không phải là nguồn gốc của vấn đề của một người sau đó - cố gắng quá thông minh có nhiều khả năng là một vấn đề.


2
Các thanh ghi không thực sự phong phú trong nhiều bối cảnh. Ngay cả khi bộ xử lý có nguồn cung cấp các thanh ghi dồi dào, mọi thanh ghi được sử dụng trong ISR là một thanh ghi phải được lưu trước đó và được khôi phục sau đó. Nếu ISR mất hai mươi chu kỳ và được cho là chạy cứ sau bốn mươi chu kỳ, thì mỗi chu kỳ bổ sung được thêm vào ISR sẽ làm giảm hiệu suất hệ thống là năm điểm phần trăm.
supercat

8

Nó sẽ làm việc chứ? Vâng.

Bạn có nên sử dụng nó? Không.

Loại tối ưu hóa vi mô này sẽ có ý nghĩa nếu:

  • bạn đã xem mã mà trình biên dịch tạo ra để thực hiện điều này một cách đơn giản (gán và tạm thời) và quyết định rằng phương pháp XOR tạo mã nhanh hơn

  • bạn đã định hình ứng dụng của mình và thấy chi phí của cách tiếp cận đơn giản vượt xa sự rõ ràng của mã (và dẫn đến tiết kiệm trong khả năng duy trì)

Đến điểm đầu tiên, trừ khi bạn thực hiện phép đo này, bạn nên tin tưởng vào trình biên dịch. Khi ngữ nghĩa của những gì bạn đang cố gắng làm rõ ràng, có rất nhiều thủ thuật mà trình biên dịch có thể làm, bao gồm sắp xếp lại quyền truy cập biến để không cần hoán đổi, hoặc thực hiện bất kỳ hướng dẫn cấp độ máy nào cung cấp nhanh nhất trao đổi cho một loại dữ liệu nhất định. "Thủ thuật" như trao đổi XOR khiến trình biên dịch khó nhìn thấy những gì bạn đang cố gắng thực hiện và do đó làm cho nó ít có khả năng áp dụng các tối ưu hóa như vậy.

Đến điểm thứ hai, bạn đạt được gì cho sự phức tạp thêm vào? Ngay cả khi bạn đã đo và thấy cách tiếp cận XOR nhanh hơn, liệu điều này có đủ tác động để biện minh cho cách tiếp cận ít rõ ràng hơn không? Làm sao bạn biết?

Cuối cùng, bạn nên xem xét liệu có chức năng trao đổi tiêu chuẩn cho nền tảng / ngôn ngữ của bạn hay không - ví dụ, C ++ STL, cung cấp chức năng hoán đổi mẫu sẽ có xu hướng được tối ưu hóa cao cho trình biên dịch / nền tảng của bạn.


2

Đồng nghiệp của tôi báo cáo rằng đây là một mẹo cơ bản mà anh ấy đã dạy trong trường đại học cho một lập trình viên của các hệ thống tự động. Nhiều hệ thống như vậy được nhúng với các tài nguyên hạn chế và chúng có thể thiếu đăng ký miễn phí để giữ giá trị tạm thời; trong trường hợp đó, việc trao đổi phức tạp như vậy (hoặc tương tự với việc cộng và trừ) đang trở nên quan trọng nên vẫn được sử dụng.

Nhưng người ta sẽ quan tâm đến việc sử dụng nó rằng hai vị trí này không thể giống nhau bởi vì trong trường hợp sau, nó sẽ thực sự bằng không cả hai giá trị. Vì vậy, thông thường nó bị giới hạn trong các trường hợp rõ ràng như trao đổi một thanh ghi và một vị trí bộ nhớ.

Với x86, các hướng dẫn xchg và cmpxchg đáp ứng nhu cầu trong hầu hết các trường hợp, nhưng RISC thường không được bảo hiểm với chúng (trừ Sparc).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.