2016-04-12 5 views
1

Dites que j'ai un pointeur vers la mémoire dans rsi, et je voudrais charger la valeur de 12 octets pointé dans les 96 bits bas de xmm0. Je me fous de ce qui arrive à les 32 bits élevés. Qu'est-ce qu'un moyen efficace de faire cela?Comment charger 96 bits de la mémoire dans un registre XMM?

(question Side: le meilleur que je suis venu avec la movlpd implique « Déplacer bas paniers à double précision en virgule flottante Valeur » instruction Est-il possible dans ces instructions qui sont spécifiques aux valeurs à virgule flottante. ? Je ne comprends pas ce qu'il est documenté cette façon,. sûrement il devrait fonctionner pour les entiers aussi)

Répondre

2

Si une charge de 16 octets ne franchira pas dans une autre page et défaut, utilisez movups. Les 4 octets hauts seront ce qu'il y a de déchets dans la mémoire. Causer un manque de mémoire cache pour le 4B dont vous ne tenez pas compte peut être un problème, tout comme la séparation de la ligne de cache.

Sinon, utilisez movq/pinsrd (SSE4.1), ou un autre moyen de faire deux charges + une lecture aléatoire. movq + pinsrd va être 3 uops à domaine fusionné sur les processeurs Intel SnB, car pinsrd ne peut pas microfusionner. (Et son ALU uop nécessite le port shuffle (p5)).


Une autre possibilité: AVX VMASKMOVPS xmm1, xmm2, m128.

déplace conditionnellement éléments de données condensées à partir du second opérande de source dans l'élément de données correspondant de l'opérande de destination, en fonction des bits de masquage associés à chaque élément de données (MSB du premier opérande src).

... Les défauts ne se produit en raison de référence à tout emplacement de mémoire si le bit de masque correspondant à cet emplacement de mémoire est 0.

Intel Haswell: 3 uops-domaine fusionné (une charge et deux aléatoire (p5)). Latence 4c, un débit par 2c.

Ce n'est probablement pas très bon comparé, esp. si le code environnant doit mélanger.


Votre branche conditionnelle très rarement prises qui utilise movups tout temps il est garanti de ne pas la faute est aussi 3 UOP-domaine fusionné sur le chemin rapide, et l'un d'entre eux peut fonctionner sur port6 ​​(pas en concurrence avec vecteur ALU du tout). Le LEA n'est pas sur le chemin critique non plus.


movlpd est sûr à utiliser sur toutes les données. Il ne sera jamais en faute ou sera lent avec des données qui représentent un NaN en virgule flottante, ou quelque chose comme ça. Vous n'avez qu'à vous soucier de cela avec des instructions qui sont listées dans le manuel d'insn ref avec une section non-vide "SIMD Floating-Point Exceptions". par exemple. addps peut générer des exceptions "Dépassement, Sous-flux, Invalide, Précision, Dénormal", mais shufps indique "Aucun".

+0

Malheureusement, je ne contrôle pas la position ou les dimensions de l'entrée , donc je ne peux pas dépasser. 'movq' et' pinsrd' est ce que j'ai trouvé aussi; merci de confirmer. – jacobsa

+0

Merci également pour la note sur 'movlpd'. Mais ma question est la suivante: pourquoi est-il documenté comme s'appliquant aux valeurs à virgule flottante en particulier? – jacobsa

+0

@jacobsa: Si vous savez qu'il y a une mémoire lisible * avant * le 12B que vous voulez, vous pouvez charger à partir de [[addr-4]], puis décaler ("psrldq"). Ou vous pouvez même masquer l'adresse pour obtenir un pointeur 16B-aligné qui couvre certaines des données que vous voulez (et ne peut toujours pas faute). –

1

La réponse de Pierre Cordes aidé en me faisant penser à des pages, et j'enroulé juste vérifier s'il y avait une chance que nous serions faute:

// We'd like to perform only a single load from memory, but there's no 96-bit 
// load instruction and it's not necessarily safe to load the full 128 bits 
// since this may read beyond the end of the buffer. 
// 
// However, observe that memory protection applies with granularity of at 
// most 4 KiB (the smallest page size). If the full 16 bytes lies within a 
// single 4 KiB page, then we're fine. If the 12 bytes we are to read 
// straddles a page boundary, then we're also fine (because the next four 
// bytes must lie in the second page, which we're already reading). The only 
// time we're not guaranteed to be okay to read 16 bytes is if the 12 bytes 
// we want to read lie near the end of one page, and some or all of the 
// following four bytes lie within the next page. 
// 
// In other words, the only time there's a risk is when the pointer mod 4096 
// is in the range [4081, 4085). This is <0.1% of addresses. Check for this 
// and handle it specially. 
// 
// We perform the check by adding 15 and then checking for the range [0, 3). 
lea rax, [rsi+15] 
test eax, 0xffc 
jz slow_read 

// Hooray, we can load from memory just once. 
movdqu xmm0, XMMWORD PTR [rsi] 

done_reading: 
[...] 

slow_read: 
movq xmm1, QWORD PTR [rsi] 
pinsrd xmm1, DWORD PTR [rsi+8], 2 
jmp done_reading 
+0

plus rapide: 'lea eax, [rsi + 15]'/'test eax, 0xffc' /' jz'. Puisque vous êtes uniquement intéressé par les octets faibles, vous n'avez pas besoin de regs 64 bits, sauvegardant des octets sur les préfixes REX. J'ai utilisé eax car il y a un encodage spécial de 'test eax, imm32'. Vous * pouvez * modifier ceci pour utiliser 'slow_read' pour les divisions de la ligne de cache aussi, mais vous perdrez probablement beaucoup plus sur les erreurs de branchement que vous ne le feriez. Vous devriez tester cela sans condition en utilisant 'movq' /' pinsrd' pour vous assurer que c'est mieux. 'lea' /' fusionné test-and-branch'/'movups' est 3 uops, même chose que' movq'/'pinsrd', mais chemin critique plus court. –

+0

Bon vieux 'lea', comment l'ai-je oublié? Merci. Mon benchmark n'est pas assez sensible pour faire la différence ici (ou ce n'est pas un goulot d'étranglement), mais votre patch est plus agréable. Terminé. – jacobsa

+0

'lea EAX, [rsi + 15]' enregistre un autre octet dans l'encodage. La taille d'adresse par défaut est 64 bits, mais la taille d'opérande par défaut est 32 bits, même pour LEA. De plus, c'est bizarre que 'movdqu' se charge dans un registre différent de celui de' movq'/'pinsrd'. (En outre, vous pouvez enregistrer un octet avec 'movups. 'Il n'y a aucun inconvénient à utiliser des chargements/magasins FP, et clang fait parfois cela.Utilisez le bon type insn pour les données pour les mouvements reg-reg, cependant, depuis quelques uarches se soucient de ça.) –