Newsgroup: it.comp.lang.c
Titolo: Problama con puntatore void

Ciao tutti!
Ho un problemino con un puntatore void e cerco' di circoscrivere le informazioni per sperare di definire correttamente la situazione. 
 
Sto lavorando con una libreria open source che non ho scritto io e che ho modificato per soddisfare le mie necessita'.
 
Attualmente il codice _funziona_ ed e' presente una funzione del tipo:
 
nomeFunzione(int nsamples, void *data)
{
   fread(data, 8, nsamples, filePointer);
}:
l'8, sebbene non sia stilisticamente bellissimo, rappresenta la dimensione di un elemento complesso in cui parte reale e parte immaginaria sono floating point (4 byte per RE e 4 byte su IM).
 
Adesso vorrei effettuare una modifica in cui i dati in ingresso invece che in formato floating point sono rappresentati come 2 short che trasformero' in floating point attraverso una divisione per 32767.
 
La mia idea era quella di dichiarare ed allocare un vettore temporaneo di short, leggere gli short e poi dividerli per 32767 e poi _in qualche modo_ riassociarlo al puntatore *data.
 
I tentativi che ho fatto non sono andati a buon fine.
 
Potreste, per cortesia, darmi una mano?
Grazie
 
 


Se ho capito bene quello che vuoi fare:
 
short* d=malloc(sizeof(short) * 2 * nsamples);
if (!d)
   errore...
size_t n=fread(d,sizeof(short) * 2, nsamples, filePointer)
 
poi converti d in un array di float (o double se e' quello) copiando in 
data tenendo conto di n
 
 
Magari prima della copia:
ASSERT(sizeof(float) == 4) ; // nel caso non sai a cosa si riferisce 
quel void* per essere sicuri - non so se quell' 8 si riferisce ad un 
sizeof(float) * 2 o sizeof(double) * 2 ma dovrebbe essere float
 
http://pubs.opengroup.org/onlinepubs/009695399/functions/fread.html
 
 
Per la fread
 
 
 

Enoquick grazie per la risposta.
Hai capito perfettamente. Mi manca però la risposta alla questione principale.
Se chiamiamo come hai fatto tu d i dati short temporanei, come faccio a fare in modo che all'uscita della subroutine in data ci siano i dati di d convertiti in floatin point e divisi per 2^15-1;
I vettore data è dichiarato void ed il compilatore mi segnala un cattivo uso del puntatore void se tento di metterci qualcosa dentro


converti il void* in un float* con un cast cosi il compilatore non si 
lamenta:
 
float* dl=(float*)data;
 
Naturalmente devi essere sicuro che la size dell'area puntata da data 
sia sufficiente per contenere il numero di float che ti interessano.
 


Ma io devo mettere il contenuto di d in data.
Come fai tu non e' al contrario (o mi sono perso qualcosa?)
Grazie


Vediamo se ho capito cosa vorresti fare...
 
#include <malloc.h>
#include <math.h>
 
union Pointer
{
	void *pVoid;
	short *pShort;
	float *pFloat;
};
FILE *fp;
void funzione(int, void **);
 
int main()
{
	Pointer uPointer;
	int i, nsamples = 4;
	funzione(nsamples, &(uPointer.pVoid));
 
	for (i = 0; i < nsamples; i++)
		printf("val: %f\n", uPointer.pFloat[i]);
	getchar();
     return 0;
}
void funzione(int nsamples, void **data)
{
	int i, gSize = sizeof(short int);
	Pointer uG, uO;
	uG.pVoid = malloc(gSize * nsamples);
	uG.pShort[0] = 1000;   /* fread(uG.pVoid, gSize, nsamples, fp); */
	uG.pShort[1] = 1000;   /*                                       */
	uG.pShort[2] = 1000;   /*        sostituisce la fread           */
	uG.pShort[3] = 1000;   /*  ----------------------------------  */
	uO.pVoid = malloc(sizeof(float) * nsamples);
	for (i = 0; i < nsamples; i++)
		uO.pFloat[i] = uG.pShort[i] / (powf(2, 15) - 1);
	free(uG.pVoid);
	*data = uO.pVoid;
}
 

Ringrazio anche jak per il sorgente postato anche se lui ha alterato i dati di ingresso. Il puntatore infatti e' singolo non doppio.
 
Alla fine ho risolto e per completezza riporto la soluzione.
 
Nella funzione il puntatore void *data arriva gia' allocato.
 
1) Genero 2 vettori tmp_data_short e tmp_data_float di nsamples elementi.
2) Leggo con fread i dati nel tmp_data_short
3) Converto tmp_data_short in tmp_data_float
4) memcpy(data, tmp_data_float,2*nsamples*4);
5) libero tmp_data_short e tmp_data_float
 
Grazie comunque per avermi dedicato del tempo


Eh sai... dopo aver letto i vostri scambi ho rinunciato a capire quale 
fosse l'esatta struttura dei dati e me ne sono inventata una per 
scrivere l'esempio. Anche la mia elaborazione dei dati è sbagliata ma 
non ho corretto nemmeno quella perché il mio voleva, comunque, essere 
solo un esempio. La cosa veramente importante che non avevo capito era 
che il prototipo della funzione doveva rimanere immutato ma in quel caso 
lo spazio per i dati _doveva_, per forza, essere preallocato a meno di 
non fare un gioco coi puntatori come questo:
 
sempre con la chiamata:
 
funzione(nsamples, &uPointer.pVoid);
 
invece di:
 
void funzione(int nsamples, void **data)
{
     ..................
      *data = uO.pVoid;
}
 
ritornare:
 
void funzione(int nsamples, void *data)
{
     ..................
     *(void **)data = (void *)(uO.pVoid);
}
 
...ma con lo spazio preallocato tutto ciò è inutile.
 
Un saluto.
 

      *(void **)data = (void *)(uO.pVoid);
il cast a destra è inutile:
       *(void **)data = uO.pVoid;

jak ti ringrazio ancora per il tempo che mi hai dedicato.
Il prototipo della funzione non può cambiare perché si tratta di una libreria open source che effettua la sincronizzazione e decodifica dei segnali in broadcast emessi da una stazione radio base LTE.
La versione originale funziona con schede SDR e forniscono il risultato in tempo reale. Io avendo una scheda con capacità elaborative limitate, posso solo acquisire i dati "grezzi" con una frequenza di campionamento tipica dell'LTE 1.92MSPS) e poi elaborare il tutto.
Queste schede prevedono la possibilità di salvare in un formato detto sc16 o fc32.
Nel primo i segnali I e Q sono salvati come due interi short (4byte) nel secondo come due float (8byte). Hi scelto il primo perché mi consentiva, ovviamente di acquisire più dati occumando metà della memoria necessaria.
 
Prima quindi dovevo convertire i dati da sc16 in fc32 e poi farli leggere dal sw .
Adesso li leggo direttamente come sc16.
 
Tutto questo pandemonio mi dirai per quale motivo?
Per abbattere i tempi di elaborazione. Su di un sistema embedded come Raspberry Pi 3 tutto il processo è psssato da 5 minuti a 42 secondi. Penso che ne valeva proprio la pena cercare di implementare questa variante.
 

Il 16/03/2017 15:43, arkkimede ha scritto:

Bene,ora visto che i tempi sono lunghi perche fare una copia in piu?
 
 

 Bene,ora visto che i tempi sono lunghi perche fare una copia in piu?
 
Scusa ma non ho capito a cosa ti riferisci.
I tempi si sono ridotti ed a che copia fai riferimento?


Non sto dicendo che i tempi non si siano ridotti
Tu lo hai affermato e non vedo il motivo di dubitare
Pero si puo fare una piccola ottimizzazione se non vado errato
 
1) costruisci un array di short
2) leggi da file nell' array di short
3) costruisci un array di float
4) copia di array di short in array di float
5) copia array di float in data (il void*)
 
 
L'ottimizzazione e' questa:
 
  perche non copiare l'array di short direttamente in data trattando 
data come un array di float che poi e'quello che e' ?
 
 
 


 
Non so che processing devi fare, ma 42 secondi sono un'eternità per qualasiasi
cosa, e te la butto lì: il RaspPi3 monta un ARMv8 con istruzioni SIMD (anche full
floating, se non sbaglio). Se la tua libreria non le sfrutta, prova a riscrivere
il processing usandole (o usando GSL se vuoi far veloce e puoi). Se invece le
sfrutta prova a ricompilare con gli switch giusti :)
 
Te lo dico perché l'ultima volta che ho avuto un caso simile su un processo che
doveva andare in batch, sono passato da tre giorni a venti minuti ;)
 
Ciao!

Si concordo con te ma come fare la copia senza passare dal vettore temporaneo float.
 
Puoi suggerirmi una soluzione?
 
Riporto i passi che ho fatto
 
short *tmp_data_short;
float *tmp_data_float;
 
tmp_data_short = (short *)malloc(sizeof(short)*2*nsamples);
tmp_data_float = (float *)malloc(sizeof(float)*2*nsamples);
 
fread(tmp_data_short,sizeof(short),2*nsamples,fp);
 
for(i=0; i<2*nsamples; i++)
  tmp_data_float[i] = (float)(tmp_data_short[i]/(float)(2^15-1)); // Perdonate l'elevamento a potenza che nel c non esiste 
 
free(tmp_data_short);
memcpy(data,tmp_data_float,2*nsamples*sizeof(float));
free(tmp_data_float);
 
Come salto il passaggio per tmp_data_float?

Può essere ma non credo di esserne capace.
 
Prima di tutto la libreria (che ripeto non ho scritto io) per essere generata fa uso del cmake che non conosco.
Ho imparato quelle quattro istruzioni che devo usare per costruire la libreria e stop.
 
Potresti, per piacere, fornirmi delle indicazioni per sfruttare le istruzioni SIMD o la libreira GLS
Grazie


 
Oddio, qui si apre un mondo :)
 
Le SIMD sono istruzioni del processore, che volendo puoi anche scrivere in
assembly, o usando le estensioni C che il compilatore ti fornisce. Per quelle
ti devi vedere la documentazione del tuo processore, per esempio qui:
https://www.element14.com/community/servlet/JiveServlet/previewBody/41836-102-1-229511/ARM.Reference_Manual.pdf
 
O la documentazione del tuo compilatore, in gcc per esempio qui:
https://gcc.gnu.org/onlinedocs/gcc-3.4.0/gcc/C-Extensions.html
 
sezione "vector extensions".
 
GSL l'ho citata perché è una delle tante liberie (anche se la più famosa nello
open source) che sfrutta questo tipo di ottimizzazioni, specialmente sui tre
livelli BLAS standard. La documentazione la trovi facilmente online, tipo qui
https://www.gnu.org/software/gsl/manual/html_node/
 
 
Comunque, a livello generale: se fai tante operazioni matematiche facilmente
vettorializzabili prendi la cosa in considerazione, altrimenti è solo tempo
perso.
 
Ciao!


Se ho capito bene pero' (e questo assunto non e' detto che sia valido), queste funzioni devono essere utilizzate durante la stesura del codice sorgente.
Io pensavo ci fossero delle opzioni di compilazione per attivare queste proprieta'. Pensare di riscrivere la libreria per includere queste funzioni e' fuori discussione.
Grazie comunque per i riferimenti


una roba cosi credo:
 
for(i=0; i<2*nsamples; i++)
{
  data[i] = (void)((float)(tmp_data_short[i]/(float)(2^15-1)));
}
 
Ciao Jack

Il giorno venerdì 17 marzo 2017 03:35:22 UTC+1, 
fma...@gmail.com
 ha scritto:

IMHO conviene usare una libreria standard (GSL, LAPACK,...) piuttosto che cominciare ad usare una libreria specifica per usare le istruzioni SIMD di ARM (ad esempio la CMSIS per gli ARM Cortex), per non perdere la portabilita' del codice.
 
In ogni caso un -O3 come opzione di compilazione aiuta parecchio :)
 
Ciao Jack

 Come salto il passaggio per tmp_data_float?
void funzione(int nsamples, void *data)
{
   short *tmp_data_short;
   float *tmp_data_float = (float *)data;
   float Div = (float)(short)~(1 << ((sizeof(short) * 8) - 1));
   nsamples *= 2;
   tmp_data_short = (short *)malloc(sizeof(short) * nsamples);
 
   fread(tmp_data_short, sizeof(short), nsamples, fp);
 
   for(i=0; i < nsamples; i++)
     tmp_data_float[i] = (float)tmp_data_short[i] / Div; //
 
   free(tmp_data_short);
}
Questo potrebbe essere un modo per semplificarla ed accelerarla ancora 
un pochino:
- nsamples che in tutta la funzione è accompagnato da un * 2 la si 
moltiplica per 2 una volta per tutte ed abbiamo tolto qualche operazione 
al micro.
- dalla memcpy si avvince che data punta ad un pezzo di memoria atta ad 
ospitare l'array di float e tanto vale scriverci direttamente 
acquisendone l'indirizzo così si può evitare la memcpy.
- visto che nel ciclo for il divisore è sempre lo stesso tantovale 
calcolarlo una volta per tutte. Per ottenere Div ho usato quel metodo 
per utilizzare il meno possibile la libreria matematica.
 

 per utilizzare il meno possibile la libreria matematica.
 
no, conviene lasciarlo li (o fare un #define), il compilatore se vede roba costante nelle istruzioni fa il calcolo lui e mette un literal. (con le ottimizzazioni attivate perlomeno).
Inoltre sei sicuro che sizeof(short) == 2? Meglio usare i "nuovi" tipi per le variabili (int16_t ad esempio).
 
Ciao Jack
 

Per Jack:
il copiare direttamente in data il float genera i seguenti errori
 
rf_imp.c:299:10: warning: dereferencing ‘void *’ pointer
      data[i] = (void)((float)(tmp_data_short[i]));
          ^
rf_imp.c:299:6: error: invalid use of void expression
      data[i] = (void)((float)(tmp_data_short[i]));
      ^
 

PS: La divisione la ho evitata, volevo solo vedere se il compilatore accettava l'istruzione

Per Jak la tua implementazione prodece i seguenti errori:
 
pdsch_ue_no_zeros.c:266:33: warning: dereferencing ‘void *’ pointer
   float *tmp_data_float=(float) *data;
                                 ^
pdsch_ue_no_zeros.c:266:3: error: invalid use of void expression
   float *tmp_data_float=(float) *data;
   ^
pdsch_ue_no_zeros.c:283:10: warning: dereferencing ‘void *’ pointer
      data[i] = (float)(tmp_data_short[i]);
          ^
pdsch_ue_no_zeros.c:283:6: error: invalid use of void expression
      data[i] = (float)(tmp_data_short[i]);
      ^

       ^
 
Anche qui, occhio: devi mettere  tmp_data_float[i] non data[i]
 
ciao
Giacomo


Abbastanza semplice,vedi data come tmp_data_float
 
 
float* tmp_data_float=(float*) data;
la copia e'la stessa di prima
 
Per l'elevamento a potenza visto che e'una costante te lo calcoli tu a 
parte (cosi non usi pow)
 
static const float m=.... // qui ci metti il risultato di 2^15 - 1
 
Devi stare attento ad una cosa, il numero di elementi letti  non e'detto 
che sia 2* nsamples ma puo essere inferiore (anche a zero)
 
La fread ritorna il numero di elementi letti
 
Per ovviare a questo problema si puo inizializzare tmp_data_short a zero 
con memset (malloc non inizializza)
memset(tmp_data_short,0,sizeof(short)*2*nsamples);
dopo la malloc
visto che il prototipo della funzione in cui stai non si puo cambiare 
(non si puo ritornare il numero di elementi letti)
 
 
 
 
 
 

 siano di 2 byte?
 
sei tu che hai scritto:
float Div = (float)(short)~(1 << ((sizeof(short) * 8) - 1));
 
lui vuole 32767 (0x7FFF, 2^15-1), se sizeof(short) != 2, Div non e' piu' quello che l'OP vuole.
 
Sei tu col tuo esempio che hai tirato fuori i short, l'OP aveva un puntatore a void e un int.
 
Il mio commento era semplicemente un consiglio, se scrivi codice e' cosa buona e giusta usare gli int16_t ecc. invece di short, int ecc. perche' altrimenti non puoi fare (se vuoi fare codice portabile) assunzioni sulle dimensioni.
Cosa che tu fa quando calcoli Div.
 
Detto questo va da se' che il compilatore non puo' fare miracoli ma fare ottimizzazioni "a cazzo" serve poco. Se si vuole ottimizzare il codice si prende un profiler e si comincia a vedere cosa conviene ottimizzare.
 
Ciao Jack


cosi funziona (sotto che' un programma minimale per testare)
 
for(i=0; i<2*nsamples; i++) 
{ 
  ((float*)data)[i] = (float)tmp_data_short[i]/(float)(2^15-1); 
}
 
///////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
 
 
void funzione(void *data,int nsamples)
{
    short a[4] = {1,2,3,4};
    int i;
 
    for(i=0;i<nsamples;i++)
    {
        ((float*)data)[i] = (float)a[i];
    }
}
 
 
int main () {
 
 float array[4] = {0.0, 0.0, 0.0, 0.0};
 
        printf("%f %f %f %f\n",array[0],array[1],array[2],array[3]);
 
        funzione((void *)array,4);
 
        printf("%f %f %f %f\n",array[0],array[1],array[2],array[3]);
 
        return 0;
}
 
///////////////////////////////////////////////////////////////////////
 
Ciao Jack

 
 
Che ricordi io -O3 non attiva le vettorializzazioni: per farlo "a mano" bisogna
usare i builtin dedicati e compilare con i relativi -march/-mssse/-mneon/etc.
 
Ciao!
 


 
Poi non insisto più ma il tuo non è un buon consiglio oppure non riesco 
a spiegarmi.
Cordialmente chiudo.


:-)
tnx

Devo ringraziare tutti quelli che hanno partecipato a questa discussione per l'aiuto che mi è stato offerto.
Oggi ho misurato il codice su Raspberry PI 3 facendolo girare 20 volte ed ho una durata media di 40 secondi (quindi mi avete fatto guadagnare ancora qualche secondo).
 
E' vero possono essere un'eternità ma per il processo in cui deve essere usato possono tranquillamente essere l'intervallo tra una fase e l'altra dell'attività e quindi va benissimo così.
 
Un'ultima richiesta: spulciando in internet e nella libreria credo di aver capito dove vanno inserite le opzioni di compilazione nei file relativi al cmake.
 
Gli autori hanno lasciato queste opzioni vuote per garantire la massima portabilità su tutte le piattaforme.
 
Nel mio caso però la piattaforma di interesse la conosco e potrei magari riempire quei campi.
 
Ho provato a cecare in internet ma trovo di tutto di più e non esiste una versione unica.
 
Potrestre indicarmi una fonte affidabile che riporti le opzioni di compilazione più adatte al processore a bordo della Raspberry PI 3.
 
GRAZIE
Daniele
 

 Cordialmente chiudo.
 
Ciao Jack


Quindi sarei io quello che non capisce?
Dal calcolo che fa, si evince che intende avere, come riultato, una 
serie di valori float che vanno da +1 a -1 la cui risoluzione è 
proporzionale alla dimensione del dato grezzo.
Ora, se tu avessi in mano sia i sorgenti che scrivono il file sia quelli 
che lo leggono avresti sicuramente ragione. Probabilmente questo è il 
presupposto da cui parti ma non è corretto. Sicuramente il problema sta 
nel fatto che chi ha scritto la parte che scrive il file non ha 
ristretto la tipologia del dato come tu intendi (int16_t o qualsiasi 
altro sia) e sicuramente farai un errore a decidere che lui intendesse 
usare 16 bit. Per come ho riscritto la funzione se tu ricompilassi i 
sorgenti che scrivono e che leggono tutto funzionerebbe lo stesso anche 
se lo short diventasse di 64 bit perché 64 sarebbero per chi scrive e 64 
sarebbero per chi legge e senza metter mano al codice; il risultato 
sarebbe sempre un float con un valore che va da +1 a -1 ma con una 
risoluzione maggiore (64 anziché 16 bit). Come dici tu, invece, dovresti 
metter mano sia alla funzione di scrittura che in quella di scrittura. 
Spero di essermi spiegato meglio, intelligentone.