2011-07-03 15 views
5

Supongamos que la funciónLa función necesita su propia matriz para el espacio de trabajo: ¿mejores prácticas?

void foo(int n, double x[]) 

tipo n-vector x, hace algunas operaciones en x, y luego se restaura el orden original de x antes de regresar. Así internamente, foo necesita un poco de almacenamiento temporal, por ejemplo, al menos un n-vector de enteros para que se guarde el orden original.

Cuál es la mejor manera de manejar esto de almacenamiento temporal? Se me ocurren dos enfoques obvios:

  1. foo declara su propio espacio de trabajo al declarar una matriz interna, es decir, en la parte superior de foo tenemos

    int temp[n]; 
    
  2. en la rutina principal vocación, dinámicamente asignar el n-vector de enteros de una vez y pasar en el almacenamiento a cada llamada a una versión de foo que acepta el almacenamiento temporal como una tercera arg, es decir,

    double *temp = malloc(n*sizeof(double)); 
    foo(n, x, temp); 
    

Me preocupa que la opción 1 es ineficiente (la función foo se llamará muchos veces con el mismo n), y la opción 2 es simplemente feo, ya que tengo que llevar este almacenamiento temporal para que sea siempre disponible donde sea que necesite una llamada al foo(n,x).

¿Hay otras opciones más elegantes?

Respuesta

5

Si que terminan usando la opción 2 - es decir, la función utiliza memoria que se asigna a otra parte - utilizar la encapsulación adecuada.

En pocas palabras, no pase en una matriz cruda, pase en un objeto context que tiene funciones init y correspondientes.

A continuación, el usuario todavía tiene que pasar en el contexto adecuadamente y configurarlo y derribarla pero los detalles están ocultos a ella y ella no se preocupa por los detalles de la asignación. Este es un patrón común en C.

typedef struct { 
    double* storage; 
} foo_context; 

void foo_context_init(foo_context*, int n); 

void foo_context_free(foo_context*); 

void foo(foo_context* context, int n, double x[]); 

Ahora, para un caso muy simple esto es claramente una tremenda sobrecarga y estoy de acuerdo con Oli que la opción 1 llamado para.

+0

+1. Por cierto, si el almacenamiento es 'n'-specific, el' int n' podría incluirse en el contexto, para acortar ligeramente la lista de argumentos a 'foo()'. –

5

Opción 1 es claramente el más limpio (porque está completamente encapsulado). Así que vaya con la Opción 1 hasta que los perfiles determinen que esto es un cuello de botella.

actualización

@ R comentario de abajo es correcta; esto podría explotar tu pila si n es grande. El método "encapsulado" pre-C99 sería malloc la matriz local, en lugar de ponerlo en la pila.

+1

La opción 1 es extremadamente peligrosa a menos que sepas que 'n' siempre es" pequeña ". –

+0

En la aplicación que estoy considerando, 'n' es probable 1e6 o 1e7, y' foo' podría llamarse cientos de veces. Supongo que esto es demasiado caro. – Michael

4

En la mayoría de las arquitecturas de la opción 1 es muy eficiente ya que asigna memoria en la pila y es típicamente un complemento a la pila y/o puntero de marco. Solo tenga cuidado de no hacer n demasiado grande.

+0

Tenga en cuenta también que esto es específico de C99. Antes de C99, esto tendría que hacerse con 'malloc'. –

+0

@Oli Charlesworth: Buen punto. –

+0

Pre-C99 simplemente se haría con 'int temp [MAX_N];' donde 'MAX_N' está vinculado a' n'. Si 'n' no tiene límites, VLA no es seguro y debes usar' malloc' de todos modos. Entonces, C99 no cambió nada en este aspecto. –

3

Como dijo Oli en su respuesta, lo mejor es que la función sea autónoma con respecto a esta matriz temporal. Una asignación única no va a costar mucho a menos que se llame a esa función en un ciclo muy rápido ... así que primero acierte, luego perfile y luego decida si vale la pena hacer una optimización.

Dicho esto, en algunos casos después de perfiles y cuando la estructura de datos de temperatura necesitaba era un poco más complejo que una sola matriz int I adoptó el siguiente enfoque:

void foo(int n, ... other parameters ...) 
{ 
    static int *temp_array, temp_array_size; 
    if (n > temp_array_size) 
    { 
     /* The temp array we have is not big enough, increase it */ 
     temp_array = realloc(temp_array, n*sizeof(int)); 
     if (!temp_array) abort("Out of memory"); 
     temp_array_size = n; 
    } 
    ... use temp_array ... 
} 

nota que el uso de unas normas matriz estática fuera por ejemplo, multihilo o recursión, y esto debe indicarse claramente en la documentación.

+0

Además de los problemas típicos con las variables "estáticas", otra cosa de la que debe tener cuidado es que el tamaño del búfer temporal no tiene límites y nunca disminuye. http://blogs.msdn.com/b/oldnewthing/archive/2006/05/02/588350.aspx – jamesdlin

Cuestiones relacionadas