2013-08-19 22 views
11

Tôi phải tìm giá trị tối đa và tối thiểu của các mảng rất lớn. Đối với điều này tôi đang sử dụngChrome: Cách giải quyết lỗi "Kích thước ngăn xếp cuộc gọi tối đa đã vượt quá" trên Math.max.apply (Math, array)

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

Nó hoạt động tốt trên trình duyệt Firefox và IE, nhưng trên Chrome Tôi luôn luôn có được Maximum call stack size exceeded lỗi ... mảng hiện tại của tôi có 221.954 yếu tố, và đó không phải là lớn nhất của tôi.

Có ai đó biết cách giải quyết lỗi này trên Chrome không? Làm cách nào để tối ưu hóa tìm kiếm giá trị tối đa và tối thiểu?

Đối với những người không thể tin được, hãy thử này trong giao diện điều khiển của Chrome:

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

---> RangeError: kích thước gọi stack tối đa vượt quá

+0

Bạn có thể muốn xem câu hỏi này: http://stackoverflow.com/questions/1669190/javascript-min-max-array-values ​​ –

+0

Tôi đã thấy điều này. Từ đó tôi đã sao chép hai dòng. Nhưng không có gì về vấn đề của tôi ... – PanChan

+0

Hãy thử cuộn xuống: http://stackoverflow.com/a/13440842/2074608 –

Trả lời

0

Để cho tôi những lỗi không nên từ cuộc gọi vào Math.min/max, nó giống như kết quả của việc sử dụng đệ quy mà tôi không thể tin tưởng Chrome sẽ sử dụng để thực hiện các chức năng đó.

Chúng có được nhúng trong mã đệ quy không?

Bạn có thể cuộn mã tối thiểu/tối đa của riêng bạn một cách tầm thường để tránh sự cố trong Chrome.

+0

Khi gỡ lỗi trong Chrome, nó luôn dừng ở dòng 'Math.max.apply', tôi cũng không thể tin được, nhưng đó là sự thật ... Và tôi không có bất kỳ đệ quy nào trong đoạn mã này. Hai dòng này nằm trong hàm init chỉ được gọi một lần. – 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 có thể sử dụng phương pháp đệ quy để có được giá trị tối đa, chỉ cần viết lại một chức năng iterating để có được tối đa instead.This sẽ tránh RangeError.

+0

Cái gì? (tối đa

+0

@ lukas.pukenis - đó là thứ tự hoạt động. Nếu max

1

Bạn đang đạt đến giới hạn kích thước thông số chức năng. Và nó là OK. Chức năng chỉ chấp nhận một vài tham số nếu không có mã số .

Nếu bạn có nhiều mục? - Sử dụng Mảng. Bạn đang sử dụng .apply() để chuyển các đối số như: fun(1,2,3,4,5,6....) và đạt đến giới hạn. Đây là thực tế không tốt.

Sự cố là - Math.max() chỉ có dây để hoạt động như thế này, do đó, đặt cược tốt nhất của bạn sẽ là chức năng tìm kiếm lặp lại. Nhưng đó là một chủ đề khác, vì hiệu suất và thuật toán có thể khác nhau, ví dụ nếu bạn sắp xếp mảng đầu tiên.

+4

Đây là một số người nổi tiếng. Sử dụng các đối số số lượng biến là một trong những điều kỳ diệu về tính linh hoạt của JavaScript. Không cần phải làm thế! Nó rất thuận tiện trong nhiều trường hợp. Bạn nên đưa ra một lý do cho nó là xấu! – Lodewijk

+1

Tôi thích nó và sử dụng nó. Ngoại trừ tôi tất cả về các trường hợp cạnh trong câu trả lời :) –

17

Sự cố này không liên quan gì đến Math.max và Math.min.

Function.prototype.apply chỉ có thể nhận mảng có độ dài giới hạn làm đối số thứ hai của nó.

tại địa phương, tôi đã thử nghiệm nó trong Chrome sử dụng:

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

tại địa phương, hạn chế (l) bị rơi chính xác với l = 124980. Trong chim hoàng yến, đó là một số khác, mà còn ~ 125 nghìn.

Đây là một ví dụ giải thích lý do tại sao điều đó xảy ra: https://code.google.com/p/v8/issues/detail?id=2896 (nó cũng có thể lặp lại trong các công cụ JS khác, ví dụ MDN có đề cập đến vấn đề: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#Using_apply_and_built-in_functions (Bắt đầu với "Nhưng hãy cẩn thận ..."), trỏ đến vấn đề này trong WebKit bugzilla: https://bugs.webkit.org/show_bug.cgi?id=80797).Theo như tôi hiểu lý do tại sao RangeError được ném trong V8:

V8 thực hiện Function.prototype.apply trong lắp ráp. Trước khi gọi hàm, nó sẽ đặt tất cả các tham số gọi hàm, ví dụ: thisArg, và tất cả các thành viên của mảng arg thứ hai, từng cái một, lên ngăn xếp, trước khi gọi hàm javascript. Nhưng stack có dung lượng giới hạn, và nếu bạn đạt đến giới hạn, bạn sẽ nhận được RangeError.

Đây là những gì tôi đã tìm thấy trong nguồn V8 (IA-32 lắp ráp, 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); 

Vui lòng kiểm tra !! THÊM BỔ SUNG cho giải thích về cách tôi hiểu nó.

Và đây là chức năng APPLY_OVERFLOW, viết bằng JS (một lần nữa, V8 nguồn, runtime.js):

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

EDIT: Trong trường hợp của bạn, tôi sẽ đi như:

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

Trong trường hợp của tôi, giới hạn của hàm sau là 251100 (khi bắt đầu mới): 'function max (arr) {return Math.max.apply (null, arr); } ' –