source

PHP: 정의되지 않은 어레이 키를 처리하는 가장 빠른 방법

factcode 2022. 9. 22. 00:08
반응형

PHP: 정의되지 않은 어레이 키를 처리하는 가장 빠른 방법

매우 엄격한 루프에서 수백만 개의 요소를 포함하는 배열의 수만 개의 값에 액세스해야 합니다.키는 정의되지 않을 수 있습니다.이 경우 오류 메시지 없이 NULL을 반환하는 것이 합법적입니다.

배열 키가 있습니다. 요소의 값을 반환합니다.어레이 키가 없습니다. null을 반환하십시오.

여러 가지 솔루션을 알고 있습니다.

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

또는

@return $lookup_table[$key];

또는

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

모든 솔루션이 최적의 솔루션과는 거리가 멀다.

  • 첫 번째는 B-TREE에서 2개의 룩업이 필요합니다.하나는 존재 확인용이고 다른 하나는 값을 취득하기 위한 것입니다.그러면 실행 시간이 효과적으로 2배가 됩니다.
  • 두 번째는 에러 억제 연산자를 사용하기 때문에 그 회선에 대량의 오버헤드가 발생합니다.
  • 세 번째는 에러 핸들러를 호출하여(error_reporting 설정을 체크하고 아무것도 표시하지 않음) 오버헤드를 생성합니다.

질문입니다만, 에러 처리를 회피할 수 있는 방법을 놓쳤는데, Btree lookup을 1개만 사용할 수 있을까요?

몇 가지 질문에 답하려면:

어레이는 복잡한 계산 결과를 캐시합니다.실시간 복잡한 계산 결과를 캐시합니다.수십억 개의 가능한 값 중 수백만 개만이 유효한 결과를 산출합니다.어레이는 1234567 => 23457, 1234999 => 74361, ...와 같습니다.이 파일은 몇 메가바이트의 PHP 파일에 저장되며 실행 시작 시 include_once-d가 포함됩니다.초기 로드 시간은 중요하지 않습니다.키가 발견되지 않으면 이 특정 값이 유효한 결과를 반환하지 않음을 의미합니다.문제는 이것을 초당 50k+로 처리하는 것입니다.

결론

에러 처리를 하지 않고, 1회 조회로 값을 얻을 수 있는 방법이 없기 때문에, 1회 회답에 응하는 것은 곤란합니다.대신 나는 모든 위대한 기부를 상향 투표했다.

가장 가치 있는 정보:

  • array_key_module을 사용합니다.이것은 다른 것보다 빠르기 때문입니다.
  • PHP Quick Hash 확인

PHP가 어레이를 처리하는 방법에 대해 많은 혼동이 있었습니다.소스 코드를 확인하면 모든 어레이가 균형 잡힌 트리임을 알 수 있습니다.자체 검색 메서드를 구축하는 것은 C 및 C++에서는 일반적이지만 PHP와 같은 상위 스크립트 언어에서는 수행되지 않습니다.

갱신하다

PHP 7에서는 null mergece 연산자를 사용하여 이 작업을 수행할 수 있습니다.

return $table[$key] ?? null;

구답

우선 어레이는 B-트리로 구현되지 않고 해시 테이블로 구현됩니다.버킷 배열(해시 함수를 통해 인덱스화됨)에는 각각 실제 값의 링크 리스트가 있습니다(해시 충돌 시).즉, 조회 시간은 해시 함수가 버킷 전체에 값을 얼마나 "확산"했는가에 따라 달라집니다. 즉, 해시 충돌 수가 중요한 요소입니다.

엄밀히 말하면 이 문장이 가장 정확합니다.

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

이것에 의해 함수 호출이 도입되기 때문에 최적화되어 있는 것보다 훨씬 느립니다.isset().얼마나요?~2e3배 느립니다.

다음으로 참조를 사용하여 두 번째 검색을 회피합니다.

$tmp = &$lookup_table[$key];

return isset($tmp) ? $tmp : null;

유감스럽게도, 이것은 원본을 수정한다.$lookup_table참조는 항상 PHP에 의해 유효하기 때문에 항목이 존재하지 않는 경우 어레이를 지정합니다.

그러면 다음과 같은 방법을 사용할 수 있습니다.

return isset($lookup_table[$key]) ? $lookup_table[$key] : null;

참조의 부작용이 없을 뿐만 아니라 검색을 두 번 수행해도 실행 시 속도가 빨라집니다.

긴 검색 시간을 줄이기 위한 하나의 방법으로 어레이를 작은 조각으로 분할하는 것을 검토할 수 있습니다.

다음 코드로 벤치 마킹을 했어요.

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) . " seconds<br/>";

$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) . " seconds<br/>";


$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) . " seconds<br/>";

$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) . " seconds<br/>";
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) . " seconds<br/>";

그리고 가장 빠르게 실행되는 테스트는 이 테스트에 사용되는 것입니다.isset($array[$key]) ? $array[$key] : null에러 리포트를 무효로 하는 솔루션이 그 뒤를 잇고 있습니다.

이 기능은 이쪽에서

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

하지만 이것은 빠르다.

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

여기에는 두 가지 일반적인 접근법이 있습니다.

  1. 정의되지 않은 키의 기본값을 정의합니다.
  2. 정의되지 않은 키가 있는지 확인합니다.

첫 번째 코드와 가능한 한 적은 코드를 실행하는 방법은 다음과 같습니다.

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

두 번째 방법은 다음과 같습니다.

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

테스트를 해봐야 할 갑작스러운 생각일 뿐인데array_intersect_key()기존 값을 가져오고 나머지는 array_module로 채웁니다().그러면 데이터에 액세스하기 위한 루프가 필요하지 않습니다.뭐 그런 거:

$searched_keys = array ('key1' => null, 'key2' => null); // the list of the keys to find

$exiting_values = array_intersect_key($lookup_table, $searched_keys);
$all_values = array_merge($searched_keys, $exiting_keys);

퍼포먼스 면에서는 시도하지 않았음을 주의해 주십시오.

@ 연산자와 error_reporting 메서드는 모두 isset을 사용하는 것보다 느립니다.이 두 가지 방법 모두 PHP 오류 보고 설정을 수정하지만 PHP 오류 핸들러는 계속 호출됩니다.에러 핸들러는 error_reporting 설정을 체크하고 아무것도 보고하지 않고 종료합니다만, 아직 시간이 걸립니다.

사용하는 것이 좋습니다.isset이 에러를 회피하지 않고 기능합니다.키가 존재하는지 확인하는 함수를 만들었습니다.그렇지 않으면 기본값이 반환됩니다.네스트된 배열의 경우 다른 키를 순서대로 추가하면 됩니다.

중첩된 배열 조회:

/**
 * Lookup array value.
 *
 * @param array $array
 * @param array $keys
 * @param $defaultValue
 */
public static function array_key_lookup($array, $keys, $defaultValue)
{
    $value = $array;
    foreach ($keys as $key) {
        if (isset($value[$key])) {
            $value = $value[$key];
        } else {
            $value = $defaultValue;
            break;
        }
    }

    return $value;
}

사용 예:

$array = [
    'key1' => 'value1',
    'key2' => 'value2',
    'key3' => [
        'key3a' => 'value3a',
        'key3b' => 'value3b'
    ]
];

array_key_lookup($array, ['key3', 'key3a'], 'default')
'value3a'

array_key_lookup($array, ['key2', 'key2a'], 'default')
'default'

array_key_lookup($array, ['key2'], 'default')
'value2'

array_key_lookup($array, ['key5'], 'default')
'default'

에러 회피:

$value = @$array[$key1][$key2] ?: $defaultValue;

우선, 데이터를 키별로 정렬하지만 새 배열에는 정규 숫자 인덱스가 포함되어 있는 새 배열을 저장하여 데이터를 성능을 위해 재구성합니다.

이 부분은 시간이 많이 걸리지만 한 번만 완료됩니다.

 // first sort the array by it's keys
 ksort($data);

 // second create a new array with numeric index
 $tmp = new array();
 foreach($data as $key=>$value)
 {
    $tmp[] = array('key'=>$key,'value'=>$value);
 }
 // now save and use this data instead
 save_to_file($tmp);

이 작업이 완료되면 바이너리 검색을 사용하여 키를 빠르게 찾을 수 있습니다.나중에 이런 기능을 사용할 수 있습니다.

  function findKey($key, $data, $start, $end)
  { 
    if($end < $start) 
    { 
        return null; 
    } 

    $mid = (int)(($end - $start) / 2) + $start; 

    if($data[$mid]['key'] > $key) 
    { 
        return findKey($key, $data, $start, $mid - 1); 
    } 
    else if($data[$mid]['key'] < $key) 
    { 
        return findKey($key, $data, $mid + 1, $end); 
    } 

    return $data[$mid]['value'];
 }

키 검색을 수행하려면 다음과 같이 하십시오.

 $result = findKey($key, $data, 0, count($data));
 if($result === null)
 {
      // key not found.
 }

경우,count($data)항상 실행되므로 어레이 데이터를 저장한 파일에 캐시할 수 있습니다.

은 일반 것 .$data을 사용하다8진수만 빠른데, 8진수만 작성하면 검색 성능이 취소될 수 있습니다(예전에 경험한 적이 있습니다).데이터를 얼마나 검색해야 하는지에 따라 달라집니다.

언급URL : https://stackoverflow.com/questions/16675753/php-fastest-way-to-handle-undefined-array-key

반응형