Цей алгоритм заміни значення XOR все ще використовується чи корисний


25

Коли я вперше почав працювати з асемблера мейнфрейму, програміст показав мені, як вони підміняються значеннями без використання традиційного алгоритму:

a = 0xBABE
b = 0xFADE

temp = a
a = b
b = temp

Що вони використовували для обміну двома значеннями - від біта до великого буфера - було:

a = 0xBABE
b = 0xFADE

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

зараз

b == 0xBABE
a == 0xFADE

який поміняв вміст 2-х об'єктів, не потребуючи третього місця для зберігання температури.

Моє запитання: чи цей алгоритм заміни XOR все ще використовується і де він досі застосовується.


50
Він все ще широко використовується для демонстрації та поганих запитань щодо інтерв'ю; ось про це.
Майкл Боргвардт

4
Це щось на кшталт хитрості: a = a + b, b = ab, a = ab?
Пітер Б

4
@PieterB так, це обидва випадки цього stackoverflow.com/questions/1826159/…
jk.

Отже ... Ви зберігаєте реєстр, але оплачуєте ще 3 інструкції. Я думаю, що темп-версія все одно буде швидшою.
Біллі ONeal

1
Зміна значень @MSalters не була проблемою у x86 з самого її початку через інструкцію xchg. OTOH для заміни 2-х локальних пам'яток потребує 3 xchgs :)
Netch

Відповіді:


41

При використанні xorswapіснує небезпека подати ту саму змінну, що і обидва аргументи до функції, яка виводить xorна нуль зазначену змінну через те, що вона буде d з собою, яка повертає всі біти до нуля. Звичайно, саме по собі це призведе до небажаної поведінки незалежно від використовуваного алгоритму, але така поведінка може бути дивовижною і не очевидною на перший погляд.

Традиційно xorswapвикористовується для низькорівневих реалізацій для обміну даними між регістрами. На практиці існують кращі альтернативи для заміни змінних у регістрах. Наприклад, x86 Intel має XCHGінструкцію, яка обмінює вміст двох регістрів. Багато разів компілятор з'ясовує семантику такої функції (вона заміняє вміст переданих їй значень) і може робити власні оптимізації при необхідності, тому намагаючись оптимізувати щось настільки тривіальне, як функція swap, насправді нічого не купує на практиці. Найкраще використовувати очевидний метод, якщо немає доведеної причини, чому було б неповноцінним сказати xorswap в проблемній області.


18
Якщо обидва значення однакові, ви все одно отримаєте правильний результат. a: 0101 ^ 0101 = 0000; b: 0101 ^ 0000 = 0101; a: 0101 ^ 0000 = 0101;
Бобсон

1
Що сказав @ GlenH7 -1-> +1.
Бобсон

1
У вас все ще є рядок про "При використанні xorswap існує небезпека подати ту саму змінну, що і обидва аргументи функції, яка виводить нуль згаданої змінної через те, що вона xor'd сама з собою, що повертає всі біти до нуля." .. Це не означало, що його видаляли?
Кріс

9
@Chris Ні, спершу я писав, що якщо значення однакові, як якщо б a=10, b=10, і якщо ви зробили xorswap(a,b)це, то це працювало б, а не нульове значення змінних, які є помилковими і тепер видаляються. Але якщо ви зробили , xorswap(a, a)то aотримали б обнуляється , який я спочатку мав в виду , але був дурний. :)
zxcdw

3
Це досить актуально в певних мовах - невеликі такі маленькі небезпеки, що ускладнюють пошук помилок у майбутньому при роботі з двома вказівниками, які так чи інакше були встановлені так, щоб вони вказували на одну і ту ж адресу 100+ ліній вгору, які ви не бачите.
Дрейк Кларріс

14

Ключ до відповіді полягає у питанні - "працюючий програміст з монтажу мейнфреймів" - за дні до компілятора. Коли люди зациклювались на інструкціях зі складання та вручну склали точні рішення для певного обладнання (що може працювати, а може і не працювати на іншій моделі того ж комп’ютера - такі питання, як терміни встановлення жорстких дисків та барабанної пам’яті, вплинули на те, яким чином був код написано - прочитайте історію Мела, якщо відчуваєте потребу бути ностальгічним).

Ще в ці минулі дні регістри та пам'ять були дефіцитними, і будь-який трюк, щоб не просити іншого байта чи слова пам’яті від головного архітектора, був збережений час - як при написанні коду, так і в часі виконання.

Ті дні минули. Трюк заміни двох речей без використання третьої - це хитрість. У сучасних обчисленнях пам'яті та регістрів є безліч, і люди вже не пишуть збірку. Ми навчили всіх наших хитрощів у наших компіляторів, і вони роблять це краще, ніж ми. Можливо, компілятор зробив щось навіть краще, ніж те, що ми зробили. У більшості випадків іноді нам потрібно записати збірку в якийсь внутрішній біт щільної петлі з якоїсь причини ... але це не для збереження регістра або слова пам'яті.

Це може бути корисним ще раз, якщо хтось працює в особливо обмеженому мікроконтролері, але оптимізація swap, швидше за все, не є джерелом вашої проблеми - спроба бути занадто розумною, швидше за все, проблема.


2
Реєстри насправді не такі багаті в багатьох контекстах. Навіть якщо процесор має достатньо запасів регістрів, кожен регістр, який використовується в ISR, є реєстром, який необхідно заздалегідь зберегти та відновити після цього. Якщо ISR займає двадцять циклів і передбачається виконувати кожні сорок циклів, кожен додатковий цикл, доданий до ISR, погіршить продуктивність системи на п'ять відсоткових пунктів.
supercat

8

Чи буде це працювати? Так.

Чи варто використовувати його? Ні.

Така мікрооптимізація має сенс, якщо:

  • ви подивилися на код, який створює компілятор для прямого способу цього (призначення та тимчасовий) і вирішили, що підхід XOR генерує швидший код

  • Ви профайлювали свою програму і виявили, що вартість прямого підходу переважає ясність коду (і, отже, заощадження на ремонтопридатності)

До першого моменту, якщо ви не зробили це вимірювання, вам слід довіряти компілятору. Коли семантика того, що ви намагаєтеся зробити, зрозуміла, компілятор може зробити чимало хитрощів, включаючи перестановку змінного доступу, щоб заміни взагалі не потрібні, або вбудовування будь-яких інструкцій на рівні машини забезпечить найшвидший swap для заданого типу даних. "Прийоми", такі як swap XOR, ускладнюють компілятору бачити, що ви намагаєтеся зробити, і, таким чином, робите менше можливостей застосовувати такі оптимізації.

До другого моменту, що ви отримуєте для додаткової складності? Навіть якщо ви швидше виміряли та знайшли підхід XOR, чи має це достатній вплив, щоб виправдати менш чіткий підхід? Звідки ти знаєш?

Нарешті, слід вивчити, чи існує стандартна функція swap для вашої платформи / мови - наприклад, STL C ++ забезпечує функцію заміни шаблонів, яка буде оптимізована для вашого компілятора / платформи.


2

Мій колега повідомив, що це базовий трюк, який він навчав в університеті, вивчаючи програміста автоматизованих систем. У багатьох таких системах є вбудовані системи з обмеженими ресурсами, і їм може не вистачати безкоштовного реєстру для збереження тимчасового значення; у такому випадку такий складний обмін (або його аналог із додаванням та відніманням) стає життєво важливим, тому його все ще використовують.

Але потрібно дбати про те, щоб ці два місця не були однаковими, оскільки в останньому випадку вони фактично нулюють обидва значення. Тому зазвичай це обмежується явними випадками, такими як обмін регістрами та місцем пам'яті.

Що стосується x86, інструкції xchg та cmpxchg задовольняють потребу в більшості випадків, але RISC, як правило, не покриваються ними (крім Sparc).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.