como - malloc sizeof
¿Es mejor asignar memoria en el poder de dos? (11)
Con la cantidad actual de memoria y su velocidad, ya no creo que sea relevante.
Además, si va a asignar memoria con frecuencia, es mejor que considere la agrupación de memoria personalizada / preasignación.
Cuando usamos malloc()
para asignar memoria, ¿deberíamos dar el tamaño que está en potencia de dos? ¿O simplemente le damos el tamaño exacto que necesitamos?
Me gusta
//char *ptr= malloc( 200 );
char *ptr= malloc( 256 );//instead of 200 we use 256
Si es mejor dar el tamaño que está en poder de dos, ¿cuál es la razón para eso? ¿Por qué es mejor?
Gracias
Editar
El motivo de mi confusión es seguir una cita del blog de Joel Back to Basics
Los programadores inteligentes minimizan la distribución potencial de malloc asignando siempre bloques de memoria que son potencias de 2 en tamaño. Ya sabes, 4 bytes, 8 bytes, 16 bytes, 18446744073709551616 bytes, etc. Por razones que deberían ser intuitivas para cualquiera que juegue con Lego, esto minimiza la cantidad de fragmentación extraña que ocurre en la cadena libre. Aunque parezca que esto desperdicia espacio, también es fácil ver cómo nunca desperdicia más del 50% del espacio. Por lo tanto, su programa no utiliza más del doble de memoria de la necesaria, lo que no es gran cosa.
Lo siento, debería haber publicado la cita anterior anteriormente. ¡Mis disculpas!
La mayoría de las respuestas, hasta ahora, dicen que asignar memoria con el poder de dos es una mala idea, entonces ¿en qué situación es mejor seguir el punto de Joel sobre malloc()
? ¿Por qué dijo eso? ¿La sugerencia citada anteriormente está obsoleta ahora?
Amablemente explícalo.
Gracias
Cuando estoy asignando un búfer que puede necesitar seguir creciendo para acomodar datos de tamaño aún desconocido, comienzo con una potencia de 2 menos 1, y cada vez que se queda sin espacio, realizo el realoque con el doble del tamaño anterior más 1. Esto hace que nunca tenga que preocuparse por desbordamientos de enteros; el tamaño solo puede desbordarse cuando el tamaño anterior era SIZE_MAX, en cuyo punto la asignación ya habría fallado, y 2*SIZE_MAX+1 == SIZE_MAX
todos modos.
Por el contrario, si acabo de utilizar una potencia de 2 y la doblo cada vez, puedo obtener con éxito un búfer de 2 ^ 31 bytes y luego reasignarlo a un búfer de 0 bytes la próxima vez que duplique el tamaño.
Como algunas personas han comentado que el poder-de-2-menos-12 es bueno para ciertas implementaciones de malloc, uno también podría comenzar con una potencia de 2 menos 12, luego duplicarla y agregar 12 en cada paso ...
Por otro lado, si solo está asignando pequeños buffers que no necesitarán crecer, solicite exactamente el tamaño que necesita. No intentes adivinar qué es bueno para malloc.
Debe usar realloc () en lugar de malloc () al reasignar. http://www.cplusplus.com/reference/clibrary/cstdlib/realloc/
¿Siempre usas una potencia de dos? Depende de lo que esté haciendo tu programa. Si necesita reprocesar toda su estructura de datos cuando crece a una potencia de dos, sí tiene sentido. De lo contrario, simplemente asigna lo que necesitas y no acaparas la memoria.
Es posible que desee asignar memoria en términos del tamaño de palabra del procesador; no cualquier viejo poder de 2 servirá.
Si el procesador tiene una palabra de 32 bits (4 bytes), luego asigne unidades de 4 bytes. Asignar en términos de 2 bytes puede no ser útil ya que el procesador prefiere que los datos comiencen en un límite de 4 bytes.
Por otro lado, esto puede ser una micro-optimización. La mayoría de las bibliotecas de asignación de memoria están configuradas para devolver la memoria que está alineada en la posición correcta y dejarán la menor cantidad de fragmentación. Si asigna 15 bytes, la biblioteca puede rellenar y asignar 16 bytes. Algunos asignadores de memoria tienen grupos diferentes según el tamaño de asignación.
En resumen, asigne la cantidad de memoria que necesita. Deje que la biblioteca / administrador de asignación maneje la cantidad real por usted. Ponga más energía en la corrección y robustez que preocuparse por estos temas triviales.
Es algo irrelevante.
Malloc realmente asigna un poco más de memoria de la que usted solicita, porque tiene sus propios encabezados para tratar. Por lo tanto, el almacenamiento óptimo es probablemente algo así como 4k-12
bytes ... pero eso varía según la implementación.
En cualquier caso, no hay motivo para que acumule más almacenamiento del que necesita como técnica de optimización.
Esto es totalmente dependiente de la implementación libc
dada de malloc(3)
. Depende de esa implementación reservar trozos de pila en el orden que considere oportuno.
Para responder a la pregunta, no, no es "mejor" (¿aquí con "mejor" quiere decir ...?). Si el tamaño que pides es demasiado pequeño, malloc(3)
reservará un trozo más grande internamente, así que simplemente conserva tu tamaño exacto.
Para hacer de abogado del diablo, así es como Qt hace:
Supongamos que agregamos 15000 caracteres a la cadena QString. Luego, las siguientes 18 reasignaciones (de un posible 15000) ocurren cuando QString se queda sin espacio: 4, 8, 12, 16, 20, 52, 116, 244, 500, 1012, 2036, 4084, 6132, 8180, 10228, 12276, 14324, 16372. Al final, QString tiene 16372 caracteres Unicode asignados, 15000 de los cuales están ocupados.
Los valores anteriores pueden parecer un poco extraños, pero aquí están los principios rectores:
QString asigna 4 caracteres a la vez hasta que alcanza el tamaño 20. De 20 a 4084, avanza doblando el tamaño cada vez. Más precisamente, avanza a la siguiente potencia de dos, menos 12. ( Algunos asignadores de memoria funcionan peor cuando se solicitan potencias exactas de dos , porque usan unos pocos bytes por bloque para la contabilidad). A partir de 4084 en adelante, avanza por bloques de 2048 caracteres (4096 bytes). Esto tiene sentido porque los sistemas operativos modernos no copian la información completa al reasignar un búfer ; las páginas de memoria física simplemente se reordenan, y solo los datos en la primera y la última página deben copiarse.
Me gusta la forma en que anticipan las características del sistema operativo en el código que debe funcionar bien desde los teléfonos inteligentes hasta las granjas de servidores. Dado que son personas más inteligentes que yo, supongo que dicha función está disponible en todos los sistemas operativos modernos.
Si está asignando algún tipo de búfer expandible donde necesita seleccionar un número para las asignaciones iniciales, entonces sí, los poderes de 2 son buenos números para elegir. Si necesita asignar memoria para struct foo, entonces solo malloc (sizeof (struct foo)). La recomendación para asignaciones de potencia de 2 proviene de la ineficacia de la fragmentación interna, pero las implementaciones de malloc modernas destinadas a sistemas de multiprocesador están empezando a utilizar agrupaciones locales de CPU para asignaciones lo suficientemente pequeñas como para que importen, lo que evita la contención de bloqueo que solía resultado cuando varios hilos intentan malloc al mismo tiempo, y pasan más tiempo bloqueados debido a la fragmentación.
Al asignar solo lo que necesita, se asegura de que las estructuras de datos estén empaquetadas de forma más densa en la memoria, lo que mejora la tasa de aciertos de la memoria caché, lo que tiene un impacto mucho mayor en el rendimiento que la fragmentación interna. Existen escenarios con implementaciones de malloc muy antiguas y sistemas multiprocesador de muy alta gama donde las asignaciones de relleno explícito pueden proporcionar una aceleración, pero sus recursos en ese caso se emplearían mejor para obtener una mejor implementación de malloc en ese sistema. El preajuste también hace que el código sea menos portátil y evita que el usuario o el sistema seleccionen el comportamiento malloc en tiempo de ejecución, ya sea programáticamente o con variables de entorno.
La optimización prematura es la fuente de todos los males.
Siempre hay pruebas ...
Puede probar un programa de "muestra" que asigna memoria en un bucle. De esta forma puede ver si su compilador asigna mágicamente memoria en potencias de 2. Con esa información, puede intentar asignar la misma cantidad de memoria total usando las 2 estrategias: bloques de tamaño aleatorio y potencia de 2 bloques de tamaño.
Sin embargo, solo esperaría diferencias, si las hubiera, para grandes cantidades de memoria.
Solo da el tamaño exacto que necesitas. La única razón por la cual un tamaño de potencia de dos podría ser "mejor" es permitir una asignación más rápida y / o evitar la fragmentación de la memoria.
Sin embargo, cualquier implementación de malloc
no trivial que se preocupe por ser eficiente redondeará internamente las asignaciones de esta forma siempre y cuando sea apropiado hacerlo. No necesita preocuparse por "ayudar" malloc; malloc puede hacerlo bien por sí mismo.
Editar:
En respuesta a su cita del artículo de Joel on Software, el punto de Joel en esa sección (que es difícil de discernir correctamente sin el contexto que sigue al párrafo que citó) es que si espera volver a asignar con frecuencia un búfer, es es mejor hacerlo multiplicativamente, en lugar de hacerlo aditivamente. Esto es, de hecho, exactamente lo que hacen las clases std::string
y std::vector
en C ++ (entre otros).
La razón de que esto sea una mejora no se debe a que esté ayudando a malloc
proporcionando números convenientes, sino porque la asignación de memoria es una operación costosa , y está tratando de minimizar el número de veces que lo hace. Joel está presentando un ejemplo concreto de la idea de una compensación de tiempo-espacio. Argumenta que, en muchos casos en los que la cantidad de memoria necesaria cambia dinámicamente, es mejor perder algo de espacio (asignando hasta el doble de lo que necesita en cada expansión) para ahorrar el tiempo que se requeriría para virar repetidamente en exactamente n
bytes de memoria, cada vez que necesite n
más bytes.
El multiplicador no tiene que ser dos: puede asignar hasta tres veces más espacio que necesita y terminar asignando en potencias de tres, o asignar hasta cincuenta y siete veces más espacio que necesita y terminar con asignaciones en potencias de cincuenta y siete. Cuanta más asignación tenga, menos necesitará volver a asignar, pero perderá más memoria. Asignar en poderes de dos, que utiliza como mucho el doble de memoria que la necesaria, resulta ser una buena compensación de punto de partida hasta y a menos que tenga una mejor idea de cuáles son exactamente sus necesidades.
Sí menciona de paso que esto ayuda a reducir la "fragmentación en la cadena libre", pero la razón es más por el número y la uniformidad de las asignaciones que se realizan, que por su tamaño exacto. Por un lado, cuantas más veces asigne y desasigne memoria, más probabilidades tendrá de fragmentar el montón, sin importar el tamaño que esté asignando. En segundo lugar, si tiene varios búferes que está cambiando de tamaño dinámicamente utilizando el mismo algoritmo de cambio de tamaño multiplicativo, es probable que si uno cambia el tamaño de 32 a 64 y otro cambia de 16 a 32, la reasignación del segundo pueda caber justo donde el primero solía ser. Este no sería el caso si uno cambia el tamaño de 25 a 60 y el otro de 16 a 26.
Y nuevamente, nada de lo que él está hablando se aplica si va a hacer el paso de asignación solo una vez.
Pudo haber sido cierto una vez, pero ciertamente no es mejor.
Simplemente asigne la memoria que necesita, cuando la necesite y libérela tan pronto como haya terminado.
Hay demasiados programas derrochadores de recursos, no hagas tuyo uno de ellos.