PDA

View Full Version : Divagazione sui massimi sistemi...


blackskop
27-12-2010, 21:06
Mi sono imbattuto in questa riga di codice


A) (x - y == 2 || x - y == -2)


e mi sono detto: "E' leggibile ma non sarebbe più efficiente in questo modo?"


B) Math.abs(x - y) == 2


e dopo un nanosecondo l'ho modificata in


C) (x - y) * -1 == -2


ma in conclusione ancora non riesco a capire qual è il giusto compromesso tra velocità e leggibilità.


A B C
Leggibilità: 3 2 1
Efficienza: 2 1 3


Se le empiriche considerazioni non sono errate, la tabellina mi porterebbe a scegliere la soluzione A ma, dato che non sono poi un microcefalo, pensando anche al famoso detto "in medio stat virtus", ho optato per la soluzione C.

Voi avete qualche altro suggerimento?

Ludo237
27-12-2010, 21:26
Concordo sulla scelta C , della leggibilità me ne frego (faccio i commenti sopra piuttosto) però il codice è molto meglio

Don[ITA]
27-12-2010, 22:16
Scusate, ma siete proprio sicuri che C sia equivalente ad A e B?
Se ad esempio:
x = 0
y = 2
A) (0 - 2 == 2 || 0 - 2 == -2) -> vero
B) Math.abs(0 - 2) == 2 -> vero
C) (0 - 2) * -1 == -2 -> falso
:mbe:
O sono solo ubriaco io?? :eek:

tuccio`
27-12-2010, 22:42
ma infatti la c è sbagliata

e secondo me la b non è più efficiente della a

ps: a supporto della tesi


public class Test {

static public void main(String[] args) {
long t[] = new long[3];
int x = 0, y = 2;
t[0] = System.nanoTime();
for (int i = 0; i < 500000; i++) {
if (x - y == 2 || x - y == -2)
continue;
}
t[1] = System.nanoTime();
for (int i = 0; i < 500000; i++) {
if (Math.abs(x - y) == 2)
continue;
}
t[2] = System.nanoTime();
System.out.println(t[1] - t[0]);
System.out.println(t[2] - t[1]);
}

}


output:
1729179
4147813

Supdario
27-12-2010, 22:48
Quoto, l'opzione più veloce è la A, la C mi sembra sbagliata, e se la B magari è più leggibile, è comunque più lenta della A.

Ludo237
27-12-2010, 22:49
Che FAIL ... non avevo letto il *-1 .. avevo visto *1... :zzz: :bsod:

tuccio`
27-12-2010, 22:54
sarebbe comunque sbagliata :E

blackskop
27-12-2010, 22:58
Si la C è sbagliata ma non ho detto che la B è più veloce della A. In teoria la più veloce dovrebbe essere la C, se fosse corretta! Quindi, alternative non ce ne sono?


public class Test {

static public void main(String[] args) {
long t[] = new long[4];
int x = 0, y = 2;
t[0] = System.nanoTime();
for (int i = 0; i < 500000; i++) {
if (x - y == 2 || x - y == -2)
continue;
}
t[1] = System.nanoTime();
for (int i = 0; i < 500000; i++) {
if (Math.abs(x - y) == 2)
continue;
}
t[2] = System.nanoTime();
for (int i = 0; i < 500000; i++) {
if ((x - y) * -1 == -2)
continue;
}
t[3] = System.nanoTime();
System.out.println(t[1] - t[0]);
System.out.println(t[2] - t[1]);
System.out.println(t[3] - t[2]);
}

}


2363279
5412704
949930

tuccio`
27-12-2010, 23:28
e grazie al cazzo, la C fa un controllo in meno :asd: tant'è che è sbagliata

che confronto è? :asd:

Supdario
27-12-2010, 23:52
In ogni caso non mi sembra il massimo prendere come riferimento il C# per misurare l'ottimizzazione. :asd: Magari con un compilatore C/C++ (e relative impostazioni di ottimizzazione attivate) avrebbe dato risultati diversi. Ma in questo caso sono scontate le opzioni, anche se la C è concettualmente sbagliata.

blackskop
27-12-2010, 23:53
e grazie al cazzo, la C fa un controllo in meno :asd: tant'è che è sbagliata

che confronto è? :asd:

Ma guarda che anche se inizializzi la y a -2 i tempi sono gli stessi.

P.s. potresti anche moderare un po' il linguaggio...

blackskop
27-12-2010, 23:54
In ogni caso non mi sembra il massimo prendere come riferimento il C# per misurare l'ottimizzazione. :asd: Magari con un compilatore C/C++ (e relative impostazioni di ottimizzazione attivate) avrebbe dato risultati diversi. Ma in questo caso sono scontate le opzioni, anche se la C è concettualmente sbagliata.

Si tratta di Java ma il ragionamento dovrebbe prescindere dal linguaggio e da ottimizzazioni specifiche.

Supdario
27-12-2010, 23:58
Si tratta di Java ma il ragionamento dovrebbe prescindere dal linguaggio e da ottimizzazioni specifiche.

Errore mio. :asd:
Quando si parla di semplici operazioni matematiche è facile capire, ma ad esempio richiamare il metodo Math.Abs in un linguaggio interpretato ha una velocità diversa dal richiamare "abs" in C/C++, ma in questo caso è abbastanza chiaro quale sia il più veloce.

tuccio`
28-12-2010, 00:40
Ma guarda che anche se inizializzi la y a -2 i tempi sono gli stessi.

P.s. potresti anche moderare un po' il linguaggio...ovviamente, perché in quel caso

questo if

if (x - y == 2 || x - y == -2)


fa un solo or, il primo che è true e quindi ritorna senza processare il secondo, perché tanto tutta l'espressione è sicuramente true

non a caso avevo scelto il caso pessimo

il punto è che il secondo controllo la C non lo fa

blackskop
28-12-2010, 01:00
ovviamente, perché in quel caso

questo if

if (x - y == 2 || x - y == -2)


fa un solo or, il primo che è true e quindi ritorna senza processare il secondo, perché tanto tutta l'espressione è sicuramente true

non a caso avevo scelto il caso pessimo

il punto è che il secondo controllo la C non lo fa
No se cambi il valore di inizializzazione della y il controllo lo fa e i tempi non cambiano affatto e sono circa 1/2 del caso A, mentre col valore attuale di y i tempi sono in media 1/3 ma è il caso A che varia!
Comunque a prescindere da questo, c'è un ulteriore modo più efficiente di A e B?

tuccio`
28-12-2010, 01:08
In ogni caso non mi sembra il massimo prendere come riferimento il C# per misurare l'ottimizzazione. :asd: Magari con un compilatore C/C++ (e relative impostazioni di ottimizzazione attivate) avrebbe dato risultati diversi. Ma in questo caso sono scontate le opzioni, anche se la C è concettualmente sbagliata.per curiosità ho voluto anche provare in C++, la differenza si è molto assottigliata, ma c'è una cosa abbastanza incredibile che vorrei che qualcuno mi spiegasse

ho scritto questo codice:

#include <climits>
#include <ctime>
#include <cmath>
#include <iostream>

#define N UINT_MAX

using namespace std;

int main(int argc, char *argv[])
{
clock_t t[3];
int x = 0, y = -2;
t[0] = clock();
for (unsigned int i = 0; i < N; i++)
{
if (x - y == 2 || x - y == -2)
continue;
}
t[1] = clock();
for (unsigned int i = 0; i < N; i++)
{
if (abs(x - y) == 2)
continue;
}
t[2] = clock();
cout << (double)(t[1] - t[0]) / CLOCKS_PER_SEC << "s" << endl;
cout << (double)(t[2] - t[1]) / CLOCKS_PER_SEC << "s" << endl;
return 0;
}


l'ho compilato con mingw con il comando

g++ -o asd.exe asd.cpp -lm

e l'ho eseguito, l'output è stato:

10.686s
13.005s

poi ho detto, va be', proviamo anche con le ottimizzazioni e ho compilato con

g++ -o asd.exe asd.cpp -O -lm

risultato:

1.833s
1.834s

la domanda è.. che cavolo di ottimizzazione ha fatto? O:

Tommo
28-12-2010, 04:14
La B contiene una chiamata a funzione, che fa cose che non conosci.
Pure assumendo il caso ottimo che il compilatore riesce a realizzare l'inline, è sicuramente l'opzione più lenta.

La C invece è sbagliata.

Conclusione: meno massimi sistemi e più test dati alla mano :D

Supdario
28-12-2010, 12:25
per curiosità ho voluto anche provare in C++, la differenza si è molto assottigliata, ma c'è una cosa abbastanza incredibile che vorrei che qualcuno mi spiegasse

ho scritto questo codice:

#include <climits>
#include <ctime>
#include <cmath>
#include <iostream>

#define N UINT_MAX

using namespace std;

int main(int argc, char *argv[])
{
clock_t t[3];
int x = 0, y = -2;
t[0] = clock();
for (unsigned int i = 0; i < N; i++)
{
if (x - y == 2 || x - y == -2)
continue;
}
t[1] = clock();
for (unsigned int i = 0; i < N; i++)
{
if (abs(x - y) == 2)
continue;
}
t[2] = clock();
cout << (double)(t[1] - t[0]) / CLOCKS_PER_SEC << "s" << endl;
cout << (double)(t[2] - t[1]) / CLOCKS_PER_SEC << "s" << endl;
return 0;
}


l'ho compilato con mingw con il comando

g++ -o asd.exe asd.cpp -lm

e l'ho eseguito, l'output è stato:

10.686s
13.005s

poi ho detto, va be', proviamo anche con le ottimizzazioni e ho compilato con

g++ -o asd.exe asd.cpp -O -lm

risultato:

1.833s
1.834s

la domanda è.. che cavolo di ottimizzazione ha fatto? O:

In pratica ha eseguito un'ottimizzazione anche nelle zone limitrofe, ad esempio ha capito che x e y non cambiano mai, ed ha agito di conseguenza, traducendo il codice in questo modo:
int x = 0, y = -2;
int tmp = x-y; //Variabile temporanea
t[0] = clock();
bool condizione = (tmp == 2 || tmp == -2);
for (unsigned int i = 0; i < N; i++)
{
if (condizione)
continue;
}
t[1] = clock();
condizione = (abs(tmp) == 2);
for (unsigned int i = 0; i < N; i++)
{
if (condizione)
continue;
}
t[2] = clock();

Poi ovviamente la traduzione in codice macchina potrebbe aver subito ulteriori ottimizzazioni.