MSalters schreef op 22 May 2003 @ 22:17:
[...]
Toch wel. Je bent namelijk een jump kwijt, dat vindt de pipeline leuk. Bovendien verklein je daar de kans op een page fault en kan de code kleiner worden, als de geinline-de code kleiner is dan de functiecall prolog/epilog.
De kans op een pagefault in een ander stuk van je programma kan hierdoor stijgen, maar dat is een tradeoff keus waar je de goede programmeurs aan kunt herkennen.
Aan die pagefaults heb je idd een punt, daar had ik even niet gedacht. Maar aan de andere kant komt een pagefault bij code tegenwoordig zo goed als niet voor. Over het algemeen is er geheugen zat zodat alle code van het programma zich ten alle tijden in het geheugen kan bevinden.
Wat die pipeline betreft: ik denk dat dat verwaarloosbaar is in vergelijking met de code die _in_ de functie zit en geoptimaliseert kan worden. Ik heb bijvoorbeeld een vector en matrix library waar ik operator overloading toepas. Vroeger was ik daar niet zo'n voorstander van, want dat bracht onnodige temporaries met zich mee waardoor er bijvoorbeeld bij een matrix vermenigvuldiging heen en weer gekopieerd werd. Tegenwoordig doe ik dat met inlining en creatief template gebruik, en wordt de berekening op het moment van de assignment uitgevoerd, en temporaries dus volledig weggeoptimaliseerd. En
dat is de kracht van inlining imho
Voorbeeld (even uit mijn hoofd):
C++:
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
| // methode 1
struct matrix
{
float data[4][4];
matrix operator * (const matrix & m) const
{
matrix r;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
r.data[i][j] = data[i][0] * m.data[0][j];
for (int k = 1; k < 4; k++)
r.data[i][j] += data[i][k] * m.data[k][j];
}
}
return r;
}
};
void func ()
{
matrix a, b, c;
// ...
c = a * b;
} |
(Stel even voor dat deze functie niet geinlined wordt, en dat er een operator = () is die met een for-lusje alles overzet)
In het ergste geval worden er dus 2 temporaries aangemaakt. Ten eerste die locale variabele r, en ten tweede een temporary waar het resultaat van r in komt te staan, die vervolgens aan c toegekend wordt
Een andere oplossing is zo (ik gebruik even geen templates, maar in mijn code is dat wel het geval. Daar wordt het aantal rijen en kolommen en het type per element ook met template argumenten bepaald)
C++:
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
| struct matrix;
struct matrix_mul
{
const matrix & l;
const matrix & r;
matrix_mul (const matrix & lhs, const matrix & rhs) : l (lhs), r (rhs) { }
};
struct matrix
{
float data[4][4];
inline matrix_mul operator * (const matrix & m) const
{
return matrix_mul (*this, m);
}
inline matrix & operator = (const matrix_mul & m)
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
data[i][j] = m.l.data[i][0] * m.r.data[0][j];
for (int k = 1; k < 4; k++)
data[i][j] += m.l.data[i][k] * m.r.data[k][j];
}
}
return *this;
}
}; |
De enige temporary is hier een matrix_mul structure, die gewoon weggeoptimaliseerd wordt door inlining, met als gevolg dat die operator = eigenlijk direct op de argumenten van de operator * werkt. De pipeline stall die weggewerkt wordt is mooi meegenomen, maar is in principe verwaarloosbaar