Ce n’est pas tous les jours que je lis un livre qui est aussi en phase avec ce que je pense…
Le sujet est le développement d’applications web mais cela peut très bien s’adapter à beaucoup d’autres domaines. J’aimerais voir des sociétés française qui partagent ces méthodes et cette façon de concevoir le développement logiciel! En tout cas, pour le moment, j’en ai trouvé aucune qui s’y rapproche, et c’est bien dommage!
En plus, c’est lisible gratuitement en ligne, alors je vous le conseille:
http://gettingreal.37signals.com/
This time I use my blog as a notepad in case I need to reinstall redmine on Ubuntu again. Perhaps it’ll help also some people.
Go root
su
Install ruby gem
apt-get install rubygems
Since Ubuntu 8.10 uses 1.2 an redmine needs 1.3.1, upgrade gem:
gem install rubygems-update
/var/lib/gems/1.8/bin/update_rubygems
Install rails:
gem install rails -v=2.2.2
Install mysql:
apt-get install libmysqlclient15-dev ruby1.8-dev
gem install mysql
Install apache:
apt-get install apache2
Install passenger:
apt-get install build-essential libopenssl-ruby libapr1-dev apache2-prefork-dev
gem install passenger
passenger-install-apache2-module
Add the “LoadModule” line in /etc/apache2/mods-available/passenger.load
Add the 2 following line line in /etc/apache2/mods-available/passenger.conf
(optional)Install php5:
apt-get install php5
Restart apache:
/etc/init.d/apache2 restart
Ensuite suivez les instructions à partir de ‘installation’ sur le site redmine
Installation
mai 11th, 2009 in
Développement |
++i ou i++ ? Tel est la question!
Combien de fois j’ai vu :
for(int i = 0; i < vec.size(); ++i)
{
...
}
Le plus étonnant, c’est la réponse donnée par un programmeur qui fait cela. La plupart du temps, il répond que c’est plus rapide, c’est une optimisation… Sauf que dans 99.99% des cas, si on demande combien on gagne concrètement, il ne sait pas répondre, tout simplement car c’est une optimisation pifométrique. Rien ne dit que ce code sera plus optimisé et encore moins que l’application ira plus vite.
Après, on trouve d’autres personnes qui vont plus loin :
int vsize = vec.size();
for(int i = 0; i < vsize; ++i)
{
...
}
J’ai même vu :
int vsize = vec.size();
for(; --vsize != 0;)
{
...
}
Le problème n’est pas l’optimisation en elle-même mais plutôt le fait d’optimiser au pif n’importe quelle partie du code quitte à la rendre moins lisible sans aucun gain réel de performance. Surtout que la règle des 80/20 est très souvent applicable. Seulement 20% du code prend 80% du temps CPU.
Si on veut vraiment optimiser du code, alors il faut le faire comme une tâche à part entière et suivre un processus simple:
- Bencher son application
- Détecter la fonction la plus lente
- Optimiser la fonction la plus lente
- Recommencer à l’étape 1
Au moins, on sait pourquoi on optimise, on a une bonne raison de rendre la fonction moins lisible et on peut donner le gain réel qui a été obtenu.
Parfois, ce n’est même pas en optimisant son code qu’on gagne en vitesse. Le dernier exemple en date a été une bonne leçon pour moi… Dans Ryzom, on a voulu changer la stl, plutôt que de prendre celle par défaut de Visual Studio 9, on a essayé STLport, pour voir. A la base c’était surtout pour tester en mode Debug et voir s’il détectait des problèmes au runtime (ce que STLport fait très bien).
En Release, on s’est rendu compte que le client Ryzom tournait 25% plus vite. C’est ENORME quand on sait que l’on a juste changé une librairie.
Alors s’il vous plaît, arrêtez de vous prendre la tête avec vos boucles for et autres optimisations locales et, si vous voulez vraiment optimiser, concentrez vous réellement sur ce qui rame.
When you do a svn delete, in fact the file stays in the repository history and can be restored.
I had to remove some directories from a repository because they were too big so I’ll share you the process I used:
First, you need to connect to the server where the svn repository is.
We dump the repository into a file called svn_full. It’s a text file that contains all information of the repository :
svnadmin dump /PATH/TO/THE/REPOSITORY > svn_full
Then we have to filter the dumped file to remove directories you want to remove. Read this page to understand the parameters.
cat svn_full | svndumpfilter exclude --drop-empty-revs --renumber-revs /trunk/PATH1/ /trunk/PATH2/ > svn_filtered 2>log
Now the svn_filtered files is the dump files without the paths trunk/PATH1/ and /trunk/PATH2/. Check the log file that was generated to see if all files was removed as you wanted.
We have to remove the old repository:
mv /PATH/TO/THE/REPOSITORY /PATH/TO/THE/REPOSITORY_BACKUP
Create a new empty repository:
svnadmin create /PATH/TO/THE/REPOSITORY
Fill the repository with the new files:
svnadmin load /PATH/TO/THE/REPOSITORY < svn_filtered >log
Check the generated log file to see if everything want right.
You should finally copy the old config and hook files from old repository to the new one:
cp /PATH/TO/THE/REPOSITORY_BACKUP/conf/* /PATH/TO/THE/REPOSITORY/conf/
cp /PATH/TO/THE/REPOSITORY_BACKUP/hook/* /PATH/TO/THE/REPOSITORY/hook/
All your users have to checkout the new repository with a svn checkout command.
That’s all
Finally, if you use redmine like me, you want redmine to update the new repository. You just have to clear (not drop) the following sql tables:
changes, changesets and changesets_issues. The next time someone will click on the “Repository” tab in redmine, it’ll regenerate all entries. If it’s too long and you have a timeout, go to the redmine directory and run the following command line to force the generation of the changes:
ruby script/runner "Repository.fetch_changesets" -e production
septembre 8th, 2008 in
Développement |
L’autre jour, je suis tombé sur le ticket d’un programmeur qui montrait un bout de son code, le voici :
bool SIM_FoodMemory::IsBoringChoice(std::string foodname)
{
//return true if we ate the same food more than once in the last 14 days
int recentmatches = 0;
iter it;
for(it = Items.begin(); it != Items.end(); ++it)
{
SIM_FoodMemoryItem* pitem = *it;
if(pitem->FoodName == foodname)
{
int diff = SIM_GetSim()->GetTurnCount() - pitem->TurnConsumed;
if(diff < 14)
{
recentmatches++;
}
}
}
if(recentmatches > 1)
{
return true;
}
else
{
return false;
}
}
Autant vous le dire tout de suite, ce bout de code ne me plait pas du tout. Si l’on fait une remarque sur ce genre de code, beaucoup de coders sortent leur carte anti-troll ; « les goûts et les couleurs, ça ne se discute pas » ainsi que l’autre carte souvent utilisée ; « pas grave, ce n’est pas ‘time critic’ ».
Mais est-ce que ces 2 cartes devraient tout autoriser ? Alors sous ces 2 prétextes, on peut faire tout et n’importe quoi ? Je suis désolé, mais je ne suis pas d’accord.
Prenons le cas de la classe string qui est passé en paramètre, on est loin du débat « est-ce que ++i est mieux que i++ » ! Même si la fonction n’est pas ‘time critic’, passer une classe string comme cela entraine non seulement un overhead de code mais surtout, des allocations/désallocations mémoire! Il faut allouer la classe sur la pile, appeler le ctor, allouer la mémoire pour la chaine de caractère, la copier, et, à la fin, la désalouer. En plus de prendre du temps CPU, ça fragmente la mémoire, ce qui peut avoir des effets réellement génants.
Maintenant le fait de ne pas utiliser de const ? Mettre un header SIM_ au lieu d’utiliser un namespace ? Ce code montre très clairement que c’est un ancien programmeur C et qu’il ne maitrise pas des concepts basiques du C++ et continue à utiliser ses anciennes habitudes du C.
Tout le monde fait des erreurs, tout le monde a tendance à faire comme il a toujours fait, c’est humain. Ce qui me dérange plus, c’est la réaction, plutôt que d’accepter qu’il y ait un problème et le corriger, le programmeur se réfugie derrière le “c’est pas grave, les gouts et les couleurs… c’est pas time critic…”. Comment évoluer, s’améliorer dans ces conditions ?
Pour finir, voilà comment, moi, j’aurais surement écrit ce bout de code (ce n’est pas du tout pour montrer la solution parfaite, mais juste pour jouer le jeu) :
using namespace std;
namespace SIM {
bool FoodMemory::isBoringChoice(const string &foodname) const
{
int recentmatches = 0;
for(iter it = Items.begin(); it != Items.end(); it++)
{
FoodMemoryItem *item = *it;
if(item->FoodName == foodname)
{
int diff = sim()->turnCount() - item->TurnConsumed;
if(diff < 14) recentmatches++;
}
}
return (recentmatches > 1);
}
J’ai clairement un style plus compact, j’ai fait exprès de ne pas changer l’algo sinon j’aurais fait directement un return dans le if pour casser la boucle ‘for’ le plus tôt possible.
Et comme je suis curieux, vous l’auriez écrit comment vous ?
février 25th, 2008 in
Extreme Programming |