Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(54)

Side by Side Diff: src/array.js

Issue 1513020: Early specialize sorting functions depending if there is a custom comparator or not. (Closed)
Patch Set: Addressing Ivan's comments Created 10 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2006-2008 the V8 project authors. All rights reserved. 1 // Copyright 2006-2008 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 626 matching lines...) Expand 10 before | Expand all | Expand 10 after
637 637
638 // Return the deleted elements. 638 // Return the deleted elements.
639 return deleted_elements; 639 return deleted_elements;
640 } 640 }
641 641
642 642
643 function ArraySort(comparefn) { 643 function ArraySort(comparefn) {
644 // In-place QuickSort algorithm. 644 // In-place QuickSort algorithm.
645 // For short (length <= 22) arrays, insertion sort is used for efficiency. 645 // For short (length <= 22) arrays, insertion sort is used for efficiency.
646 646
647 var custom_compare = IS_FUNCTION(comparefn); 647 function InsertionSortWithFunc(a, from, to) {
648 for (var i = from + 1; i < to; i++) {
649 var element = a[i];
Lasse Reichstein 2010/04/07 17:43:27 Come to think of it, using binary search seems ove
antonm 2010/04/07 17:47:27 Agree. That's the thing I implemented in C++, but
650 // place element in a[from..i[
651 // binary search
652 var min = from;
653 var max = i;
654 // The search interval is a[min..max[
655 while (min < max) {
656 var mid = min + ((max - min) >> 1);
657 var order = (a[mid] === element) ?
658 0 : comparefn.call(null, a[mid], element);
Lasse Reichstein 2010/04/07 17:43:27 Reading a[mid] twice. Maybe assign to temporary va
antonm 2010/04/07 17:47:27 Thanks, will give it a try.
659 if (order == 0) {
660 min = max = mid;
Lasse Reichstein 2010/04/07 17:43:27 No need to assign to max.
antonm 2010/04/07 17:47:27 Will give it a try as well.
661 break;
662 }
663 if (order < 0) {
664 min = mid + 1;
665 } else {
666 max = mid;
667 }
668 }
669 // place element at position min==max.
670 for (var j = i; j > min; j--) {
671 a[j] = a[j - 1];
672 }
673 a[min] = element;
674 }
675 }
676
677 function QuickSortWithFunc(a, from, to) {
678 // Insertion sort is faster for short arrays.
679 if (to - from <= 22) {
680 InsertionSortWithFunc(a, from, to);
681 return;
682 }
683 var pivot_index = $floor($random() * (to - from)) + from;
684 var pivot = a[pivot_index];
685 // Issue 95: Keep the pivot element out of the comparisons to avoid
686 // infinite recursion if comparefn(pivot, pivot) != 0.
687 a[pivot_index] = a[from];
688 a[from] = pivot;
689 var low_end = from; // Upper bound of the elements lower than pivot.
690 var high_start = to; // Lower bound of the elements greater than pivot.
691 // From low_end to i are elements equal to pivot.
692 // From i to high_start are elements that haven't been compared yet.
693 for (var i = from + 1; i < high_start; ) {
694 var element = a[i];
695 var order = (element === pivot) ?
696 0 : comparefn.call(null, element, pivot);
697 if (order < 0) {
698 a[i] = a[low_end];
699 a[low_end] = element;
700 i++;
701 low_end++;
702 } else if (order > 0) {
703 high_start--;
704 a[i] = a[high_start];
705 a[high_start] = element;
706 } else { // order == 0
707 i++;
708 }
709 }
710 QuickSortWithFunc(a, from, low_end);
711 QuickSortWithFunc(a, high_start, to);
712 }
648 713
649 function Compare(x,y) { 714 function Compare(x,y) {
650 // Assume the comparefn, if any, is a consistent comparison function. 715 // Assume the comparefn, if any, is a consistent comparison function.
651 // If it isn't, we are allowed arbitrary behavior by ECMA 15.4.4.11. 716 // If it isn't, we are allowed arbitrary behavior by ECMA 15.4.4.11.
Lasse Reichstein 2010/04/07 17:43:27 Comment doesn't make sense any more (not here, at
antonm 2010/04/07 17:47:27 Agree. Will remove.
652 if (x === y) return 0; 717 if (x === y) return 0;
653 if (custom_compare) {
654 // Don't call directly to avoid exposing the builtin's global object.
655 return comparefn.call(null, x, y);
656 }
657 if (%_IsSmi(x) && %_IsSmi(y)) { 718 if (%_IsSmi(x) && %_IsSmi(y)) {
658 return %SmiLexicographicCompare(x, y); 719 return %SmiLexicographicCompare(x, y);
659 } 720 }
660 x = ToString(x); 721 x = ToString(x);
661 y = ToString(y); 722 y = ToString(y);
662 if (x == y) return 0; 723 if (x == y) return 0;
663 else return x < y ? -1 : 1; 724 else return x < y ? -1 : 1;
664 }; 725 };
665 726
666 function InsertionSort(a, from, to) { 727 function InsertionSort(a, from, to) {
667 for (var i = from + 1; i < to; i++) { 728 for (var i = from + 1; i < to; i++) {
668 var element = a[i]; 729 var element = a[i];
669 // Pre-convert the element to a string for comparison if we know 730 // Pre-convert the element to a string for comparison if we know
670 // it will happen on each compare anyway. 731 // it will happen on each compare anyway.
671 var key = 732 var key = %_IsSmi(element) ? element : ToString(element);
672 (custom_compare || %_IsSmi(element)) ? element : ToString(element);
673 // place element in a[from..i[ 733 // place element in a[from..i[
674 // binary search 734 // binary search
675 var min = from; 735 var min = from;
676 var max = i; 736 var max = i;
677 // The search interval is a[min..max[ 737 // The search interval is a[min..max[
678 while (min < max) { 738 while (min < max) {
679 var mid = min + ((max - min) >> 1); 739 var mid = min + ((max - min) >> 1);
680 var order = Compare(a[mid], key); 740 var order = Compare(a[mid], key);
681 if (order == 0) { 741 if (order == 0) {
682 min = max = mid; 742 min = max = mid;
(...skipping 16 matching lines...) Expand all
699 function QuickSort(a, from, to) { 759 function QuickSort(a, from, to) {
700 // Insertion sort is faster for short arrays. 760 // Insertion sort is faster for short arrays.
701 if (to - from <= 22) { 761 if (to - from <= 22) {
702 InsertionSort(a, from, to); 762 InsertionSort(a, from, to);
703 return; 763 return;
704 } 764 }
705 var pivot_index = $floor($random() * (to - from)) + from; 765 var pivot_index = $floor($random() * (to - from)) + from;
706 var pivot = a[pivot_index]; 766 var pivot = a[pivot_index];
707 // Pre-convert the element to a string for comparison if we know 767 // Pre-convert the element to a string for comparison if we know
708 // it will happen on each compare anyway. 768 // it will happen on each compare anyway.
709 var pivot_key = 769 var pivot_key = %_IsSmi(pivot) ? pivot : ToString(pivot);
710 (custom_compare || %_IsSmi(pivot)) ? pivot : ToString(pivot);
711 // Issue 95: Keep the pivot element out of the comparisons to avoid 770 // Issue 95: Keep the pivot element out of the comparisons to avoid
712 // infinite recursion if comparefn(pivot, pivot) != 0. 771 // infinite recursion if comparefn(pivot, pivot) != 0.
713 a[pivot_index] = a[from]; 772 a[pivot_index] = a[from];
714 a[from] = pivot; 773 a[from] = pivot;
715 var low_end = from; // Upper bound of the elements lower than pivot. 774 var low_end = from; // Upper bound of the elements lower than pivot.
716 var high_start = to; // Lower bound of the elements greater than pivot. 775 var high_start = to; // Lower bound of the elements greater than pivot.
717 // From low_end to i are elements equal to pivot. 776 // From low_end to i are elements equal to pivot.
718 // From i to high_start are elements that haven't been compared yet. 777 // From i to high_start are elements that haven't been compared yet.
719 for (var i = from + 1; i < high_start; ) { 778 for (var i = from + 1; i < high_start; ) {
720 var element = a[i]; 779 var element = a[i];
721 var order = Compare(element, pivot_key); 780 var order = Compare(element, pivot_key);
722 if (order < 0) { 781 if (order < 0) {
723 a[i] = a[low_end]; 782 a[i] = a[low_end];
724 a[low_end] = element; 783 a[low_end] = element;
725 i++; 784 i++;
726 low_end++; 785 low_end++;
727 } else if (order > 0) { 786 } else if (order > 0) {
728 high_start--; 787 high_start--;
729 a[i] = a[high_start]; 788 a[i] = a[high_start];
730 a[high_start] = element; 789 a[high_start] = element;
731 } else { // order == 0 790 } else { // order == 0
732 i++; 791 i++;
733 } 792 }
734 } 793 }
735 QuickSort(a, from, low_end); 794 QuickSort(a, from, low_end);
736 QuickSort(a, high_start, to); 795 QuickSort(a, high_start, to);
737 } 796 }
738 797
739 var length;
740
741 // Copies elements in the range 0..length from obj's prototype chain 798 // Copies elements in the range 0..length from obj's prototype chain
742 // to obj itself, if obj has holes. Returns one more than the maximal index 799 // to obj itself, if obj has holes. Returns one more than the maximal index
743 // of a prototype property. 800 // of a prototype property.
744 function CopyFromPrototype(obj, length) { 801 function CopyFromPrototype(obj, length) {
745 var max = 0; 802 var max = 0;
746 for (var proto = obj.__proto__; proto; proto = proto.__proto__) { 803 for (var proto = obj.__proto__; proto; proto = proto.__proto__) {
747 var indices = %GetArrayKeys(proto, length); 804 var indices = %GetArrayKeys(proto, length);
748 if (indices.length > 0) { 805 if (indices.length > 0) {
749 if (indices[0] == -1) { 806 if (indices[0] == -1) {
750 // It's an interval. 807 // It's an interval.
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
848 obj[i] = void 0; 905 obj[i] = void 0;
849 } else { 906 } else {
850 delete obj[i]; 907 delete obj[i];
851 } 908 }
852 } 909 }
853 910
854 // Return the number of defined elements. 911 // Return the number of defined elements.
855 return first_undefined; 912 return first_undefined;
856 } 913 }
857 914
858 length = TO_UINT32(this.length); 915 var length = TO_UINT32(this.length);
859 if (length < 2) return this; 916 if (length < 2) return this;
860 917
861 var is_array = IS_ARRAY(this); 918 var is_array = IS_ARRAY(this);
862 var max_prototype_element; 919 var max_prototype_element;
863 if (!is_array) { 920 if (!is_array) {
864 // For compatibility with JSC, we also sort elements inherited from 921 // For compatibility with JSC, we also sort elements inherited from
865 // the prototype chain on non-Array objects. 922 // the prototype chain on non-Array objects.
866 // We do this by copying them to this object and sorting only 923 // We do this by copying them to this object and sorting only
867 // local elements. This is not very efficient, but sorting with 924 // local elements. This is not very efficient, but sorting with
868 // inherited elements happens very, very rarely, if at all. 925 // inherited elements happens very, very rarely, if at all.
869 // The specification allows "implementation dependent" behavior 926 // The specification allows "implementation dependent" behavior
870 // if an element on the prototype chain has an element that 927 // if an element on the prototype chain has an element that
871 // might interact with sorting. 928 // might interact with sorting.
872 max_prototype_element = CopyFromPrototype(this, length); 929 max_prototype_element = CopyFromPrototype(this, length);
873 } 930 }
874 931
875 var num_non_undefined = %RemoveArrayHoles(this, length); 932 var num_non_undefined = %RemoveArrayHoles(this, length);
876 if (num_non_undefined == -1) { 933 if (num_non_undefined == -1) {
877 // There were indexed accessors in the array. Move array holes and 934 // There were indexed accessors in the array. Move array holes and
878 // undefineds to the end using a Javascript function that is safe 935 // undefineds to the end using a Javascript function that is safe
879 // in the presence of accessors. 936 // in the presence of accessors.
880 num_non_undefined = SafeRemoveArrayHoles(this); 937 num_non_undefined = SafeRemoveArrayHoles(this);
881 } 938 }
882 939
883 QuickSort(this, 0, num_non_undefined); 940 if(IS_FUNCTION(comparefn)) {
941 QuickSortWithFunc(this, 0, num_non_undefined);
942 } else {
943 QuickSort(this, 0, num_non_undefined);
944 }
884 945
885 if (!is_array && (num_non_undefined + 1 < max_prototype_element)) { 946 if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
886 // For compatibility with JSC, we shadow any elements in the prototype 947 // For compatibility with JSC, we shadow any elements in the prototype
887 // chain that has become exposed by sort moving a hole to its position. 948 // chain that has become exposed by sort moving a hole to its position.
888 ShadowPrototypeElements(this, num_non_undefined, max_prototype_element); 949 ShadowPrototypeElements(this, num_non_undefined, max_prototype_element);
889 } 950 }
890 951
891 return this; 952 return this;
892 } 953 }
893 954
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after
1149 "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1), 1210 "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
1150 "reduce", getFunction("reduce", ArrayReduce, 1), 1211 "reduce", getFunction("reduce", ArrayReduce, 1),
1151 "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1) 1212 "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1)
1152 )); 1213 ));
1153 1214
1154 %FinishArrayPrototypeSetup($Array.prototype); 1215 %FinishArrayPrototypeSetup($Array.prototype);
1155 } 1216 }
1156 1217
1157 1218
1158 SetupArray(); 1219 SetupArray();
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698