2009-12-21 6 views
4

Voici le code que j'utilise pour créer un tableau commandé différemment:Performance des opérations de mémoire sur iPhone

const unsigned int height = 1536; 
const unsigned int width = 2048; 

uint32_t* buffer1 = (uint32_t*)malloc(width * height * BPP); 
uint32_t* buffer2 = (uint32_t*)malloc(width * height * BPP); 

int i = 0; 
for (int x = 0; x < width; x++) 
    for (int y = 0; y < height; y++) 
     buffer1[x+y*width] = buffer2[i++]; 

Quelqu'un peut-il expliquer pourquoi en utilisant l'affectation suivante:

buffer1[i++] = buffer2[x+y*width]; 

au lieu de celui de mon code prend deux fois plus de temps?

+0

Sauter entre différentes pages de mémoire? L'accès à la mémoire n'est pas une opération à temps constant, contrairement à ce que vous supposez dans la classe des algorithmes, bien que vous puissiez probablement lui imposer une limite supérieure pour une architecture donnée. –

+0

Quelques questions: D'abord, testez-vous sur une version de débogage ou de version. Deuxièmement, comment déterminez-vous le moment? – Toji

+0

Quelle est la sortie de l'assembly pour le segment de code? Cela ressemble à une absurdité d'architecture, à moins que je ne manque quelque chose d'évident. –

Répondre

4

Il est probable qu'il s'agisse d'un comportement de cache de l'UC (à 12 Mo, vos images dépassent de loin le cache L2 de 256 Ko de l'ARM Cortex A8 qui se trouve dans un iphone3gs).

Le premier exemple accède au tableau de lecture dans un ordre séquentiel, qui est rapide, mais doit accéder au tableau d'écriture dans le désordre, ce qui est lent.

Le deuxième exemple est le contraire: le tableau d'écriture est écrit dans un ordre séquentiel rapide et le tableau de lecture est accédé plus lentement. Les échecs d'écriture sont évidemment moins coûteux sous cette charge de travail que les échecs de lecture.

L'article d'Ulrich Drepper What Every Programmer Should Know About Memory est recommandé de lire si vous voulez en savoir plus sur ce genre de chose.

Notez que si vous avez cette opération enveloppé dans une fonction, alors vous aider à l'optimisateur pour générer un meilleur code si vous utilisez le qualificatif restrict sur vos arguments pointeur, comme ceci:

void reorder(uint32_t restrict *buffer1, uint32_t restrict *buffer2) 
{ 
    int i = 0; 
    for (int x = 0; x < width; x++) 
     for (int y = 0; y < height; y++) 
      buffer1[x+y*width] = buffer2[i++]; 
} 

(Le restrict qualificateur promet au compilateur que les données pointées par les deux pointeurs ne se chevauchent pas - ce qui dans ce cas est nécessaire pour que la fonction ait du sens quand même).

2

Chaque accès de pixel dans le premier a un locality of reference linéaire, le second souffle votre cache à chaque lecture devant aller à la mémoire principale pour chacun. Le processeur peut gérer beaucoup plus efficacement les écritures avec une mauvaise localité que les lectures, si l'écriture doit aller à la mémoire principale, cette écriture peut se produire en parallèle à une autre opération de lecture/arithmétique. Si une lecture rate le cache, elle peut bloquer complètement le processeur en attendant que plus de données soient filtrées à travers les hiérarchies de caches.