더 빠른 것 : 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 꽤 수행하지만 테스트하는 동안 나는 또한, 실현 whileforeach각 새로 고침에 나는 다른 "수상자"를 얻는 것을. 그것은 항상 너무 많은 서버 변수에 의존하며, 가장 좋은 방법은 다른 시간에 매우 많은 횟수를 반복하고 더 자주이기는 것을 얻거나 백그라운드에서 일어나는 일을 알고 최종 승자가 될 것임을 아는 것입니다. 상관없이
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.