C/Visual C - Concatenar Datos

 
Vista:
sin imagen de perfil

Concatenar Datos

Publicado por Mohammed (5 intervenciones) el 07/02/2015 21:08:41
Ante todo agradeceros el tiempo invertido en leer mi pregunta.

Estoy trabajando en un software para procesado de audio en tiempo REAL en C++ con la herramienta QT. Necesito que los tiempos de computo de cada uno de los procesos que realizo sean minimizados por lo que ando optimizando cada una de las líneas de código.

Definiendo un buffer temporal de 40ms , al lanzar nuestro dispositivo con una frecuencia de muestreo de Fs=8000Hz, cada 320 muestras entramos en un función ProcesarDatos().

La idea es disponer de un buffer global que almacena los ultimos 10s grabados, es decir, de 80000 muestras. Este Buffer en cada iteración libera las 320 muestras iniciales y enlaza al final las 320 muestras nuevas. De esta forma el buffer es actualizado y el usuario puede observar en tiempo real la representación gráfica de la señal grabada.

En un principio he pensado en utilizar QVector (equivalente a std::vector pero para la herramienta QT) para esta implementación, con ello reducimos el proceso a pocas lineas de codigo:

1
2
3
int NUM_POINTS=320;
DatosTemporales.erase(DatosTemporales.begin(),DatosTemporales.begin()+NUM_POINTS);
DatosTemporales+= (DatosNuevos);  // Datos Nuevos con un tamaño de NUM_POINTS

En cada iteración creamos un vector de 80000 muestras ademas de liberar algunas posiciones por lo que requiere de cierto tiempo de procesado. Una alternativa por la que opte, fue el uso de double[], y iteraciones con un bucle for:

1
2
3
4
5
6
7
8
9
for(int i=0;i<80000;i++){
        if(i<80000-NUM_POINTS){
             aux=DatosTemporales[i];
             DatosTemporales[i+NUM_POINTS]=aux;
        }else{
             DatosTemporales[i]=DatosNuevos[i-NUN_POINTS];
       }
 
}

Tampoco presenta buenos resultados. Creo que la mejor forma es el uso de memoria dinámica. Implementar este proceso mediante punteros. ¿Alguien podría darme alguna idea de como implementarlo?
Valora esta pregunta
Me gusta: Está pregunta es útil y esta claraNo me gusta: Está pregunta no esta clara o no es útil
0
Responder

Concatenar Datos

Publicado por Tom (619 intervenciones) el 08/02/2015 22:07:22
Hay otros factores de diseño que podrían ser importantes. Pero en principio no uses memoria dinámica (es decir, no estés pidiendo y liberando memoria constantemente).
Si tu buffer global lo creas de una sola vez, con malloc() por ejemplo, luego no tienes más que ir manteniendo unos índices a las posiciones que te interesen. Además nunca necesitas borrar datos, cuando haga falta los sobreescribes y punto (es algo como un "buffer circular").

En tu caso, tienes bloques fijos de 320 muestras, así que te basta con usar índices al bloque.

suponiendo que la muestra sea un valor long, algo así:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// A nivel global si es necesario
long *buffer = nullptr;
int    primer_bloque, ultimo_bloque;
 
// Inicializacion cuando sea conveniente
void init() {
   buffer = new long[80000];
   primer_bloque = 0;
   ultimo_bloque = 0;
}
void append(long muestras[]) {
    long *tmp = buffer;
 
    // Caben 250 bloques
    if(ultimo_bloque == 249) {
        ultimo_bloque = 0;
    } else {
        tmp += (ultimo_bloque * 320);
        ultimo_bloque++;
    }
    // Probablemente memcpy() sea más rápido que un bucle for.
    memcpy(tmp, muestras, (320 * sizeof(long)));
}
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
1
Comentar

Concatenar Datos

Publicado por chema (234 intervenciones) el 08/02/2015 23:26:27
Este Buffer en cada iteración libera las 320 muestras iniciales y enlaza al final las 320 muestras nuevas.

Si se enlaza al final no hay buffer circular posible.
Se debe sustituir desde el principio.

N = new data
O = old data
+++++++++++++++++++++++
+NNNNNNNOOOOOOOOOOOO
+OOOOOOOOOOOOOOOOOO
+++++++++++++++++++++++
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
1
Comentar

Concatenar Datos

Publicado por Tom (619 intervenciones) el 09/02/2015 14:37:34
Vaya, pues acabo de entregar una librería, para algo muy parecido, basada en este funcionamiento. Espero que tomen en consideración el hecho de que funciona perfectamente antes de despedirme.

Si no quieres llamarlo circular, llámalo cíclico.

Por cierto, una forma de iterar sobre este buffer (por bloques) puede ser:

1
2
3
4
5
6
7
for(int i = bloque_mas_viejo; i != bloque_mas_reciente; i = (i + 1) % total_de_bloques) {
        long *bloque = buffer + (i * muestras_por_bloque);
 
        for(int c = 0; c < muestras_por_bloque; c++) {
           plot_muestra(bloque[c]);
        }
}
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
1
Comentar
sin imagen de perfil

Concatenar Datos

Publicado por Mohammed (5 intervenciones) el 09/02/2015 20:27:56
Sobreescribiendo las muestras iniciales con las que vienen a partir de las 320 siguientes te evitar el problema de liberar memoria y el buffer se comporta como un buffer ciclico o circular.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar
sin imagen de perfil

Concatenar Datos

Publicado por Mohammed (5 intervenciones) el 09/02/2015 20:26:47
Lo estoy implementado siguiendo las pautas que has planteado. Ahorro mucho tiempo liberando la memoria, el proceso de sobre-escritura presenta mejores resultados.

Respecto al uso memcpy(), parece ser que es mas rápido su uso en UNIX en windows apenas cambia los resutlados. Muchas gracias por tu respuesta.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Concatenar Datos

Publicado por Chema (234 intervenciones) el 09/02/2015 22:34:12
Si se trabaja en bloques se debe prestar atención al uso de memcpy .

The memcpy() function copies n bytes from memory area src to memory
area dest. The memory areas must not overlap. Use memmove(3) if the
memory areas do overlap.

The memmove() function copies n bytes from memory area src to memory
area dest. The memory areas may overlap:
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Concatenar Datos

Publicado por Chema (234 intervenciones) el 10/02/2015 02:45:28
Un ejemplo realizado en apenas una hora , tiene un par de tonterías , que ahora no tengo ganas de modificar.
lee un bloque de 100, y sobrescribe un bloque de 100 sobre la lectura anterior, hasta el final del buffer, es un ejemplo muy básico se puede mejorar .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
 
u_int *buffread ;
 
typedef struct FakeBuff{
	int size;
	u_int *buff;
	u_int *raw;
	u_int *end;
}FakeBuff;
 
FakeBuff *FkBuff;
 
int ReadNewData(u_int *start, u_int *end ){
	int i = 0;
 
	while( start <= end) {
		*start+++= i++;
	}
	return --i;
}
 
int ReadFromFakeBuff(u_int *raw){
	int size = 0;
	/* Sólo entra la primera vez*/
	if ( raw == FkBuff->buff  ){
		raw+=100;
		size = ReadNewData(FkBuff->raw ,raw);
	}else if ( raw <= FkBuff->end  ){
		size = FkBuff->end -raw;
		if ( size < 100)
			return 0;
		size = ReadNewData(FkBuff->raw ,raw);
		FkBuff->raw = raw;
		printf("\n SIZE = %u \n ",size);
	} else {
		return 0;
	}
 
	return size;
}
int ProcesData(void){
	u_int *end;
	u_int *raw;
	u_int readsize = 0;
	end = raw = buffread;
	readsize = ReadFromFakeBuff(raw);
	end = buffread + readsize;
	for(;;){
		if (raw >= end){
			readsize = ReadFromFakeBuff(raw);
			if (readsize == 0)
				break;
			end = raw + readsize;
		}
			printf("%u-",*raw);
		raw++;
 
	}
	return 0;
}
 
int main(void){
	buffread = malloc(1000 * sizeof(u_int));
 
	FkBuff = malloc(sizeof(FakeBuff));
	FkBuff->size = 1000;
	FkBuff->buff =  FkBuff->raw = buffread;
	FkBuff->end =  FkBuff->buff  + 1000;
	ReadNewData(FkBuff->buff,FkBuff->end );
	ProcesData();
 
	free(FkBuff);
	free(buffread);
 
	return 0;
}
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Concatenar Datos

Publicado por Chema (234 intervenciones) el 10/02/2015 11:39:48
El ejemplo anterior modificado.

Lo repito, es un ejemplo que funciona, pero no está comprobado para ponerlo en producción.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
 
u_int *buffread ;
 
typedef struct FakeBuff{
	u_int *buff;
	u_int *raw;
	u_int *end;
}FakeBuff;
 
FakeBuff *FkBuff;
#define BLOCKSIZE 100
 
int ReadNewData(u_int *start, u_int *end ){
	int i = 0;
 
	for(; start < end; i++) {
		*start++ = 1;
	}
	return i;
}
 
int FillBuff(u_int *start, u_int *end ){
	int i = 0;
 
	while( start < end) {
		*start+++= i++;
	}
	return i;
}
 
int ReadFromFakeBuff(u_int *raw){
	int size = 0;
	/* Sólo entra la primera vez*/
	if ( raw == FkBuff->buff  ){
		size = BLOCKSIZE;
		ReadNewData(FkBuff->raw ,raw);
		printf("\n FIRST TIME: SIZE = %u \n ",size);
	}else if ( raw < FkBuff->end  ){
		size = ReadNewData(FkBuff->raw ,raw);
		printf("\n NEW BLOCK: SIZE = %u \n ",size);
	} else {
		/* 
		* las ultimas 100 posiciones han sido leidas pero no 
		*actualizadas con datos nuevos.
		*completa el buffer hasta el final con datos nuevos
		*/
		ReadNewData(FkBuff->raw ,raw);
		return 0;
	}
	/*
	* Si ReadNewData() finalizara la lectura de datos
	*/
	if (size == 0 )
		return -1;
	FkBuff->raw = raw;
	raw += size;
 
	return size;
}
int ProcesData(void){
	u_int *end;
	u_int *raw;
	u_int readsize = 0;
 
	end = raw = buffread;
 
	for(;;){
		if (raw >= end){
			readsize = ReadFromFakeBuff(raw);
			if (readsize == 0){
			/* 
			* lectura terminada , el buffer contien datos  nuevos
			*por leer
			*Para volver a leer de nuevo el buffer
			* if (condición de fin = true )
			*end = raw = buffread; 
			*/
				break;
			} else if (readsize == -1){
				/*
				* Bufer de lectura vacio
				*/
				break;
			}
			end = raw + readsize;
		}
		printf("%u-",*raw);
		raw++;
 
	}
 
	return 0;
}
void PrintData(int size){
	u_int *p;
 
	p = FkBuff->buff;
	for(;size--; p++)
		printf("%u-", *p);
}
 
int main(void){
	buffread = malloc(1000 * sizeof(u_int));
 
	FkBuff = malloc(sizeof(FakeBuff));
	FkBuff->buff =  FkBuff->raw = buffread;
	FkBuff->end =  FkBuff->buff  + 1000;
	FillBuff(FkBuff->buff,FkBuff->end );
	printf("\n\n\nbuffer inicial, numeros consecutivos\n\n\n");
	PrintData(1000);
	printf("\n\n\nLectura de buffer y rellenado de las zonas leidas con 1\n\n\n");
	ProcesData();
	printf("\n\n\n buffer rellenado con 1, en la zonas leidas\n\n\n");
	PrintData(1000);
	free(FkBuff);
	free(buffread);
 
	return 0;
}
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
1
Comentar
sin imagen de perfil

Concatenar Datos

Publicado por Mohammed (5 intervenciones) el 10/02/2015 14:08:04
La verdad que soy nuevo en este foro y se agradece la grandes respuestas que se dan en esta comunidad. Basándome en las pautas que planteas en tu código estoy optimizando el mio propio.

He observado que usando memmove() se solventan la mayoría de los problemas de memcpy(). Por lo que he logrado agilizar los resultados. Ya se que no es la mejor forma de evaluar el rendimiento, pero usando el codigo inicial mediante bucles for y utilizando punteros y memmove(), he reducido los tiempos de ejecución de 245 us a 42 us tras 1000000 de iteraciones y haciendo la media ponderada. Esto supone que los tiempos se han reducido en un factor de 5 mejorando considerablemente el rendimiento.

Voy a realizar las modificaciones oportunas añadiendo algunos aspectos interesantes que he observado en tu código para ver la mejoría.


Muchas gracias.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Concatenar Datos

Publicado por Chema (1 intervención) el 10/02/2015 17:38:50
Lo único que vale de mi código es la idea y la técnica, el código en si mismo vale poco.

Si te fijas ,todo gira en 3 punteros. (u_int *buff; u_int *raw; u_int *end; ), y sus distancias, el resto es pura lógica, sumas y restas.

buff.............raw...................end

hay factores que pueden optimizar el código, como leer el fichero de entrada en bloques, crear un proceso/ hilo independiente que sólo lea el fichero y llene el buffer.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar
sin imagen de perfil

Concatenar Datos

Publicado por Mohammed (5 intervenciones) el 10/02/2015 18:53:31
Por muy sencillo que sea ya la propia molestia de ponerse a programarlo ya es un gran detalle a considerar. Además de que me sirve para observar alternativas y planteamientos posibles desde distintas perspectivas.

Yo estudio ingeniera de telecomunicaciones, mis capacidades de programación están bastante capadas pero mis capacidades de procesamiento, desarrollo y optimización de algoritmos para procesado de señal son más avanzadas. Con ayuda de gente más entendida en el tema se logra optimizar los resultados. Muchas gracias por todo.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar