103 votes

La manière la plus rapide de gérer une clé de tableau indéfinie

Dans une boucle très serrée, je dois accéder à des dizaines de milliers de valeurs dans un tableau contenant des millions d'éléments. La clé peut être indéfinie: dans ce cas, il sera légal de retourner NULL sans aucun message d'erreur :

  • La clé du tableau existe : retourner la valeur de l'élément.
  • La clé du tableau n'existe pas : retourner null.

Je connais plusieurs solutions :

if (isset($lookup_table[$key])) {
    return $lookup_table[$key];
} else {
    return;
}

ou

@return $lookup_table[$key];

ou

error_reporting(0);
$return = $lookup_table[$key];
error_reporting(E_ALL);
return $return;

Toutes les solutions sont loin d'être optimales :

  • La première nécessite 2 recherches dans l'arbre B : une pour vérifier l'existence, une autre pour récupérer la valeur. Cela double effectivement le temps d'exécution.
  • La deuxième utilise l'opérateur de suppression des erreurs, et crée ainsi une surcharge massive sur cette ligne.
  • La troisième appelle le gestionnaire d'erreurs (qui vérifiera le paramètre error_reporting puis n'affichera rien) et crée ainsi une surcharge.

Aurais-je raté une façon d'éviter la gestion des erreurs et pourtant travailler avec une seule recherche dans l'arbre B ?

Pour répondre à quelques questions :

Le tableau met en cache les résultats d'un calcul complexe - trop complexe pour être fait en temps réel. Sur des milliards de valeurs possibles, seuls des millions produisent un résultat valide. Le tableau ressemble à 1234567 => 23457, 1234999 => 74361, .... Cela est enregistré dans un fichier PHP de plusieurs mégaoctets, et inclus au début de l'exécution avec include_once. Le temps de chargement initial n'a pas d'importance.

Si la clé n'est pas trouvée, cela signifie simplement que cette valeur spécifique ne renverra pas un résultat valide. Le problème est de réaliser cela 50k+ fois par seconde.

166voto

Jack Points 88446

Tout d'abord, les tableaux ne sont pas implémentés comme un arbre B, c'est une table de hachage; un tableau de seaux (indexé via une fonction de hachage), chacun avec une liste chaînée de valeurs réelles (en cas de collisions de hachage). Cela signifie que les temps de recherche dépendent de la façon dont la fonction de hachage a "réparti" les valeurs à travers les seaux, c'est-à-dire que le nombre de collisions de hachage est un facteur important.

PHP < 7

Votre déclaration initiale serait le code le plus performant, simplement parce que isset() est optimisé en tant qu'opcode VM.

PHP >= 7

Cette déclaration est la plus correcte :

return array_key_exists($key, $table) ? $table[$key] : null;

Depuis que ce changement a été fusionné, ce n'est plus un appel de fonction (mais un opcode VM) et est aussi performant (voire plus) que isset(), mais parce que vous itérez techniquement le tableau deux fois dans le pire des cas (si la clé existe), les performances sont comparables à votre idée initiale.

Alternative abrégée

Vous pouvez également accomplir ce que vous voulez avec moins de code en utilisant l' opérateur de fusion null (en gardant à l'esprit qu'il contournera l'élément de tableau existante avec une valeur null, de la même manière que isset() le ferait) :

return $table[$key] ?? null;

Cela devrait être le plus performant entre les trois options (le troisième étant votre propre suggestion).

6voto

Omar Jackman Points 6632

J'ai fait quelques tests avec le code suivant :

set_time_limit(100);

$count = 2500000;
$search_index_end = $count * 1.5;
$search_index_start = $count * .5;

$array = array();
for ($i = 0; $i < $count; $i++)
    $array[md5($i)] = $i;

$start = microtime(true);
for ($i = $search_index_start; $i < $search_index_end; $i++) {
    $key = md5($i);
    $test = isset($array[$key]) ? $array[$key] : null;
}
$end = microtime(true);
echo ($end - $start) . " secondes";

$start = microtime(true);
for ($i = $search_index_start; $i < $search_index_end; $i++) {
    $key = md5($i);
    $test = array_key_exists($key, $array) ? $array[$key] : null;
}
$end = microtime(true);
echo ($end - $start) . " secondes";

$start = microtime(true);
for ($i = $search_index_start; $i < $search_index_end; $i++) {
    $key = md5($i);
    $test = @$array[$key];
}
$end = microtime(true);
echo ($end - $start) . " secondes";

$error_reporting = error_reporting();
error_reporting(0);
$start = microtime(true);
for ($i = $search_index_start; $i < $search_index_end; $i++) {
    $key = md5($i);
    $test = $array[$key];
}
$end = microtime(true);
echo ($end - $start) . " secondes";
error_reporting($error_reporting);

$start = microtime(true);
for ($i = $search_index_start; $i < $search_index_end; $i++) {
    $key = md5($i);
    $tmp = &$array[$key];
    $test = isset($tmp) ? $tmp : null;
}
$end = microtime(true);
echo ($end - $start) . " secondes";

et j'ai trouvé que le test le plus rapide était celui qui utilise isset($array[$key]) ? $array[$key] : null, suivi de près par la solution qui désactive simplement le signalement d'erreurs.

2voto

Cela fonctionne pour moi

{{ isset($array['key']) ? $array['key']: 'Default' }} 

mais c'est rapide

{{ $array['key'] or 'Default' }}

2voto

thomasrutter Points 42905

L'opérateur @ et les méthodes error_reporting seront tous les deux plus lents que l'utilisation de isset. Avec ces deux méthodes, elles modifient le paramétrage du rapport d'erreurs pour PHP, mais le gestionnaire d'erreurs de PHP sera toujours appelé. Le gestionnaire d'erreurs vérifiera le paramétrage du rapport d'erreurs et sortira sans signaler quoi que ce soit, cependant cela a tout de même pris du temps.

1voto

Mathew Foscarini Points 6076

Il existe deux approches typiques pour cela.

  1. Définir des valeurs par défaut pour une clé non définie.
  2. Vérifier si la clé est définie ou non.

Voici comment effectuer la première avec le moins de code possible.

$data = array_merge(array($key=>false),$data);
return $data[$key];

Voici comment effectuer la deuxième approche.

return isset($data[$key]) ? $data[$key] : false;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X