Existe-t-il memset () qui accepte des entiers plus grands que char?

Existe-t-il une version de memset () qui définit une valeur supérieure à 1 octet (caractère)? Par exemple, supposons que nous ayons une fonction memset32 (). Pour l’utiliser, nous pouvons faire ce qui suit:

int32_t array[10]; memset32(array, 0xDEADBEEF, sizeof(array)); 

Cela définira la valeur 0xDEADBEEF dans tous les éléments du tableau. Actuellement, il me semble que cela ne peut être fait qu’avec une boucle.

Plus précisément, je suis intéressé par une version 64 bits de memset (). Savoir quelque chose comme ça?

 void memset64( void * dest, uint64_t value, uintptr_t size ) { uintptr_t i; for( i = 0; i < (size & (~7)); i+=8 ) { memcpy( ((char*)dest) + i, &value, 8 ); } for( ; i < size; i++ ) { ((char*)dest)[i] = ((char*)&value)[i&7]; } } 

(Explication, comme demandé dans les commentaires: lorsque vous assignez un pointeur, le compilateur suppose que le pointeur est aligné sur l'alignement naturel du type; pour uint64_t, cela correspond à 8 octets. Memcpy () n'émet aucune hypothèse de ce type. Sur certains matériels non alignés les access étant impossibles, l’atsortingbution n’est pas une solution appropriée, à moins que vous ne sachiez que les access non alignés fonctionnent sur le matériel avec peu ou pas de pénalité, ou que vous ne le ferez jamais, ou les deux. s avec un code plus approprié pour qu’elle ne soit pas aussi horrible qu’elle soit, mais si vous en savez suffisamment pour garantir que l’affectation fonctionnera toujours et que votre profileur vous dira que c’est plus rapide, vous pouvez remplacer la mémoire par une affectation. La boucle est présente au cas où la quantité de mémoire à remplir ne serait pas un multiple de 64 bits. Si vous savez qu'elle le sera toujours, vous pouvez simplement supprimer cette boucle.)

Il n’y a pas de fonction de bibliothèque standard autant que je sache. Donc, si vous écrivez du code portable, vous regardez en boucle.

Si vous écrivez du code non-portable, consultez la documentation de votre compilateur / de votre plate-forme, mais ne retenez pas votre souffle car il est rare que de l’aide soit fournie ici. Peut-être que quelqu’un d’autre donnera des exemples de plates-formes qui fournissent quelque chose.

La façon dont vous écrivez vous-même dépend de la possibilité de définir dans l’API que l’appelant garantit que le pointeur dst sera suffisamment aligné pour les écritures 64 bits sur votre plate-forme (ou les plates-formes si elles sont portables). Sur toutes les plates-formes possédant un type entier de 64 bits, malloc au moins renverra des pointeurs correctement alignés.

Si vous devez faire face au non-alignement, vous avez besoin de quelque chose comme la réponse de Moonshadow. Le compilateur peut aligner / dérouler cette mémoire avec une taille de 8 (et utiliser des opérations d’écriture non alignées 32 ou 64 bits s’ils existent), le code devrait donc être assez simple, mais j’imagine qu’il ne fera probablement pas de cas spécial toute la fonction pour la destination étant alignée. J’aimerais être corrigé, mais craignez que je ne le serai pas.

Donc, si vous savez que l’appelant vous donnera toujours un dst avec un alignement suffisant pour votre architecture et une longueur multiple de 8 octets, faites une simple boucle en écrivant un uint64_t (ou quel que soit le paramètre int de 64 bits dans votre compilateur) et vous obtiendrez probablement un code plus rapide (sans promesse). Vous aurez certainement un code plus court.

Quel que soit le cas, si vous vous souciez de la performance, profilez-la. Si ce n’est pas assez rapide, essayez à nouveau avec plus d’optimisation. Si ce n’est toujours pas assez rapide, posez une question sur une version asm pour le (s) processeur (s) sur laquelle elle n’est pas assez rapide. memcpy / memset peut obtenir des gains de performances énormes grâce à l’optimisation par plateforme.

Consultez la documentation de votre système d’exploitation pour connaître la version locale, puis envisagez simplement d’utiliser la boucle.

Le compilateur en sait probablement plus que vous sur l’optimisation de l’access à la mémoire pour une architecture particulière, laissez-le donc faire le travail.

Enveloppez-le en tant que bibliothèque et comstackz-le avec toutes les optimisations d’amélioration de la vitesse permises par le compilateur.

Pour mémoire, ce qui suit utilise memcpy(..) dans le motif suivant. Supposons que nous voulions remplir un tableau avec 20 entiers:

 -------------------- First copy one: N------------------- Then copy it to the neighbour: NN------------------ Then copy them to make four: NNNN---------------- And so on: NNNNNNNN------------ NNNNNNNNNNNNNNNN---- Then copy enough to fill the array: NNNNNNNNNNNNNNNNNNNN 

Cela prend O (lg (num)) applications de memcpy(..) .

 int *memset_int(int *ptr, int value, size_t num) { if (num < 1) return ptr; memcpy(ptr, &value, sizeof(int)); size_t start = 1, step = 1; for ( ; start + step <= num; start += step, step *= 2) memcpy(ptr + start, ptr, sizeof(int) * step); if (start < num) memcpy(ptr + start, ptr, sizeof(int) * (num - start)); return ptr; } 

Je pensais que cela pourrait être plus rapide qu'une boucle si memcpy(..) était optimisé en utilisant une fonctionnalité de copie de mémoire matérielle en bloc, mais il s'avère qu'une boucle simple est plus rapide que celle ci-dessus avec -O2 et -O3. (Au moins en utilisant MinGW GCC sur Windows avec mon matériel particulier.) Sans le commutateur -O, sur un tableau de 400 Mo, le code ci-dessus est environ deux fois plus rapide qu'une boucle équivalente et prend 417 ms sur ma machine, tandis que l'optimisation les deux vont à environ 300 ms. Ce qui signifie qu'il faut environ le même nombre de nanosecondes que d'octets et qu'un cycle d'horloge dure environ une nanoseconde. Donc, soit il n’existe pas de fonctionnalité de copie de mémoire de bloc matériel sur ma machine, soit l’ memcpy(..) n’en tire pas parti.

wmemset(3) est la version large (16 bits) de memset. Je pense que c’est le plus proche que vous allez obtenir en C, sans boucle.

Si vous ne ciblez qu’un compilateur x86, vous pouvez essayer quelque chose comme (exemple avec VC ++):

 inline void memset32(void *buf, uint32_t n, int32_t c) { __asm { mov ecx, n mov eax, c mov edi, buf rep stosd } } 

Sinon, faites simplement une boucle et faites confiance à l’optimiseur pour savoir ce qu’il fait, par exemple:

 for(uint32_t i = 0;i < n;i++) { ((int_32 *)buf)[i] = c; } 

Si vous compliquez les choses, il y aura des chances que le code soit plus lent que l'optimisation, sans parler de la maintenance.

Vous devriez vraiment laisser le compilateur optimiser ceci pour vous comme quelqu’un d’autre l’a suggéré. Dans la plupart des cas, cette boucle sera négligeable.

Mais si cela ne vous dérange pas d’être spécifique à une plate-forme et que vous avez vraiment besoin de vous débarrasser de la boucle, vous pouvez le faire dans un bloc d’assemblage.

 //pseudo code asm { rep stosq ... } 

Vous pouvez probablement utiliser la commande google stosq assembly pour les détails. Cela ne devrait pas être plus que quelques lignes de code.

écrivez votre propre; c’est sortingvial même en asm.