Bon, alors pour "trancher" le débat sur ce qui est plus rapide/plus lent/etc, j'ai mis tout le code dans un programme [et j'ai espoir J'ai crédité la bonne personne pour le bon extrait de code].
Le code peut être trouvé ci-dessous, pour l'inspection que j'ai intepreded le code correctement quand je l'ai fait dans des fonctions. Je l'ai exécuté sans sortie correcte et j'ai vérifié que chaque fonction donne le même résultat [en gardant à l'esprit que l'ordre est légèrement différent dans certains cas - j'ai donc fait une variation pour exécuter l'autre sens de mon code, juste pour voir qu'il donne le "bon" résultat]. Donc sans plus attendre, voici les résultats :
mats1 time in clocks per iteration 10.3457
mats2 time in clocks per iteration 10.4785
mats3 time in clocks per iteration 10.5538
viraptor time in clocks per iteration 6.24603
lemees time in clocks per iteration 14.4818
npe time in clocks per iteration 13.1455
alex time in clocks per iteration 24.8272
(résultats de viraptor sur core i5, g++ 4.7)
mats1 time in clocks per iteration 7.62338
mats2 time in clocks per iteration 7.36226
mats3 time in clocks per iteration 7.45361
viraptor time in clocks per iteration 2.09582
lemees time in clocks per iteration 9.43744
npe time in clocks per iteration 7.51016
alex time in clocks per iteration 19.3554
(résultats de viraptor sur core i5, clang++ 3.2)
mats1 time in clocks per iteration 12.956
mats2 time in clocks per iteration 13.4395
mats3 time in clocks per iteration 13.3178
viraptor time in clocks per iteration 2.12914
lemees time in clocks per iteration 13.9267
npe time in clocks per iteration 16.2102
alex time in clocks per iteration 13.8705
Ce sont des cycles d'horloge sur un AMD Athlon2 à 3,4 GHz - je n'ai pas de machine Intel moderne - si quelqu'un souhaite exécuter le code sur cette machine, je serais intéressé de voir à quoi cela ressemble. Je suis presque sûr que tout fonctionne bien dans le cache - peut-être à part récupérer certaines des valeurs pour les vérifier.
Ainsi, le gagnant est clairement viraptor, d'environ 40% - "mon" code est deuxième. Le code d'Alex n'a pas de sauts/branches, mais il semble toujours fonctionner plus lentement que les autres alternatives. Je ne sais pas pourquoi les résultats de npe sont beaucoup plus lents que les miens - il fait presque la même chose (et le code semble très similaire quand on regarde la sortie assembleur de g++).
#include <iostream>
#include <fstream>
#include <cstdint>
using namespace std;
const int SIZE = 1000000;
uint64_t g_val[SIZE];
ofstream nulloutput;
static __inline__ unsigned long long rdtsc(void)
{
unsigned hi, lo;
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
#define BITA_TO_B(x, a, b) (((x) >> (a-b)) & (1 << b))
unsigned char get_col_mats1(uint64_t val, int col)
{
return BITA_TO_B(val, 56+col, 7) |
BITA_TO_B(val, 48+col, 6) |
BITA_TO_B(val, 40+col, 5) |
BITA_TO_B(val, 32+col, 4) |
BITA_TO_B(val, 24+col, 3) |
BITA_TO_B(val, 16+col, 2) |
BITA_TO_B(val, 8+col, 1) |
BITA_TO_B(val, 0+col, 0);
}
unsigned char get_col_mats2(uint64_t val, int col)
{
return BITA_TO_B(val, 63-col, 7) |
BITA_TO_B(val, 55-col, 6) |
BITA_TO_B(val, 47-col, 5) |
BITA_TO_B(val, 39-col, 4) |
BITA_TO_B(val, 31-col, 3) |
BITA_TO_B(val, 23-col, 2) |
BITA_TO_B(val, 15-col, 1) |
BITA_TO_B(val, 7-col, 0);
}
unsigned char get_col_viraptor(uint64_t board, int col) {
const uint64_t column_mask = 0x8080808080808080ull;
const uint64_t magic = 0x2040810204081ull ;
uint64_t column = board & (column_mask >> col);
column <<= col;
column *= magic;
return (column >> 56) & 0xff;
}
unsigned char get_col_alex(uint64_t bitboard, int col)
{
unsigned char result;
result |= (bitboard & (1ULL << 63-col)) ? 0x80 : 0;
result |= (bitboard & (1ULL << 55-col)) ? 0x40 : 0;
result |= (bitboard & (1ULL << 47-col)) ? 0x20 : 0;
result |= (bitboard & (1ULL << 39-col)) ? 0x10 : 0;
result |= (bitboard & (1ULL << 31-col)) ? 0x08 : 0;
result |= (bitboard & (1ULL << 23-col)) ? 0x04 : 0;
result |= (bitboard & (1ULL << 15-col)) ? 0x02 : 0;
result |= (bitboard & (1ULL << 7-col)) ? 0x01 : 0;
return result;
}
unsigned char get_col_lemees(uint64_t val, int column)
{
int result = 0;
int source_bitpos = 7 - column; // "point" to last entry in this column
for (int target_bitpos = 0; target_bitpos < 8; ++target_bitpos)
{
bool bit = (val >> source_bitpos) & 1; // "extract" bit
result |= bit << target_bitpos; // add bit if it was set
source_bitpos += 8; // move one up in table
}
return result;
}
int get(uint64_t board, int row, int col) {
return (board >> (row * 8 + col)) & 1;
}
uint8_t get_col_npe(uint64_t board, int col) {
uint8_t ret = 0;
for (int i = 0; i < 8; ++i) {
ret = (ret << 1) + get(board, i, col);
}
return ret;
}
#define BITA_TO_B2(x, a, b) (((x) >> (a-b)) & (1 << b))
unsigned char get_col_mats3(uint64_t val, int col)
{
return BITA_TO_B2(val, 63-col, 7) |
BITA_TO_B2(val, 55-col, 6) |
BITA_TO_B2(val, 47-col, 5) |
BITA_TO_B2(val, 39-col, 4) |
BITA_TO_B2(val, 31-col, 3) |
BITA_TO_B2(val, 23-col, 2) |
BITA_TO_B2(val, 15-col, 1) |
BITA_TO_B2(val, 7-col, 0);
}
template<unsigned char (*f)(uint64_t val, int col)>
void runbench(const char *name)
{
unsigned char col[8] = {0};
uint64_t long t = rdtsc();
for(int j = 0; j < SIZE; j++)
{
uint64_t val = g_val[j];
for(int i = 0; i < 8; i++)
{
col[i] += f(val, i);
}
// __asm__ __volatile__("":::"memory");
}
t = rdtsc() - t;
for(int i = 0; i < 8; i++)
{
nulloutput<< "col " << i << " has bits " << hex << (int)col[i] << endl;
}
cout << name << " time in clocks per iteration " << dec << t / (8.0 * SIZE) << endl;
}
#define BM(name) void bench_##name() { runbench<get_col_##name>(#name); }
BM(mats1);
BM(mats2);
BM(mats3);
BM(viraptor);
BM(lemees);
BM(npe);
BM(alex);
struct function
{
void (*func)(void);
const char *name;
};
#define FUNC(f) { bench_##f, #f }
function funcs[] =
{
FUNC(mats1),
FUNC(mats2),
FUNC(mats3),
FUNC(viraptor),
FUNC(lemees),
FUNC(npe),
FUNC(alex),
};
int main()
{
unsigned long long a, b;
int i;
int sum = 0;
nulloutput.open("/dev/nul");
for(i = 0; i < SIZE; i++)
{
g_val[i] = rand() + ((long)rand() << 32L);
}
unsigned char col[8];
for(i = 0; i < sizeof(funcs)/sizeof(funcs[0]); i++)
{
funcs[i].func();
}
}
4 votes
Est-ce que vous venez vraiment de demander "est-ce que c'est faisable" ? Vous attendez-vous sérieusement à ce que la réponse soit "non, c'est totalement impossible" ?
1 votes
@KerrekSB Non, en fait je demande comment ... lol.
1 votes
Bien sûr que c'est possible. Pour ce faire, il faut un peu de décalage, et et ou. En fonction de ce que vous voulez réellement faire [ou est-ce un exercice scolaire ?], il peut y avoir différents types de "trucs intelligents" qui peuvent être appliqués, mais les décalages, les et et les ou sont probablement ce que vous voulez. Voulez-vous que je poste une réponse avec cela ?
0 votes
C'est juste une table de vérité...
0 votes
J'imagine que vous pourriez avoir une fonction
get(board, row, col)
renvoyant le bit correspondant. Vous pourriez ensuite l'appeler dans une boucle et combiner les résultats en un seul octet. Cela ne serait-il pas, d'une certaine manière, insatisfaisant ?1 votes
@NPE Oui, c'est vrai. Mon objectif est de ne pas utiliser de boucles du tout ; je cherche en fait le moyen le plus rapide possible.
0 votes
@MatsPetersson Cela n'a rien à voir avec les devoirs à domicile. Et les quarts de travail, etc. sont acceptés. Cependant, je cherche la solution la plus rapide possible. (cette fonction va être exécutée quelques millions de fois par seconde, donc la vitesse est cruciale ;-))
0 votes
Vous devez être plus clair. Quand vous dites "isoler", voulez-vous (1) les 8 bits sélectionnés dans les 8 bits inférieurs du résultat ou (2) les 8 bits dans leur position originale et tous les bits non sélectionnés à 0 ? Quand je jouais avec le programme d'échecs 0.5, il utilisait la deuxième signification.
1 votes
@Dr.Kameleon : Les boucles et le plus rapide possible ne sont pas nécessairement mutuellement exclusifs. Voir ma réponse.