OVH Cloud OVH Cloud

c , c++ et les jeu video???

177 réponses
Avatar
elekis
bonjour,

j'ai entendu que les jeux videos de nos jours, sont toujours concu en c
et non en c++,
ma quesiton est POUrquoi??, le c++ a quand meme beaucoup plus d'avantage
que le c non

merci


Ps: ceci n'est pas un troll; je suis en premiere année informatique, et
je commence a apprendre le c++, et les differences du c, et etant
passionné de jeu (c'est la dedans que j'aimerais travailler) je me
posait cette question

merci

a++++

10 réponses

Avatar
Cyrille Karmann
Christophe Lephay disait...
"Cyrille Karmann" a écrit dans le message de
news:
Le rtti est très souvent activé par défaut sur les compilateurs. Et le
programmeur débutant qui apprendra bien plus tard ce qu'est le rtti se
retrouve sans comprendre pourquoi avec un exécutable faisant "Hello,
World!" qui est cinquante fois plus gros que son équivalent en C.

Je crois que c'est un peu là le problème: il y a dans C++ un certains
nombres de pièges à performances qu'on apprend à éviter mais dans lequel
tombe systématiquement le programmeur débutant. Il en ressort alors avec
l'impression fausse que "le C++ c'est plus lent".

Par exemple, quelqu'un qui veut faire un moteur 3D fera peut-être une
classe Vector pour représenter les vecteurs. Intuitivement, il fera
quelque chose comme ceci pour les opérations mathématiques:


Intuitivement, j'attendrais de quelqu'un qui veut faire un moteur 3D utilise
un langage qu'il connait bien...


Je parlais du programmeur débutant qui veut refaire Doom 3 tout seul sur
son PC, tout en en profitant pour apprendre le C++, et qui va sans doute
apprendre ainsi à mesurer ses ambitions ;-).

// operator addition grossier
Vector operator + (Vector const&a, Vector const &b)
{
return Vector(a.x+b.x, a.y+b.y, a.z+b.z);
}

Cette implémentation comporte un piège: la création d'un objet
temporaire qui va être créé et détruit et donc entrainer une perte de
temps. Si ensuite il écrit une ligne du genre:

Vector ret = a+b+c+d;

3 objets temporaires seront construits et détruits et prendront de la
ressource cpu. L'optimisation du compilateur n'y change pas grand chose,
surtout si les fonctions ne sont pas inline.


L'optimisation du compilateur n'y change pas grand chose peut-être, si ce
n'est d'éviter la création de ces temporaires...


Euh... chez moi ce code là:

#include <iostream>

struct Vector
{
public:
float x,y,z;
Vector() :x(0), y(0), z(0) {}

Vector(float x, float y, float z) : x(x), y(y), z(z) { /*cout <<
"constructeur xyz" << endl;*/ }

~Vector() { std::cout << "destructeur" << endl; }
};


Vector operator + (Vector const &a, Vector const &b)
{
return Vector(a.x+b.x, a.y+b.y, a.z+b.z);
}


int main ()
{
Vector a,b,c,d;
Vector ret = a+b+c+d;
}

... appelle 7 fois le destructeur de Vector, soit deux fois de trop (gcc
3.2).

L'alternative la plus simple est alors d'écrire:
void AdditionneVecteurs(Vector *out, Vector const *in1, Vector const
*in2);
(ou l'équivalent avec des références à la place des pointeurs. Ca change
rien)


Ce qui change, c'est que la signature devient illisible, que tu vas soit
devoir déréférencer tes objets systématiquement pour les transmettre à la
fonction, soit (plus probablement hélas) utiliser des pointeur partout dans
ton code...


Je voulais dire: ca change rien à la performance de l'appel à la
fonction en question.

Mais ça c'est du pur C. Conclusion rapide: le C est plus rapide que le
C++. Or en fait la bonne conclusion devrait être: le bon C est plus
rapide que le mauvais C++. Le fait que beaucoup de programmeurs ayant à
peine touché le C++ fasse l'amalgame est à mon avis une cause de cette
mauvaise réputation du C++ pour les jeux.


Ta conclusion est juste bien que tes postulats soient faux (que le second
code est plus rapide que le premier)...


Bon, de toute manière le mieux est encore celui-là:

void AddVectorArray(Vector *out, Vector const *in1, Vector const *in2,
unsigned int count);

Qui additionne count vecteurs dans un tableau et peut ainsi tirer profit
d'instructions processeurs optimisées, comme le SSE d'Intel, tout en ne
coutant qu'un seul appel de fonction.

--
Cyrille


Avatar
Fred
"Cyrille Karmann" a écrit dans le message de news:

Fred disait...

"Cyrille Karmann" a écrit dans le message de news:


L'alternative la plus simple est alors d'écrire:
void AdditionneVecteurs(Vector *out, Vector const *in1, Vector const
*in2);
(ou l'équivalent avec des références à la place des pointeurs. Ca
change



rien)


const est un mot cle C?


Aux dernières nouvelles, c'est un mot clé de C.


Tu m'apprends un truc la.
Merci

Fred



Avatar
Cyrille Karmann
Fred disait...

"Cyrille Karmann" a écrit dans le message de news:


L'alternative la plus simple est alors d'écrire:
void AdditionneVecteurs(Vector *out, Vector const *in1, Vector const
*in2);
(ou l'équivalent avec des références à la place des pointeurs. Ca
change



rien)


const est un mot cle C?


Aux dernières nouvelles, c'est un mot clé de C.


Tu m'apprends un truc la.


Je crois que c'est dans la norme depuis C89.
Il peut encore y avoir des compilateurs qui ne le supportent pas.

--
Cyrille




Avatar
Richard Delorme

Richard Delorme wrote:
Bien sûr un programme C compatible C++ fonctionne à la même vitesse que
le compilateur soit en mode C ou C++. Néanmoins les approches offertes
par le C++, comme la programmation objet, générique,... peuvent conduire
à un programme légèrement plus lent,


Heuh... En quoi la généricité rendrait-elle plus lent ?


Parce qu'elle prend en compte tous les cas de figure, tandis que, le plus
souvent, on n'a que des cas particuliers à traiter. Un exemple, dans
l'exemple suivant, je somme le nombre de bits qu'il y a dans les entiers de
0 à 999999999 :

En C :

-8<--------------------------------------------------------
#include <stdio.h>

static inline int count_bits(unsigned long value){
unsigned long n = value - ((value >> 1) & 0x55555555);
n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
n = (n & 0x0F0F0F0F) + ((n >> 4) & 0x0F0F0F0F);
n = (n & 0xFFFF) + (n >> 16);
n = (n & 0xFF) + (n >> 8);

return (int) n;
}

int main(void)
{
double c = 0.0;
for(unsigned long i = 0; i < 1000000000; i++)
c += count_bits(i);

printf("count = %.0fn", c);

return 0;
}
-8<--------------------------------------------------------
$ time ./bits
count = 14846928128

real 0m8.288s
user 0m8.220s
sys 0m0.070s

En C++
-8<--------------------------------------------------------
#include <iostream>
#include <bitset>

int main()
{
double c = 0.0;

for (unsigned long i = 0; i < 1000000000; i++)
c += std::bitset<32>(i).count();

std::cout << "count = " << c << std::endl;
return 0;
}
-8<--------------------------------------------------------

$ time ./bits++
count = 1.48469e+10

real 0m34.239s
user 0m33.860s
sys 0m0.220s


Chez moi (gcc 3.2.3 sous linux), la version en C est 4 fois plus rapide.
Le bitset a plusieurs avantages : la fonction pour compter les bits est déjà
écrite, et elle fonctionne sur toute taille de bits. Mais, pour la
performance sur ce cas particulier, ce n'est pas ça. D'une part
l'initialisation du bitset, inutile dans la version C, ralentit le
programme; d'autre part, l'algorithme utilisé par gcc (comptage à partir
d'une table) est relativement plus lent sur ma machine, mais à l'avantage
d'être plus souple pour un nombre quelconque de bits.

Quand on recherche la performance, il faut toujours tenir compte des données
particulières du problème et utiliser l'algorithme le plus adapté pour
cela. La présence d'un algorithme tout-cuit dans la STL va naturellement
conduire à son utilisation et non à rechercher quelquechose de plus
performant.


Quand à l'objet, je vois pas non plus pourquoi il serait
plus lent (à moins de, comme aiment à le faire certains
en découvrant C++, d'ajouter du virtual partout).


Un des problèmes de l'objet, c'est qu'il y a des appels de fonction
implicite, fait à l'insu du plein gré du programmeur (constructeur,
destructeur, ...), en particulier lorsque des objets temporaires sont
créés. En C++ orienté objet, on peut donc avoir des pertes de performances
sans en être réellement conscient. La programmation en C pur est plus
explicite.

En plus, le système est souvent (Unix + X11, Windows, openGL, ...) écrit
en C à la base, et l'utilisation du C++ invite à écrire une surcouche
objet dessus qui introduit un peu de lenteur.


Hum... La encore, je demande à voir pourquoi une surcouche objet
introduirait quoi que ce soit de lenteur.


A mon avis, tu n'a pas idée de la taille d'une surcouche. Prends l'exemple
de Qt/X11 :

Pour dessiner un rectangle sous X11, on utilise la fonction :
XDrawRectangle(display, d, gc, x, y, width, height)
Display *display;
Drawable d;
GC gc;
int x, y;
unsigned int width, height;


Sous Qt (version 3.1.2) l'épaisse surcouche ressemble à :

void QPainter::drawRect( int x, int y, int w, int h )
{
if ( !isActive() )
return;
if ( testf(ExtDev|VxF|WxF) ) {
if ( testf(ExtDev) ) {
QPDevCmdParam param[1];
QRect r( x, y, w, h );
param[0].rect = &r;
if ( !pdev->cmd( QPaintDevice::PdcDrawRect, this, param ) || !hd
)
return;
}
if ( txop == TxRotShear ) { // rotate/shear polygon
QPointArray pa = xmat.mapToPolygon( QRect(x, y, w, h) );
pa.resize( 5 );
pa.setPoint( 4, pa.point( 0 ) );
drawPolyInternal( pa );
return;
}
map( x, y, w, h, &x, &y, &w, &h );
}
if ( w <= 0 || h <= 0 ) {
if ( w == 0 || h == 0 )
return;
fix_neg_rect( &x, &y, &w, &h );
}
if ( cbrush.style() != NoBrush ) {
if ( cpen.style() == NoPen ) {
XFillRectangle( dpy, hd, gc_brush, x, y, w, h );
return;
}
int lw = cpen.width();
int lw2 = (lw+1)/2;
if ( w > lw && h > lw )
XFillRectangle( dpy, hd, gc_brush, x+lw2, y+lw2, w-lw-1, h-lw-1
);
}
if ( cpen.style() != NoPen )
XDrawRectangle( dpy, hd, gc, x, y, w-1, h-1 );
}

Elle est sans doute plus facile d'utilisation, plus robuste, et tout ce
qu'on veut ; mais, vu tout ce qu'il y a autour de la fonction X11, je ne
vois pas comment elle peut-être aussi rapide.

--
Richard


Avatar
Gabriel Dos Reis
Richard Delorme writes:

|
| > Richard Delorme wrote:
| >> Bien sûr un programme C compatible C++ fonctionne à la même vitesse que
| >> le compilateur soit en mode C ou C++. Néanmoins les approches offertes
| >> par le C++, comme la programmation objet, générique,... peuvent conduire
| >> à un programme légèrement plus lent,
| >
| > Heuh... En quoi la généricité rendrait-elle plus lent ?
|
| Parce qu'elle prend en compte tous les cas de figure,

dans ce cas ce n'est pas de généricité : c'est de la masturbation
intellectuelle.

La généricité, c'est écrire du code général *pour un ensemble de
critères données*. En particulier, lorsqu'on spécialise on doit
retrouver au moins la même performance que si on avait écrit le code
directement.

[...]

| Sous Qt (version 3.1.2) l'épaisse surcouche ressemble à :

cela en dit long sur la couche Qt, mais c'est à peu près tout.

-- Gaby
Avatar
Alain Naigeon
"Richard Delorme" a écrit dans le message news:
3f819e98$0$24668$

Parce qu'elle prend en compte tous les cas de figure, tandis que, le plus
souvent, on n'a que des cas particuliers à traiter.


C'est sûr que le but de template, c'est d'éviter la situation
"que des cas particuliers à traiter".
Il me semble que les bons auteurs insistent sur la possibilité
de spécialiser un template lorsqu'il y a de bonnes raisons.
En revanche, pour une classe de la STL, j'avoue ne l'avoir
jamais fait, et je ne sais si c'est toujours faisable ou même
permis.

Un des problèmes de l'objet, c'est qu'il y a des appels de fonction
implicite, fait à l'insu du plein gré du programmeur (constructeur,
destructeur, ...), en particulier lorsque des objets temporaires sont
créés.


Là je ne comprends pas : le constructeur alloue - ce que C doit
faire (*) aussi ! Ensuite, il fait ce qu'on a écrit, et dans le cas d'un
constructeur vide de code utilisateur, je serais étonné qu'un bon
compilateur génère du superflu. Enfin, si tu codes quelque chose
à bon escient dans le constructeur, c'est que, d'une façon ou d'une
autre, tu le coderas aussi en C !

(*) : ou, plus exactement faire sous-traiter par l'OS, ce qui, AMHA,
est déjà sensiblement plus long qu'un appel de fonction "normal".

--

Français *==> "Musique renaissance" <==* English
midi - facsimiles - ligatures - mensuration
http://anaigeon.free.fr | http://www.medieval.org/emfaq/anaigeon/
Alain Naigeon - - Strasbourg, France

Avatar
Fabien LE LEZ
On Mon, 6 Oct 2003 18:15:42 +0200, Cyrille Karmann
wrote:

~Vector() { std::cout << "destructeur" << endl; }




Vector ret = a+b+c+d;
}

... appelle 7 fois le destructeur de Vector, soit deux fois de trop (gcc
3.2).


Oui, mais là le destructeur a un effet de bord, donc le compilo ne
peut pas optimiser en virant certains temporaires.

--
http://www.giromini.org/usenet-fr/repondre.html

Avatar
Fabien LE LEZ
On Mon, 6 Oct 2003 16:59:29 +0200, "Chewee"
wrote:

Je travaille moi aussi dans le jeu vidéo et c'est ce que j'ai toujours
entendu...


Le C est meilleur que le C++, je le dis depuis 20 ans ;-)

--
http://www.giromini.org/usenet-fr/repondre.html

Avatar
Cyrille Karmann
Fabien LE LEZ disait...
On Mon, 6 Oct 2003 18:15:42 +0200, Cyrille Karmann
wrote:

~Vector() { std::cout << "destructeur" << endl; }




Vector ret = a+b+c+d;
}

... appelle 7 fois le destructeur de Vector, soit deux fois de trop (gcc
3.2).


Oui, mais là le destructeur a un effet de bord, donc le compilo ne
peut pas optimiser en virant certains temporaires.


HAHA! C'est donc pas si simple! ;-)

--
Cyrille


Avatar
Fabien LE LEZ
On Mon, 6 Oct 2003 22:53:52 +0200, Cyrille Karmann
wrote:

Oui, mais là le destructeur a un effet de bord, donc le compilo ne
peut pas optimiser en virant certains temporaires.


HAHA! C'est donc pas si simple! ;-)


L'optimisation ne doit pas changer le comportement du programme.
Mais bon, de toutes façons on ne peut pas utiliser les iostream dans
un destructeur (du moins pas sans précautions), puisqu'ils peuvent
lancer des exceptions, et une exception dans un destructeur est un
comportement indéfini.

--
http://www.giromini.org/usenet-fr/repondre.html