더 빠른 것 : in_array 또는 isset? [닫은]


96

이 질문은 내가 항상 값싼 느린 서버 (또는 트래픽이 많은 서버)에서도 실행할 수있는 최적화 된 코드를 작성하는 것을 좋아하기 때문에 나에게만 해당됩니다.

주위를 둘러 보니 답을 찾을 수 없었습니다. 필자의 경우 배열의 키가 중요하지 않다는 점을 염두에두고이 두 예제 사이에서 무엇이 더 빠른지 궁금합니다 (당연히 의사 코드).

<?php
$a = array();
while($new_val = 'get over 100k email addresses already lowercased'){
    if(!in_array($new_val, $a){
        $a[] = $new_val;
        //do other stuff
    }
}
?>

<?php
$a = array();
while($new_val = 'get over 100k email addresses already lowercased'){
    if(!isset($a[$new_val]){
        $a[$new_val] = true;
        //do other stuff
    }
}
?>

질문의 요점은 배열 충돌이 아니므로, 나는 당신이 삽입 충돌을 두려워하면 것을 추가하고 싶습니다 $a[$new_value], 당신은 사용할 수 있습니다 $a[md5($new_value)]. 여전히 충돌을 일으킬 수 있지만 사용자가 제공 한 파일 ( http://nikic.github.com/2011/12/28/Supercolliding-a-PHP-array.html ) 에서 읽을 때 가능한 DoS 공격을 피할 수 있습니다.


3
항상 최적화 된 코드를 작성하기 위해 노력하고 있다면 확실히 프로파일 러를 사용하고 계십니까?
— mario

60
재개를 위해 투표합니다. 질문은 잘 구성되어 있으며 답변은 사실과 참조로 뒷받침됩니다. 얼마 마이크로 - 최적화, 이러한 유형의 질문은 건설 .
— Jason McCreary 2013

5
@JasonMcCreary 두 번째; 딱 하나만 더.
— Ja͢ck 2012

7
이것은 몇 년 후이지만 나는 이것을 마이크로 최적화라고 생각하지도 않을 것입니다. 큰 데이터 세트의 경우 엄청난 차이를 만들 수 있습니다 !!
— Robert

2
...이 질문은 나에게 "건설적"인 것 같습니다. 또 다른 재개 캠페인을 시작하겠습니다.
— mickmackusa

답변:


117

지금까지의 답변은 정확합니다. isset이 경우 사용 하는 것이 더 빠릅니다.

  • 키에서 O (1) 해시 검색을 사용하는 반면 in_array일치 항목을 찾을 때까지 모든 값을 확인해야합니다.
  • opcode이므로 in_array내장 함수를 호출하는 것보다 오버 헤드가 적습니다 .

값이있는 배열 (아래 테스트에서 10,000 개)을 사용하여 in_array더 많은 검색을 수행 함으로써 이를 입증 할 수 있습니다 .

isset:    0.009623
in_array: 1.738441

이것은 임의의 값을 채우고 때때로 배열에 존재하는 값을 찾아 Jason의 벤치 마크를 기반으로합니다. 모두 무작위이므로 시간이 변동될 수 있습니다.

$a = array();
for ($i = 0; $i < 10000; ++$i) {
    $v = rand(1, 1000000);
    $a[$v] = $v;
}
echo "Size: ", count($a), PHP_EOL;

$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    isset($a[rand(1, 1000000)]);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    in_array(rand(1, 1000000), $a);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

해시에 대해 알고 있지만 함수 속도를 높이기 위해 가능한 경우 배열 값에서 비슷한 작업이 수행되지 않는 이유가 궁금합니다. 단순히 값에 추가 해싱을 추가하여 유사한 값을 사용하면 메모리 소모를 줄일 수 있습니다 .. 맞습니까?
— Fabrizio 2012

3
@Fabrizio-배열 값은 중복 될 수 있으며 해시 할 수없는 개체를 포함 할 수 있습니다. 키는 고유해야하며 쉽게 해시 할 수있는 문자열과 정수 여야합니다. 키와 값을 모두 해시하는 일대일 맵을 만들 수 있지만 이것이 PHP의 배열이 작동하는 방식은 아닙니다.
— David Harkness 2012

3
배열에 고유 한 값이 포함되어 있다고 확신하는 경우 또 다른 옵션 인 flip + isset이 있습니다.
— Arkadij Kuzhel

이 예제에서는 뒤집힌 isset이 in_array보다 더 빠릅니다.```$ start = microtime (true); $ foo = array_flip ($ a); for ($ i = 0; $ i <10000; ++ $ i) {isset ($ foo [rand (1, 1000000)]); } $ total_time = microtime (true)-$ start; echo "총 시간 (flipped isset) :", number_format ($ total_time, 6), PHP_EOL;
— 앙드레 Baumeier

@AndreBaumeier 더 빠른 것은 배열의 크기와 수행 할 테스트 수에 따라 다릅니다. 3 개의 테스트를 수행하기 위해 10,000 개의 요소 배열을 뒤집는 것은 아마도 효율적이지 않을 것입니다.
— David Harkness

42

더 빠릅니다 : isset()vsin_array()

isset() 가 더 빠르다.

분명해야하지만 isset()단일 값만 테스트합니다. 반면는 in_array()각 요소의 값을 테스트하고, 배열 전체를 반복한다.

대략적인 벤치마킹은 microtime().

결과 :

Total time isset():    0.002857
Total time in_array(): 0.017103

참고 : 존재 여부에 관계없이 결과는 유사했습니다.

암호:

<?php
$a = array();
$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    isset($a['key']);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    in_array('key', $a);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

exit;

추가 자료

다음 사항도 살펴 보시기 바랍니다.


좋은 솔루션입니다. 더 많은 사람들이 microtime()또는 다른 도구를 사용하여 기능 / 코드를 더 많이 분할하지 않는 것에 놀랐습니다 . 믿을 수 없을만큼 가치가 있습니다.
— nickhar

1
동일한 키에 대해 빈 배열을 검색하면 내장 in_array함수를 사용하는 것과 비교하여 함수 를 호출하는 오버 헤드 만 강조 isset됩니다. 이것은 임의의 키를 포함하고 때때로 기존 키 / 값을 검색하는 배열을 사용하는 것이 좋습니다.
— David Harkness

내가 사용하는 벤치 마크 및 microtime 꽤 수행하지만 테스트하는 동안 나는 또한, 실현 while및 foreach각 새로 고침에 나는 다른 "수상자"를 얻는 것을. 그것은 항상 너무 많은 서버 변수에 의존하며, 가장 좋은 방법은 다른 시간에 매우 많은 횟수를 반복하고 더 자주이기는 것을 얻거나 백그라운드에서 일어나는 일을 알고 최종 승자가 될 것임을 아는 것입니다. 상관없이
— Fabrizio

@David Harkness, 당신은 이미 내 대답을 골랐습니다. 더 많은 것을 원한다면 내 어깨에 서서 자신의 답변을 게시하십시오. :) 그럼에도 불구하고 함수 오버 헤드가 이미에 비해 훨씬 더 비싸 다면 더 큰 배열을 isset()전달 하면 더 빨라질 것이라고 생각하는 이유는 무엇입니까?
— Jason McCreary 2012

1
@Fabrizio- 해싱 함수 및 해시 테이블 에 대해 읽어보십시오 .
— David Harkness

19

를 사용하면 해시 테이블을isset() 사용하기 때문에 검색 속도가 빨라지므로 검색 이 필요하지 않습니다 .O(n)

키는 djb 해시 함수 를 사용하여 먼저 해시 되어에서 유사하게 해시 된 키의 버킷을 결정합니다 O(1). 그런 다음에서 정확한 키를 찾을 때까지 버킷을 반복적으로 검색 O(n)합니다.

의도적 인 해시 충돌을 제외 하고이 접근 방식은보다 나은 성능을 제공 in_array()합니다.

isset()표시된 방식으로 사용할 때 최종 값을 다른 함수에 전달하려면 array_keys()을 사용하여 새 배열을 만들어야합니다. 키와 값 모두에 데이터를 저장하면 메모리가 손상 될 수 있습니다.

최신 정보

코드 디자인 결정이 런타임 성능에 미치는 영향을 확인하는 좋은 방법으로 컴파일 된 스크립트 버전 을 확인할 수 있습니다.

echo isset($arr[123])

compiled vars:  !0 = $arr
line     # *  op                           fetch      ext  return  operands
-----------------------------------------------------------------------------
   1     0  >   ZEND_ISSET_ISEMPTY_DIM_OBJ              2000000  ~0      !0, 123
         1      ECHO                                                 ~0
         2    > RETURN                                               null

echo in_array(123, $arr)

compiled vars:  !0 = $arr
line     # *  op                           fetch      ext  return  operands
-----------------------------------------------------------------------------
   1     0  >   SEND_VAL                                             123
         1      SEND_VAR                                             !0
         2      DO_FCALL                                 2  $0      'in_array'
         3      ECHO                                                 $0
         4    > RETURN                                               null

in_array()상대적으로 비효율적 인 O(n)검색을 사용할 뿐만 아니라 이를 위해 단일 opcode ( )를 사용하는 DO_FCALL반면 함수 ( ) 로 호출해야합니다 .isset()ZEND_ISSET_ISEMPTY_DIM_OBJ


7

두 번째는 특정 배열 키만 찾고 발견 될 때까지 전체 배열을 반복 할 필요가 없기 때문에 더 빠릅니다 (찾을 수없는 경우 모든 배열 요소를 봅니다).


뿐만 아니라의 행방 따라 달라진다는 전역 범위에서 VAR 검색
— 엘 야

@ EL2002, 그 진술에 대해 자세히 설명해 주시겠습니까?
— Fabrizio 2012

1
마이크, isset()찾을 수없는 경우 에도 전체 어레이를 보지 않겠습니까?
— Fabrizio 2012

1
@Fabrizio 아니요, 반복 할 필요가 없습니다. 내부적으로 (C에서) PHP 배열은 해시 테이블 일뿐입니다. 단일 인덱스 값을 조회하기 위해 C는 해당 값의 해시를 만들고 메모리에서 할당 된 위치를 조회합니다. 거기에 가치가 있거나 존재하지 않습니다.
— Mike Brant 2012

1
@Fabrizio이 기사는 배열이 PHP에 의해 C에서 내부적으로 어떻게 표현되는지에 대한 좋은 개요를 제공합니다. nikic.github.com/2012/03/28/…
— Mike Brant
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.