Одна (и недорогая) операция для циклического перебора 3-значного перечисления C # (т.е. с переносом) - PullRequest
0 голосов
/ 21 июня 2019

У меня есть 3-элементное перечисление, оно определяет один из трех контекстов, например, красный, зеленый или синий.Это перечисление используется в цикле с миллионами итераций, например, много пикселей.Поля в настоящее время находятся на расстоянии int, по умолчанию.Учитывая желаемый производственный порядок R, G, B, R, G, B ..., я в настоящее время прибегаю к проверке, является ли значение в настоящее время B, таким образом присваивая его R, иначе увеличивая значение.

private enum CHANNEL_CONTEXT {RED, GREEN, BLUE} //here is a sample enum

//here is a sample loop with the relevant construct
CHANNEL_CONTEXT current = CHANNEL_CONTEXT.RED;
while(condition)
{
    use current;
    //...
    if(current == CHANNEL_CONTEXT.BLUE)
        current = CHANNEL_CONTEXT.RED
    else
        current+=1;

}

Есть ли способ обернуть перечисление из 3 полей одной операцией, так что не требуется ветвь, чтобы определить, настало ли время для переноса.Я знаю, что модуль (%) отвечает всем требованиям, но моя мотивация основывается на производительности, и я бы в лучшем случае даже обошелся с такой дорогостоящей операцией (тестирование подтвердилось, но не исчерпывающе).

Дляесли посмотреть на мою повестку дня, если бы у меня было 256 соответствующих полей, я мог бы создать перечисление на основе байтов и увеличивать его безнаказанно и планировалось переполнение.Увы, у меня есть только три, и я не могу придумать, как манипулировать любым интегральным примитивом таким образом, чтобы три значения создавались циклически с использованием облегченной операции ALU, (+, -, &, ^, |, << ..так далее).Я также не смог бы придумать способ обмена битами без временного использования таких операций, но есть редко практичный, но возможный способ сделать это.</p>

Может кто-нибудь подсказать мне, как распределить 3 целочисленных значения перечисления так, чтобы они периодически проходили через обходы без необходимости ветвления и не использовали операторы на основе деления (например, модуль)?

1 Ответ

2 голосов
/ 21 июня 2019

Хотя это маловероятно, что вы можете победить x = (x + 1) % 3, вы можете попробовать использовать таблицу сопоставления:

var map = new[]{1,2,0};
x = map[x];

Возможно, вам понадобится обернуть это в unsafe, чтобы удалить проверки границ при доступе к массиву.


Если вы действительно настроили манипулирование битами независимо от читабельности кода - интересующая вас таблица преобразования чисел достаточно мала, чтобы составлять вручную для каждого бита и затем объединять.

Таблица истинности:

Source     Result
Bit2 Bit1  Bit2 Bit1
0    0     0    1
0    1     1    0
1    0     0    0
1    1     x    x 

Как видите, интересующие нас значения выдают только 2 ненулевых бита, поэтому результирующее выражение будет очень простым - один случай для 1 для младшего бита и один случай для старшего бита (при условии, что значения никогда не выпадают из диапазон 0-2 (который является безопасным, если это единственное преобразование).

var b1 = (x & 1) >> 0; // extract lower bit  0
var b2 = (x & 2) >> 1; // extract higher bit 1
// only care of cases when pair of bits results in 1 
var resultBit1 =  1 & (~b1 & ~b2); // 00 -> x1, other cases is 0
var resultBit2 = (1 & (b1 & ~b2)) << 1;               // 01 -> 1x, other cases is 0
x = resultBit1 | resultBit2;

Или вставить все в одну нечитаемую строку:

x = 1 & ~(x | x >> 1) | 2 & (x & 1 & ~x >> 1) << 1;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...