2013-08-19 10 views
11

Muszę znaleźć maksymalną i minimalną wartość bardzo dużych tablic. Do tego używamChrome: jak rozwiązać błędy "Maksymalne przekroczenie limitu stosu połączeń" na Math.max.apply (Math, array)

Math.max.apply(Math, my_array); 
Math.min.apply(Math, my_array); 

To działa dobrze w Firefox i IE, ale na Chrome zawsze dotrzesz Maximum call stack size exceeded błędy ... moja obecna tablica ma 221954 elementów, a to nie jest mój największy.

Czy ktoś wie, jak rozwiązać ten błąd w Chrome? Jak mogę zoptymalizować wyszukiwanie wartości maksymalnej i minimalnej?

Dla tych ludzi, którzy nie mogą uwierzyć, spróbuj tego w konsoli Chrome:

var xxx = [] 
for(var i=0; i<300000; i++){ 
    xxx.push(Math.random()); 
} 
Math.max.apply(Math, xxx); 

---> RangeError: Maksymalny rozmiar stosu wywołań przekroczyła

+0

Możesz rzucić okiem na to pytanie: http://stackoverflow.com/questions/1669190/javascript-min-max-array-values ​​ –

+0

Widziałem to. Stamtąd skopiowałem moje dwie linie. Ale nie ma nic o moim problemie ... – PanChan

+0

Spróbuj przewinąć w dół: http://stackoverflow.com/a/13440842/2074608 –

Odpowiedz

0

Dla mnie ten błąd nie powinien od wywołania Math.min/max wygląda na wynik użycia rekursji, której nie mogę uwierzyć, że Chrome użyłaby do zaimplementowania tych funkcji.

Czy są osadzone w kodzie rekurencyjnym?

Możesz swobodnie przetasować swój własny kod min/maks, aby uniknąć problemu w Chrome.

+0

Podczas debugowania w przeglądarce Chrome zawsze zatrzymuje się na linii z" Math.max.apply ", również nie mogłem w to uwierzyć, ale to jest prawda ... I nie mam żadnej rekursji w tym kodzie. Te dwie linie są w funkcji inicjującej, która jest wywoływana tylko raz. – PanChan

0
var a=[]; 
for(var i=0;i<1125011;i++){ 
    a[i] = i; 
} 
function maxIterate(arr){ 
    var max = arr[0]; 
    for(var i = 1;i< arr.length; i++){ 
     (max < arr[i]) && (max = arr[i]) 
    } 
    return max; 
} 
console.log(maxIterate(a)); 

Math.max może użyć metody rekurencyjnej, aby uzyskać wartość maksymalną, po prostu przepisać funkcję iteracji, aby uzyskać maksymalną instead.This uniknie RangeError.

+0

Co? (max

+0

@ lukas.pukenis - kolejność operacji. Jeśli max

1

Osiągasz limit wielkości parametru funkcji. I jest to OK. Funkcja powinna akceptować tylko kilka parametrów, ponieważ inny kod to zapach zapachowy.

Jeśli masz kilka przedmiotów? - Użyj tablicy. Używasz .apply(), który przekazuje argumenty takie jak: fun(1,2,3,4,5,6....) i osiąga limit. To jest zła praktyka.

Problem polega na tym, że - Math.max() jest podłączony do pracy tylko w ten sposób, więc najlepszym rozwiązaniem będzie iteracyjna funkcja wyszukiwania. Ale to już inny temat, ponieważ wydajność i algorytm mogą się różnić, na przykład, jeśli najpierw posortujesz tablicę.

+4

To są elitarne woły. Używanie argumentów o zmiennej ilości jest jednym z cudów elastyczności JavaScriptu. Nie musisz tego robić! Jest to bardzo wygodne w wielu przypadkach. Powinieneś podać powód, dla którego jest zły! – Lodewijk

+1

Uwielbiam to i go używam. Z wyjątkiem sytuacji, w których w odpowiedziach mam najistotniejsze przypadki :) –

17

Ten problem nie ma nic wspólnego z Math.max i Math.min.

Funkcja.prototype.apply może przyjmować jedynie tablicę o ograniczonej długości jako swój drugi argument.

lokalnie I przetestowane w Chrome z:

function limit(l) { 
    var x = []; x.length = l; 
    (function(){}).apply(null, x); 
} 

lokalnie Termin (l) awarii dokładnie l = 124980. W kanarków to, że kolejny numer, a również ~ 125k.

Oto przykładowe wyjaśnienie, dlaczego tak się dzieje: https://code.google.com/p/v8/issues/detail?id=2896 (jest on również reprorowalny w innych silnikach JS, na przykład MDN wspomniał o numerze: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#Using_apply_and_built-in_functions (Zaczynając od "Ale uwaga ..."), wskazując na ten problem w Webkit bugzilla: https://bugs.webkit.org/show_bug.cgi?id=80797).O ile rozumiem dlaczego RangeError jest zgłaszany w V8:

V8 implementuje Function.prototype.apply w montażu. Przed wywołaniem funkcji należy umieścić wszystkie parametry wywołania funkcji, np. thisArg i wszystkie elementy 2. arg array, pojedynczo, na stos, przed wywołaniem funkcji javascript. Ale stos ma ograniczoną pojemność, a jeśli osiągniesz limit, otrzymasz RangeError.

To co znalazłem w źródle V8 (IA-32 zgromadzeń, builtins-ia32.cc):

void Builtins::Generate_FunctionApply(MacroAssembler* masm) { 
    static const int kArgumentsOffset = 2 * kPointerSize; 
    static const int kReceiverOffset = 3 * kPointerSize; 
    static const int kFunctionOffset = 4 * kPointerSize; 
    { 
    FrameScope frame_scope(masm, StackFrame::INTERNAL); 

    __ push(Operand(ebp, kFunctionOffset)); // push this 
    __ push(Operand(ebp, kArgumentsOffset)); // push arguments 
    __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION); 

    // Check the stack for overflow. We are not trying to catch 
    // interruptions (e.g. debug break and preemption) here, so the "real stack 
    // limit" is checked. 
    Label okay; 
    ExternalReference real_stack_limit = 
     ExternalReference::address_of_real_stack_limit(masm->isolate()); 
    __ mov(edi, Operand::StaticVariable(real_stack_limit)); 
    // Make ecx the space we have left. The stack might already be overflowed 
    // here which will cause ecx to become negative. 
    // !! ADDED COMMENT: IA-32 stack grows downwards, if address to its current top is 0 then it cannot be placed any more elements into. esp is the pointer to stack top. 
    __ mov(ecx, esp); 
    // !! ADDED COMMENT: edi holds the "real_stack_limit", which holds the minimum address that stack should not grow beyond. If we subtract edi from ecx (=esp, or, in other words, "how much space is left on the stack"), we may get a negative value, and the comment above says that 
    __ sub(ecx, edi); 
    // Make edx the space we need for the array when it is unrolled onto the 
    // stack. 
    // !! ADDED COMMENT: eax holds the number of arguments for this apply call, where every member of the 2nd argument array counts as separate argument 
    __ mov(edx, eax); 
    // !! ADDED COMMENT: kPointerSizeLog2 - kSmiTagSize is the base-2-logarithm of how much space would 1 argument take. By shl we in fact get 2^(kPointerSizeLog2 - kSmiTagSize) * arguments_count, i.e. how much space do actual arguments occupy 
    __ shl(edx, kPointerSizeLog2 - kSmiTagSize); 
    // Check if the arguments will overflow the stack. 
    // !! ADDED COMMENT: we compare ecx which is how much data we can put onto stack with edx which now means how much data we need to put onto stack 
    __ cmp(ecx, edx); 
    __ j(greater, &okay); // Signed comparison. 

    // Out of stack space. 
    __ push(Operand(ebp, 4 * kPointerSize)); // push this 
    __ push(eax); 
    __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION); 

Proszę sprawdzić !! DODANO KOMENTARZ dla wyjaśnienia, jak to rozumiem.

A to APPLY_OVERFLOW funkcja, napisany w JS (ponownie, źródło V8, runtime.js):

function APPLY_OVERFLOW(length) { 
    throw %MakeRangeError('stack_overflow', []); 
} 

EDIT: W przypadku, pójdę jak:

var max = -Infinity; 
for(var i = 0; i < arr.length; i++) if (arr[i] > max) max = arr[i]; 
+1

W moim przypadku ograniczenie następującej funkcji wynosi 251100 (przy nowym uruchomieniu): 'function max (arr) {return Math.max.apply (null, arr); } ' –

Powiązane problemy