La forma más sencilla de alternar una variable entera entre dos valores

Tengo una variable a que solo puede tener dos valores x1 o x2 . Cómo alternar entre estos valores. Se me ocurrió esto. ¿Alguna otra forma más eficiente?

 a = (a == x1 ? x2: x1); 

Es (muy) poco probable que sea su cuello de botella, pero podría usar el método XOR :

 togglex1x2 = (x1 ^ x2); // This is the combined toggle value a = x1; // Initialise to either x1 or x2 a ^= togglex1x2; // toggles a ^= togglex1x2; // toggles ... 

[Primero debe escribir un código que sea comprensible, y optimizar solo cuando haya medido un cuello de botella (¡y luego verificar dos veces que es donde cree que está!), Y si optimiza, asegúrese de comentar con razonamiento. ]

Intenta algo como esto. una voluntad cambiará entre x1 y x2

 a = (x1 + x2) - a; 

Otra forma es alternar un índice entre 0 y 1 e indexar una matriz con eso:

 int main() { int const values[] = {0x55, 0xaa}; int selector = 0; selector ^= 1; // toggle index int value = values[selector]; // select value } 

Es muy difícil predecir qué método es mejor sin un contexto (la mayor incógnita es de qué modo es crítica esta operación) si está vinculada a la latencia (para .eg si está haciendo un cálculo largo con dependencias de datos que pasan por este código) , o tal vez crítico para el ancho de banda (está intercambiando muchos elementos no relacionados y comienza a agotar sus recursos).

Trató de comparar las soluciones propuestas aquí. ver este código para, por ejemplo:

 int main() { int x1 = 123, x2 = 456; int x1_xor_x2 = x1 ^ x2; int a = x1; int i; for (i = 0; i < 10000; ++i) a = (a == x1 ? x2: x1); for (i = 0; i < 10000; ++i) a ^= x1_xor_x2; printf ("a=%d\n", a); // prevent all this from being optimized out } 

se convierte en (gcc, con -O3):

 0000000000400440 
: 400440: b8 10 27 00 00 mov $0x2710,%eax // loop counter 400445: ba c8 01 00 00 mov $0x1c8,%edx 40044a: be 7b 00 00 00 mov $0x7b,%esi // 123 in esi 40044f: b9 c8 01 00 00 mov $0x1c8,%ecx // 456 in ecx 400454: eb 12 jmp 400468 400456: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 40045d: 00 00 00 400460: 83 fa 7b cmp $0x7b,%edx 400463: 89 ca mov %ecx,%edx 400465: 0f 45 d6 cmovne %esi,%edx // conditional move 400468: 83 e8 01 sub $0x1,%eax 40046b: 75 f3 jne 400460 40046d: b8 10 27 00 00 mov $0x2710,%eax 400472: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) 400478: 81 f2 b3 01 00 00 xor $0x1b3,%edx 40047e: 83 e8 01 sub $0x1,%eax // xoring 400481: 75 f5 jne 400478 400483: be 6c 06 40 00 mov $0x40066c,%esi 400488: bf 01 00 00 00 mov $0x1,%edi 40048d: 31 c0 xor %eax,%eax 40048f: e9 9c ff ff ff jmpq 400430 <__printf_chk@plt>

Después de agregar controles de tiempo (y boost el conteo de bucles a 100M), me meto en mi servidor (AMD Opteron 6272):

 first: 0.089000s second: 0.067000s a=123 

Sin embargo, esto no es muy interesante, ya que no hay un consumidor que requiera datos de baja latencia (por lo que los cálculos pueden amortiguarse y estamos verificando ALU BW, no latencia)

Intentar agregar sum += a en cada iteración resultó en un incremento delta a favor de la primera:

 first: 0.106000s second: 0.066000s 

¡Pero! ya que un simple complemento no consume mucho tiempo por sí mismo, se intentó usar el recíproco (sum flotante y += 1/a ); este realmente necesitaría los datos rápidamente:

 first: 0.014000s second: 0.087000s 

Finalmente, inversión 🙂

Esto demuestra que puede tener diferentes resultados de rendimiento según la forma en que se utilice una operación determinada en su progtwig. No tiene mucho sentido hacer una evaluación comparativa de un solo método sin el rest del código (no es que no lo hagamos, es solo que necesita obtener cualquier resultado con un trozo de sal).

Por supuesto, esto es todo por el bien de la discusión, lo más probable es que este bit de código no sea ni remotamente un cuello de botella.

Así que lo comparaste y este es el cuello de botella, ¿verdad?

Oh, bueno, no … entonces simplemente olvídate de la eficiencia. Esta es ya una expresión muy pequeña que se evalúa rápidamente.

Por cierto, existen otros métodos, pero no estoy seguro de que 1. sean realmente más rápidos, 2. si son más rápidos, realmente cuentan, 3. si son más lentos, la pena de legibilidad es una compensación que vale la pena.

Por ejemplo:

 #define FIRST 42 #define SECOND 1337 /* initialize */ int x = FIRST; /* toggle */ x = FIRST + SECOND - x;