2009-08-08 14 views
10

La mayor parte de las constantes que veo en el código de otros son potencias de 2, es decir¿Por qué siempre declaramos que las constantes son potencias de 2?

#define SIZE 256 

o

public static final int SIZE = 2048; 

¿Hay alguna razón en particular por lo que hacemos que en lugar de decir

#define SIZE 257 

?

+2

No creo que se pueda responder a esa pregunta sin saber para qué se usa la constante. El propósito de la constante es qué define qué valores puede asumir la constante. –

+0

Un ejemplo podría ser el tamaño de una matriz de entradas. –

+1

Creo que estás pensando que usamos poderes de 2 más de los que realmente se usan. Las personas usarán una matriz de 100 ints igual que una matriz de 256. El enmascaramiento de bits es la razón principal de las potencias de 2 y no se aplica a los tamaños de las matrices. –

Respuesta

29

Los poderes de 2 son convenientes porque se adaptan bien a las limitaciones subyacentes del hardware.

Tales como:

  1. Tamaño de página
  2. límites de espacio
  3. Dirección
  4. constaints alineación (DWORD o de alineación de QWORD general, ambas potencias de 2)

para banderas, potencias de 2 siempre tenga un solo bit configurado. Entonces cosas como MY_FLAG_1 | MY_FLAG_2 | MY_FLAG_3 | ... solo funcionan con potencias de dos. Del mismo modo para la prueba de banderas con &.

También es una especie de convención elegir la mayor potencia más cercana de dos para tamaños de búfer y similares.

-2

Porque la memoria del equipo funciona con 0/1 que es un sistema binario.

+0

¿Y qué diferencia hay si hago una matriz de 256 caracteres en lugar de 100 caracteres? –

+0

Mi respuesta se refiere a la forma en que el procesador accede a la memoria, es más fácil cuando es 2^n – programmernovice

18

Una buena razón es bitmasking. Como ejemplo, si usa constantes para representar atributos de un objeto (o cualquier otra cosa), puede guardar muchos atributos en un solo entero mediante el enmascaramiento de bits e identificar los atributos individuales más adelante. Ideal para guardar muchas "banderas" en una base de datos.

4

La memoria se asigna normalmente en múltiplos de tamaños de página desde el sistema operativo, y en muchos casos, es útil organizar las cosas para ajustarse exactamente a las páginas (para no perder memoria). Depende de la rutina de asignación específica si esto realmente ayuda; p.ej. si hay un encabezado implícito, tener el tamaño de una potencia de dos realmente puede doler.

1

No hay muchas razones realmente. Debido a la alineación de variables en estructura y en la pila, una matriz de tres bytes probablemente tomará de 4 u 8 bytes en la memoria. Creo que se siente bien.

La asignación de un número entero de páginas del montón puede no funcionar tan bien como se desea debido a la sobrecarga de la estructura interna del montón. La asignación de 4096 bytes (1 página para una máquina de Windows de 32 bits) probablemente dé como resultado una asignación de 4104 bytes o más.

Si las constantes son banderas, entonces la historia es muy diferente. Normalmente es más eficiente tener indicadores de bits que indicadores en alguna base que no es una potencia de 2.

2

Nuestros tamaños variables son potencias de 2 (1, 2, 4 u 8 bytes). La computadora se siente cómoda trabajando en estos límites. En los viejos tiempos, solíamos rellenar cuidadosamente nuestras estructuras para que nuestro código fuera más rápido y, a veces, para facilitar la aritmética del puntero.

Si tiene una opción entre un tamaño de 256 y 257, vamos con 256. Una razón sería para la depuración.Cuando mira la memoria o un archivo, el depurador o el visualizador de archivos hexadecimales mostrarán los datos en líneas que son una potencia de dos.

Aquí hay una que muestra 16 bytes por línea, en grupos de 4.

alt text http://upload.wikimedia.org/wikipedia/en/2/2c/Hexedit-screenshot.png

Para banderas, las hacemos potencias de dos para que podamos tratarlos a todos por separado en una variable en lugar de en muchas variables o una matriz.

Por lo tanto, todos pueden enviarse ay desde la misma variable.

bits |= 8;  //00000100 
bits |= 32;  //00010000 

//bits is now 40 00010100 

bits &= 32;  //00010000 

//bits is now 32 00010000 

Muchos programadores escribirán los números en hexadecimal en lugar de decimal para que sea más fácil ver lo que está pasando con los bits individuales.

1

Con las computadoras binarias, es conveniente usar las normas binary multiples como Mebibyte (o Kibi, Gibi, Tebi ...). Esta potencia de 2 números también se ve bien en Octal o Hex notaciones.

1

Agregar más bits a un chip de memoria se suele hacer cuadruplicando el número de "celdas" en el chip. El doble de ancho, el doble de largo, cuatro veces la memoria (excepto distancias más pequeñas entre las "celdas").

También es más fácil multiplicar usando un solo desplazamiento en lugar del algoritmo genérico de mulplicación de agregar cambios sucesivos dependiendo de si un bit en particular está configurado o no. OpenGL era famoso por requerir una potencia de dos tamaños para que las texturas accedan a líneas de escaneo particulares.

2

Cuando se trata de tamaños de matriz, sospecho que hay dos razones por las cuales se favorecen los poderes de dos. Una de ellas, como lo demuestran varias respuestas aquí, es que los programadores que no saben qué está pasando "bajo el capó" parecen tener una sensación general de que de alguna manera podría ser más eficiente usar un poder de dos. El otro (en su mayor parte histórico ahora) tiene que ver con los búferes cíclicos.

Los búfers cíclicos que tienen potencias de dos pueden manejarse de manera más fácil y rápida utilizando máscaras en los índices de lectura y escritura (o punteros) en lugar de usar la operación de módulo normalmente más lenta o usar condiciones que requieren bifurcaciones. Esto fue crucial en las máquinas más antiguas, pero aún puede ser importante para transferir grandes cantidades de datos, por ej. el procesamiento de gráficos

Por ejemplo, en C, el número de bytes disponibles para ser leídos en una memoria intermedia cíclica se pueden obtener por:

pending = (SIZE + wr - rd) & (SIZE - 1); 

Si no se utiliza potencia de dos, entonces el equivalente sería:

pending = (SIZE + wr - rd) % (SIZE - 1); 

En las máquinas que no implementan una instrucción de división/módulo de ese pequeño "%" podría tomar varios cientos de ciclos, por lo que se necesita algo como:

if (wr >= rd) 
    pending = wr - rd; 
else 
    pending = (SIZE + wr) - rd; 

Que desordena el código y causa la bifurcación que puede paralizar la tubería de instrucción.

escritura a la memoria intermedia que era algo así como

buf[wr++] = x; 
if (wr == SIZE) 
    rd = 0; 

se convierte en el (generalmente) más eficiente:

buf[wr++] = x; 
wr &= SIZE-1; 

Por supuesto, si se ha utilizado una variable de 8 bits sin signo de índice de una matriz 256 de entrada entonces ni siquiera necesitas hacer el enmascaramiento.

+0

Buen punto. Todavía pienso en lenguaje ensamblador, y hago buffers circulares con ands. Cuando construyo una tabla de trigonometría, la construyo con 256 "grados" para poder mirar solo el byte inferior después de una suma o una resta. – Nosredna

Cuestiones relacionadas