From dc30092b605c9f1f6c92a49f4aae2be6cbe883be Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Thu, 7 Jun 2012 23:25:58 +0200 Subject: [PATCH 01/10] A very fast sortUsingDescriptors: I needed to sort a lot of objects and was not impressed by the speed. Sharing my improvements... --- Foundation/CPArray/CPMutableArray.j | 191 +++++++++++++++++++++------- 1 file changed, 148 insertions(+), 43 deletions(-) diff --git a/Foundation/CPArray/CPMutableArray.j b/Foundation/CPArray/CPMutableArray.j index 420bf0873..f62ae1c06 100644 --- a/Foundation/CPArray/CPMutableArray.j +++ b/Foundation/CPArray/CPMutableArray.j @@ -334,7 +334,15 @@ - (void)sortUsingDescriptors:(CPArray)descriptors { - [self sortUsingFunction:compareObjectsUsingDescriptors context:descriptors]; + var i = [descriptors count], + jsDescriptors = []; + // Revert the order of the descriptors + while (i--) + { + var d = [descriptors objectAtIndex:i]; + [jsDescriptors addObject:{ "k": [d key], "a": [d ascending]}]; + } + sortArrayUsingJSDescriptors(self, jsDescriptors); } /*! @@ -344,46 +352,7 @@ */ - (void)sortUsingFunction:(Function)aFunction context:(id)aContext { - var h, - i, - j, - k, - l, - m, - n = [self count], - o; - - var A, - B = []; - - for (h = 1; h < n; h += h) - { - for (m = n - 1 - h; m >= 0; m -= h + h) - { - l = m - h + 1; - if (l < 0) - l = 0; - - for (i = 0, j = l; j <= m; i++, j++) - B[i] = self[j]; - - for (i = 0, k = l; k < j && j <= m + h; k++) - { - A = self[j]; - o = aFunction(A, B[i], aContext); - if (o >= 0) - self[k] = B[i++]; - else - { - self[k] = A; - j++; - } - } - - while (k < j) - self[k++] = B[i++]; - } - } + sortArrayUsingFunction(self, aFunction, aContext); } /*! @@ -392,7 +361,7 @@ */ - (void)sortUsingSelector:(SEL)aSelector { - [self sortUsingFunction:selectorCompare context:aSelector]; + sortArrayUsingFunction(self, selectorCompare, aSelector); } @end @@ -413,8 +382,52 @@ var selectorCompare = function selectorCompare(object1, object2, selector) return [object1 performSelector:selector withObject:object2]; }; +var sortArrayUsingFunction = function sortArrayUsingFunction(array, aFunction, aContext) +{ + var h, + i, + j, + k, + l, + m, + n = array.length, + o; + + var A, + B = []; + + for (h = 1; h < n; h += h) + { + for (m = n - 1 - h; m >= 0; m -= h + h) + { + l = m - h + 1; + if (l < 0) + l = 0; + + for (i = 0, j = l; j <= m; i++, j++) + B[i] = array[j]; + + for (i = 0, k = l; k < j && j <= m + h; k++) + { + A = array[j]; + o = aFunction(A, B[i], aContext); + if (o >= 0) + array[k] = B[i++]; + else + { + array[k] = A; + j++; + } + } + + while (k < j) + array[k++] = B[i++]; + } + } +} + // sort using sort descriptors -var compareObjectsUsingDescriptors= function compareObjectsUsingDescriptors(lhs, rhs, descriptors) +var compareObjectsUsingDescriptors = function compareObjectsUsingDescriptors(lhs, rhs, descriptors) { var result = CPOrderedSame, i = 0, @@ -425,3 +438,95 @@ var compareObjectsUsingDescriptors= function compareObjectsUsingDescriptors(lhs, return result; }; + +// Observe that the sort descriptors has the reversed order by the caller +var sortArrayUsingJSDescriptors = function sortArrayUsingJSDescriptors(a, d) +{ + var h, + i, + j, + k, + l, + m, + n = a.length, + dl = d.length - 1, + o, + c = {}; + + var A, + B = [], + C1, + C2, + cn, + aUID, + bUID, + key; + + if (dl < 0) + return; + for (h = 1; h < n; h += h) + { + for (m = n - 1 - h; m >= 0; m -= h + h) + { + l = m - h + 1; + if (l < 0) + l = 0; + + for (i = 0, j = l; j <= m; i++, j++) + B[i] = a[j]; + + for (i = 0, k = l; k < j && j <= m + h; k++) + { + A = a[j]; + aUID = A._UID; + if (!aUID) + aUID = [A UID]; + C1 = c[aUID]; + if (!C1) + { + C1 = {}; + cn = dl; + do + { + key = d[cn].k; + C1[key] = [A valueForKey:key]; + } while (cn--) + c[aUID] = C1; + } + bUID = B[i]._UID; + if (!bUID) + bUID = [B[i] UID]; + C2 = c[bUID]; + if (!C2) + { + C2 = {}; + cn = dl; + do + { + key = d[cn].k; + C2[key] = [B[i] valueForKey:key]; + } while (cn--) + c[bUID] = C2; + } + cn = dl; + do + { + key = d[cn].k; + o = [C1[key] compare:C2[key]]; + if (o && !d[cn].a) + o = -o; + } while (cn-- && o == CPOrderedSame) + if (o >= 0) + a[k] = B[i++]; + else + { + a[k] = A; + j++; + } + } + + while (k < j) + a[k++] = B[i++]; + } + } +} From 0483f9d7142e092da244407467a3a0b9038aa661 Mon Sep 17 00:00:00 2001 From: cacaodev Date: Fri, 8 Jun 2012 18:19:37 +0200 Subject: [PATCH 02/10] CPArrayPerformanceTest: fixed native sort testing, added tests for random numeric array and text array --- Tests/Foundation/CPArrayPerformanceTest.j | 180 ++++++++++++++++++---- Tests/Foundation/the_big_sort.txt | 1 + 2 files changed, 150 insertions(+), 31 deletions(-) create mode 100644 Tests/Foundation/the_big_sort.txt diff --git a/Tests/Foundation/CPArrayPerformanceTest.j b/Tests/Foundation/CPArrayPerformanceTest.j index 573e54dab..7c7339b2a 100644 --- a/Tests/Foundation/CPArrayPerformanceTest.j +++ b/Tests/Foundation/CPArrayPerformanceTest.j @@ -1,72 +1,111 @@ +var FILE = require("file"); + @import @import @import @import - var ELEMENTS = 100, REPEATS = 10; @implementation CPArrayPerformanceTest : OJTestCase - -- (void)testSortUsingDescriptorsSpeed { - var array = [self makeUnsorted]; + CPArray descriptors; + CPString the_big_sort; +} +- (void)setUp +{ var descriptors = [ [CPSortDescriptor sortDescriptorWithKey:"a" ascending:NO], [CPSortDescriptor sortDescriptorWithKey:"b" ascending:YES] ]; + the_big_sort = FILE.read(FILE.join(FILE.dirname(module.path), "the_big_sort.txt"), { charset:"UTF-8" }); +} + +- (void)testAlmostSortedNumericUsingMergeSort +{ + CPLog.warn("\nNUMERIC ALMOST SORTED"); + var a = [self makeUnsorted]; + var sorted = [self sortUsingMergeSort:a]; + [self checkAlmostSorted:sorted]; +} + +- (void)testAlmostSortedNumericUsingNativeSort +{ + var a = [self makeUnsorted]; + var sorted = [self sortUsingNativeSort:a]; + [self checkAlmostSorted:sorted]; +} + +- (void)testRandomNumericUsingMergeSort +{ + CPLog.warn("\nNUMERIC RANDOM"); + var a = [self makeRandomNumeric]; + var sorted = [self sortUsingMergeSort:a]; + [self checkRandomSorted:sorted]; +} + +- (void)testRandomNumericUsingNativeSort +{ + var a = [self makeRandomNumeric]; + var sorted = [self sortUsingNativeSort:a]; + [self checkRandomSorted:sorted]; +} + +- (void)testRandomTextUsingMergeSort +{ + CPLog.warn("\nTEXT RANDOM"); + var a = [self makeRandomText]; + var sorted = [self sortUsingMergeSort:a]; + [self checkRandomSorted:sorted]; +} + +- (void)testRandomTextUsingNativeSort +{ + var a = [self makeRandomText]; + var sorted = [self sortUsingNativeSort:a]; + [self checkRandomSorted:sorted]; +} + +- (CPArray)sortUsingMergeSort:(CPArray)anArray +{ + var sorted; + var start = (new Date).getTime(); for (var i = 0; i < REPEATS; ++i) - { - var sorted = [array sortedArrayUsingDescriptors:descriptors]; - - [self checkSorted:sorted]; - } + sorted = [anArray sortedArrayUsingDescriptors:descriptors]; var end = (new Date).getTime(); CPLog.warn(_cmd + ": " + (end - start) + "ms"); + + return sorted; } -- (void)testSortUsingNativeSort +- (void)sortUsingNativeSort:(CPArray)anArray { - var array = [self makeUnsorted]; - - var descriptors = [ - [CPSortDescriptor sortDescriptorWithKey:"a" ascending:NO], - [CPSortDescriptor sortDescriptorWithKey:"b" ascending:YES] - ]; - - function sortFunction(lhs, rhs) - { - return ([lhs a] === [rhs a]) ? ([lhs b] - [rhs b]) : ([rhs a] - [lhs a]); - } + var sorted; var start = (new Date).getTime(); for (var i = 0; i < REPEATS; ++i) - { - var sorted = [array copy]; - - sorted.sort(sortFunction) - - [self checkSorted:sorted]; - } + sorted = [anArray _native_sortedArrayUsingDescriptors:descriptors]; var end = (new Date).getTime(); CPLog.warn(_cmd+": " + (end - start) + "ms"); + + return sorted; } - (CPArray)makeUnsorted { var array = []; - for (var i=0; i < ELEMENTS; ++i) + for (var i = 0; i < ELEMENTS; ++i) { var s = [Sortable new]; @@ -79,10 +118,46 @@ var ELEMENTS = 100, return array; } -- (void)checkSorted:(CPArray)sorted +- (CPArray)makeRandomNumeric +{ + var array = []; + + for (var i = 0; i < ELEMENTS; i++) + { + var s = [Sortable new], + n1 = ROUND(RAND() * ELEMENTS); + n2 = ROUND(RAND() * ELEMENTS); + + [s setA:n1]; + [s setB:n2]; + array.push(s); + } + + return array; +} + +- (CPArray)makeRandomText +{ + var words = the_big_sort.split(" ", ELEMENTS), + wordcount = words.length, + array = []; + + for (var i=0; i < wordcount-1; i++) + { + var s = [Sortable new]; + + [s setA:words[i]]; + [s setB:words[i+1]]; + array.push(s); + } + + return array; +} + +- (void)checkAlmostSorted:(CPArray)sorted { // Verify it really got sorted. - for (var j=0; j < ELEMENTS; ++j) + for (var j=0; j < sorted.length; ++j) { var expectedA = 4-FLOOR(j * 5 / ELEMENTS); @@ -96,6 +171,49 @@ var ELEMENTS = 100, } } +- (void)checkRandomSorted:(CPArray)sorted +{ + // Verify it really got sorted. + for (var j=0; j < sorted.length - 1; ++j) + { + if ([sorted[j].a compare:sorted[j+1].a] == CPOrderedAscending) + [self fail:"a out of order: " + sorted[j].a + " > " + sorted[j+1].a]; + + if ([sorted[j].a compare:sorted[j+1].a] == CPOrderedSame && [sorted[j].b compare: sorted[j+1].b] == CPOrderedDescending) + [self fail:"b out of order: " + sorted[j].b + " < " + sorted[j+1].b]; + } +} + +@end + +@implementation CPArray (NativeSort) + +- (CPArray)_native_sortedArrayUsingDescriptors:(CPArray)descriptors +{ + var sorted = [self copy]; + + [sorted _native_sortUsingDescriptors:descriptors]; + + return sorted; +} + +- (CPArray)_native_sortUsingDescriptors:(CPArray)descriptors +{ + var count = [descriptors count]; + + sort(function(lhs, rhs) + { + var i = 0, + result = CPOrderedSame; + + while (i < count) + if ((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame) + return result; + + return result; + }); +} + @end @implementation Sortable : CPObject diff --git a/Tests/Foundation/the_big_sort.txt b/Tests/Foundation/the_big_sort.txt new file mode 100644 index 000000000..871ec5b6a --- /dev/null +++ b/Tests/Foundation/the_big_sort.txt @@ -0,0 +1 @@ +Introduction We have a neighborhood Internet listserve in South Austin that is often a source of good information about painters, plumbers, and lost animals. The e-discussion can also become a parody of liberal preciousness. One participant wrote to say that he planned to live-trap rats that had invaded his garage. The vermin-friendly homeowner wanted to know where it might be safe to relocate the rodents. The one Republican on the newsgroup, Stephen Mason, dared to say what most of us probably thought, volunteering that the man could release the varmints near Masons rat terrier, Hotard, who would happily relocate them to rat heaven. Our lone conservative correspondent, however, knew better than to make his comments overtly political. Mason, an intellectual property attorney and Texas A&M graduate, had tried to start a genuine political discussion on the list the year before. He wasnt about to try again. It was the spring of 2004, so things were already tense when Mason called the newsgroups attention to the election for the board of the local community college. Mason gave the names of both candidates, listed their websites, and then, after a warning that what followed was possibly inappropriate electioneering, recommended one of the candidates. The man Mason backed was deeply conservative, a member of the Federalist Society, a former officer of the Young Conservatives of Texas, and an opponent of gay marriage and adoption. Within the day, the newsgroup reacted in a way that wasnt as much ideological as biological. Mason wasnt just someone to be argued against. For the protection of the group, he needed to be isolated, sealed off, and expelled. First response: Okay, as a member of this list, Id really like to see this political discussion disappear. As a lesbian, obviously Im not going to vote for anybody who doesnt believe I shouldnt be allowed to adopt kids . . . As a resident of Travis Heights and a member of this neighborhood list, Im not interested in having this kind of discussion here. I have to defend myself against my government pretty much daily these days, and one place I dont want to have to do it is on this list. And then: A-men. Stephen, youre in the minority politically on this list and in this neighborhood, and while your opinions are your own to have, this list isnt the place for them . . . T-Hts is my home, and this list an extension of that . . . I hope we can all agree to prevent it from becoming a battleground. Mason responded: [The] ideological balkanization of America is dead-on true . . . Living here, especially as an out Republican, is a great deal of fun, and I learn a great deal from it. The most valuable thing that I learn daily is the capacity to respect people with whom I have disagreements. I hope not to be exiled to some place where the vast majority agrees with me. Then, alas, enough with the calls for reasoned discussion: Im really not interested [in] being surprised by right-wing e-mail in my in box, no matter what its guise. It makes me feel bad, and I dont like it. There were dissenting views. I really dont want political or other uncomfortable subjects segregated into some kind of opinion ghetto where only proselytizing activists and ideologues would venture, wrote one South Austinite. Another: Im going to stick my neck out here. I DO want to have political discussion . . . It is a tragedy that this country is becoming more and more polarized. Neighborhoods are becoming more and more homogeneous, either Democrat or Republican. Talk about segregation! But Mason got the picture: he was a Republican Crusoe on this Democratic island, and so he withdrew, promising never to talk politics again with his neighbors. Look, I know a lot about homogeneous political communities, Mason said one afternoon at a South Austin coffeehouse. Hed grown up in the Republican suburbs of Houston and attended the conservative hive of Texas A&M University. He was pugnacious about his politics, ending every e-mail message with a quote from Teddy Roosevelt: Aggressive fighting for the right is the greatest sport in the world. Mason was also engaged to marry a feminist filmmaker, an experience that taught him that nearly everything is political. But nothing had prepared him for the unsettling experience of being a political minority in the community where he lived — for being a minority in the age of political segregation. I ran off that listserve when the rhetoric became so shrill that I didnt have the taste for saying about anyone the things you would need to say to win that argument, Mason continued. Im not going to use that listserve for politics again, and there is some shame in that. The experience had changed the way he saw himself in the neighborhood. Knowing he was a minority, he wondered what people thought of him as he walked Hotard. In some way after that exchange, I think Im viewed with suspicion by my neighbors because of an act of political expression, which is a little on the bizarre side, he said. Im just a guy who has a dog and works a job. Chapter 3: The Psychology of the Tribe Washington was, from its beginning, a politically segregated city. In his forty-year-old study, The Washington Community, historian James Sterling Young mapped three Washingtons, one created for each of the three branches of government. The nine members of the U.S. Supreme Court lived in the same house until 1845. Executive branch workers gathered in one section of the city, near the White House, while congressmen were bunched together nearer the Capitol. Men whom the Constitution merely separated into different work groups separated themselves into different societies, wrote Young. Congressmen lived in boarding houses. They formed eating clubs around common tables, and they slept together, two to a room. Young tracked the membership of these new boarding house communities and found that the residential \ No newline at end of file From d7ff93f7f27c4ab048b7e79d9311b7fb13d94e8e Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Fri, 8 Jun 2012 19:48:39 +0200 Subject: [PATCH 03/10] SortUsingDescriptors: Using the selector in the descriptor instead of always using compare: --- Foundation/CPArray/CPMutableArray.j | 12 +++++++----- Tests/Foundation/CPArrayTest.j | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Foundation/CPArray/CPMutableArray.j b/Foundation/CPArray/CPMutableArray.j index f62ae1c06..2f6fcc58f 100644 --- a/Foundation/CPArray/CPMutableArray.j +++ b/Foundation/CPArray/CPMutableArray.j @@ -340,7 +340,7 @@ while (i--) { var d = [descriptors objectAtIndex:i]; - [jsDescriptors addObject:{ "k": [d key], "a": [d ascending]}]; + [jsDescriptors addObject:{ "k": [d key], "a": [d ascending], "s": [d selector]}]; } sortArrayUsingJSDescriptors(self, jsDescriptors); } @@ -460,7 +460,8 @@ var sortArrayUsingJSDescriptors = function sortArrayUsingJSDescriptors(a, d) cn, aUID, bUID, - key; + key, + dd; if (dl < 0) return; @@ -511,9 +512,10 @@ var sortArrayUsingJSDescriptors = function sortArrayUsingJSDescriptors(a, d) cn = dl; do { - key = d[cn].k; - o = [C1[key] compare:C2[key]]; - if (o && !d[cn].a) + dd = d[cn]; + key = dd.k; + o = objj_msgSend(C1[key], dd.s, C2[key]); + if (o && !dd.a) o = -o; } while (cn-- && o == CPOrderedSame) if (o >= 0) diff --git a/Tests/Foundation/CPArrayTest.j b/Tests/Foundation/CPArrayTest.j index 75951c471..a99dfc931 100644 --- a/Tests/Foundation/CPArrayTest.j +++ b/Tests/Foundation/CPArrayTest.j @@ -539,6 +539,20 @@ [self assertTrue:d.indexOf("y: 6") !== -1 message:"Can't find 'y: 6' in description of array " + d]; } +- (void)testSortUsingDescriptorsWithDifferentSelectors +{ + var a = [CPDictionary dictionaryWithJSObject:{"a": "AB", "b": "ba"}], + b = [CPDictionary dictionaryWithJSObject:{"a": "aa", "b": "BB"}], + array = [a, b], + d1 = [[CPSortDescriptor sortDescriptorWithKey:@"a" ascending:YES selector:@selector(compare:)]], + d2 = [[CPSortDescriptor sortDescriptorWithKey:@"a" ascending:YES selector:@selector(caseInsensitiveCompare:)]], + s1 = [array sortedArrayUsingDescriptors:d1], + s2 = [array sortedArrayUsingDescriptors:d2]; + + [self assertTrue:s1[0] === a message:s1[0] + " is larger then " + a + " when sorting case sensitive"]; + [self assertTrue:s2[1] === a message:s2[1] + " is larger then " + a + " when sorting case insensitive"]; +} + @end @implementation AlwaysEqual : CPObject From 0696b22971b324bcfeaff63c92811e410614aa58 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Fri, 8 Jun 2012 20:18:22 +0200 Subject: [PATCH 04/10] Fixed formatting --- Foundation/CPArray/CPMutableArray.j | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/Foundation/CPArray/CPMutableArray.j b/Foundation/CPArray/CPMutableArray.j index 2f6fcc58f..42e3a88de 100644 --- a/Foundation/CPArray/CPMutableArray.j +++ b/Foundation/CPArray/CPMutableArray.j @@ -336,6 +336,7 @@ { var i = [descriptors count], jsDescriptors = []; + // Revert the order of the descriptors while (i--) { @@ -377,12 +378,12 @@ @end -var selectorCompare = function selectorCompare(object1, object2, selector) +var selectorCompare = function(object1, object2, selector) { return [object1 performSelector:selector withObject:object2]; }; -var sortArrayUsingFunction = function sortArrayUsingFunction(array, aFunction, aContext) +var sortArrayUsingFunction = function(array, aFunction, aContext) { var h, i, @@ -411,6 +412,7 @@ var sortArrayUsingFunction = function sortArrayUsingFunction(array, aFunction, a { A = array[j]; o = aFunction(A, B[i], aContext); + if (o >= 0) array[k] = B[i++]; else @@ -427,7 +429,7 @@ var sortArrayUsingFunction = function sortArrayUsingFunction(array, aFunction, a } // sort using sort descriptors -var compareObjectsUsingDescriptors = function compareObjectsUsingDescriptors(lhs, rhs, descriptors) +var compareObjectsUsingDescriptors = function(lhs, rhs, descriptors) { var result = CPOrderedSame, i = 0, @@ -440,7 +442,7 @@ var compareObjectsUsingDescriptors = function compareObjectsUsingDescriptors(lhs }; // Observe that the sort descriptors has the reversed order by the caller -var sortArrayUsingJSDescriptors = function sortArrayUsingJSDescriptors(a, d) +var sortArrayUsingJSDescriptors = function(a, d) { var h, i, @@ -465,11 +467,13 @@ var sortArrayUsingJSDescriptors = function sortArrayUsingJSDescriptors(a, d) if (dl < 0) return; + for (h = 1; h < n; h += h) { for (m = n - 1 - h; m >= 0; m -= h + h) { l = m - h + 1; + if (l < 0) l = 0; @@ -480,44 +484,59 @@ var sortArrayUsingJSDescriptors = function sortArrayUsingJSDescriptors(a, d) { A = a[j]; aUID = A._UID; + if (!aUID) aUID = [A UID]; + C1 = c[aUID]; + if (!C1) { C1 = {}; cn = dl; + do { key = d[cn].k; C1[key] = [A valueForKey:key]; } while (cn--) + c[aUID] = C1; } + bUID = B[i]._UID; + if (!bUID) bUID = [B[i] UID]; + C2 = c[bUID]; + if (!C2) { C2 = {}; cn = dl; + do { key = d[cn].k; C2[key] = [B[i] valueForKey:key]; } while (cn--) + c[bUID] = C2; } + cn = dl; + do { dd = d[cn]; key = dd.k; o = objj_msgSend(C1[key], dd.s, C2[key]); + if (o && !dd.a) o = -o; } while (cn-- && o == CPOrderedSame) + if (o >= 0) a[k] = B[i++]; else From 7b307a6fb491aa7b1277225eb7ec120b5b55fff7 Mon Sep 17 00:00:00 2001 From: Alexander Ljungberg Date: Sat, 9 Jun 2012 23:08:27 +0100 Subject: [PATCH 05/10] Re #1562. Format code. --- Tests/Foundation/CPArrayPerformanceTest.j | 64 +++++++++++------------ 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/Tests/Foundation/CPArrayPerformanceTest.j b/Tests/Foundation/CPArrayPerformanceTest.j index 7c7339b2a..cf6db1187 100644 --- a/Tests/Foundation/CPArrayPerformanceTest.j +++ b/Tests/Foundation/CPArrayPerformanceTest.j @@ -17,9 +17,9 @@ var ELEMENTS = 100, - (void)setUp { var descriptors = [ - [CPSortDescriptor sortDescriptorWithKey:"a" ascending:NO], - [CPSortDescriptor sortDescriptorWithKey:"b" ascending:YES] - ]; + [CPSortDescriptor sortDescriptorWithKey:"a" ascending:NO], + [CPSortDescriptor sortDescriptorWithKey:"b" ascending:YES] + ]; the_big_sort = FILE.read(FILE.join(FILE.dirname(module.path), "the_big_sort.txt"), { charset:"UTF-8" }); } @@ -27,53 +27,52 @@ var ELEMENTS = 100, - (void)testAlmostSortedNumericUsingMergeSort { CPLog.warn("\nNUMERIC ALMOST SORTED"); - var a = [self makeUnsorted]; - var sorted = [self sortUsingMergeSort:a]; + var a = [self makeUnsorted], + sorted = [self sortUsingMergeSort:a]; [self checkAlmostSorted:sorted]; } - (void)testAlmostSortedNumericUsingNativeSort { - var a = [self makeUnsorted]; - var sorted = [self sortUsingNativeSort:a]; + var a = [self makeUnsorted], + sorted = [self sortUsingNativeSort:a]; [self checkAlmostSorted:sorted]; } - (void)testRandomNumericUsingMergeSort { CPLog.warn("\nNUMERIC RANDOM"); - var a = [self makeRandomNumeric]; - var sorted = [self sortUsingMergeSort:a]; + var a = [self makeRandomNumeric], + sorted = [self sortUsingMergeSort:a]; [self checkRandomSorted:sorted]; } - (void)testRandomNumericUsingNativeSort { - var a = [self makeRandomNumeric]; - var sorted = [self sortUsingNativeSort:a]; + var a = [self makeRandomNumeric], + sorted = [self sortUsingNativeSort:a]; [self checkRandomSorted:sorted]; } - (void)testRandomTextUsingMergeSort { CPLog.warn("\nTEXT RANDOM"); - var a = [self makeRandomText]; - var sorted = [self sortUsingMergeSort:a]; + var a = [self makeRandomText], + sorted = [self sortUsingMergeSort:a]; [self checkRandomSorted:sorted]; } - (void)testRandomTextUsingNativeSort { - var a = [self makeRandomText]; - var sorted = [self sortUsingNativeSort:a]; + var a = [self makeRandomText], + sorted = [self sortUsingNativeSort:a]; [self checkRandomSorted:sorted]; } - (CPArray)sortUsingMergeSort:(CPArray)anArray { - var sorted; - - var start = (new Date).getTime(); + var sorted, + start = (new Date).getTime(); for (var i = 0; i < REPEATS; ++i) sorted = [anArray sortedArrayUsingDescriptors:descriptors]; @@ -87,16 +86,15 @@ var ELEMENTS = 100, - (void)sortUsingNativeSort:(CPArray)anArray { - var sorted; - - var start = (new Date).getTime(); + var sorted, + start = (new Date).getTime(); for (var i = 0; i < REPEATS; ++i) sorted = [anArray _native_sortedArrayUsingDescriptors:descriptors]; var end = (new Date).getTime(); - CPLog.warn(_cmd+": " + (end - start) + "ms"); + CPLog.warn(_cmd + ": " + (end - start) + "ms"); return sorted; } @@ -125,7 +123,7 @@ var ELEMENTS = 100, for (var i = 0; i < ELEMENTS; i++) { var s = [Sortable new], - n1 = ROUND(RAND() * ELEMENTS); + n1 = ROUND(RAND() * ELEMENTS), n2 = ROUND(RAND() * ELEMENTS); [s setA:n1]; @@ -142,12 +140,12 @@ var ELEMENTS = 100, wordcount = words.length, array = []; - for (var i=0; i < wordcount-1; i++) + for (var i = 0; i < wordcount - 1; i++) { var s = [Sortable new]; [s setA:words[i]]; - [s setB:words[i+1]]; + [s setB:words[i + 1]]; array.push(s); } @@ -157,9 +155,9 @@ var ELEMENTS = 100, - (void)checkAlmostSorted:(CPArray)sorted { // Verify it really got sorted. - for (var j=0; j < sorted.length; ++j) + for (var j = 0; j < sorted.length; ++j) { - var expectedA = 4-FLOOR(j * 5 / ELEMENTS); + var expectedA = 4 - FLOOR(j * 5 / ELEMENTS); if (sorted[j].a != expectedA) [self fail:"a out of order: " + expectedA + " != " + sorted[j].a]; @@ -173,14 +171,14 @@ var ELEMENTS = 100, - (void)checkRandomSorted:(CPArray)sorted { - // Verify it really got sorted. - for (var j=0; j < sorted.length - 1; ++j) + // Verify that it really got sorted. + for (var j = 0; j < sorted.length - 1; ++j) { - if ([sorted[j].a compare:sorted[j+1].a] == CPOrderedAscending) - [self fail:"a out of order: " + sorted[j].a + " > " + sorted[j+1].a]; + if ([sorted[j].a compare:sorted[j + 1].a] == CPOrderedAscending) + [self fail:"a out of order: " + sorted[j].a + " > " + sorted[j + 1].a]; - if ([sorted[j].a compare:sorted[j+1].a] == CPOrderedSame && [sorted[j].b compare: sorted[j+1].b] == CPOrderedDescending) - [self fail:"b out of order: " + sorted[j].b + " < " + sorted[j+1].b]; + if ([sorted[j].a compare:sorted[j + 1].a] == CPOrderedSame && [sorted[j].b compare: sorted[j + 1].b] == CPOrderedDescending) + [self fail:"b out of order: " + sorted[j].b + " < " + sorted[j + 1].b]; } } From f042996e7f9b690c5f726f76a23a31aa19c5117d Mon Sep 17 00:00:00 2001 From: Alexander Ljungberg Date: Sat, 9 Jun 2012 23:16:52 +0100 Subject: [PATCH 06/10] Re #1562. Only load sort text for tests which use it. --- Tests/Foundation/CPArrayPerformanceTest.j | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Tests/Foundation/CPArrayPerformanceTest.j b/Tests/Foundation/CPArrayPerformanceTest.j index cf6db1187..c31c11a71 100644 --- a/Tests/Foundation/CPArrayPerformanceTest.j +++ b/Tests/Foundation/CPArrayPerformanceTest.j @@ -11,7 +11,6 @@ var ELEMENTS = 100, @implementation CPArrayPerformanceTest : OJTestCase { CPArray descriptors; - CPString the_big_sort; } - (void)setUp @@ -20,8 +19,6 @@ var ELEMENTS = 100, [CPSortDescriptor sortDescriptorWithKey:"a" ascending:NO], [CPSortDescriptor sortDescriptorWithKey:"b" ascending:YES] ]; - - the_big_sort = FILE.read(FILE.join(FILE.dirname(module.path), "the_big_sort.txt"), { charset:"UTF-8" }); } - (void)testAlmostSortedNumericUsingMergeSort @@ -136,7 +133,8 @@ var ELEMENTS = 100, - (CPArray)makeRandomText { - var words = the_big_sort.split(" ", ELEMENTS), + var the_big_sort = FILE.read(FILE.join(FILE.dirname(module.path), "the_big_sort.txt"), { charset:"UTF-8" }), + words = the_big_sort.split(" ", ELEMENTS), wordcount = words.length, array = []; From 0827f584be59bec0206dd1c0ae384cbd064b6131 Mon Sep 17 00:00:00 2001 From: Alexander Ljungberg Date: Sun, 10 Jun 2012 00:39:37 +0100 Subject: [PATCH 07/10] Re #1562. Simplify. --- Tests/Foundation/CPArrayPerformanceTest.j | 31 ++++++----------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/Tests/Foundation/CPArrayPerformanceTest.j b/Tests/Foundation/CPArrayPerformanceTest.j index c31c11a71..4407befe8 100644 --- a/Tests/Foundation/CPArrayPerformanceTest.j +++ b/Tests/Foundation/CPArrayPerformanceTest.j @@ -25,14 +25,14 @@ var ELEMENTS = 100, { CPLog.warn("\nNUMERIC ALMOST SORTED"); var a = [self makeUnsorted], - sorted = [self sortUsingMergeSort:a]; + sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingDescriptors:) withObject:descriptors]; [self checkAlmostSorted:sorted]; } - (void)testAlmostSortedNumericUsingNativeSort { var a = [self makeUnsorted], - sorted = [self sortUsingNativeSort:a]; + sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingDescriptors:) withObject:descriptors]; [self checkAlmostSorted:sorted]; } @@ -40,14 +40,14 @@ var ELEMENTS = 100, { CPLog.warn("\nNUMERIC RANDOM"); var a = [self makeRandomNumeric], - sorted = [self sortUsingMergeSort:a]; + sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingDescriptors:) withObject:descriptors]; [self checkRandomSorted:sorted]; } - (void)testRandomNumericUsingNativeSort { var a = [self makeRandomNumeric], - sorted = [self sortUsingNativeSort:a]; + sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingDescriptors:) withObject:descriptors]; [self checkRandomSorted:sorted]; } @@ -55,39 +55,24 @@ var ELEMENTS = 100, { CPLog.warn("\nTEXT RANDOM"); var a = [self makeRandomText], - sorted = [self sortUsingMergeSort:a]; + sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingDescriptors:) withObject:descriptors]; [self checkRandomSorted:sorted]; } - (void)testRandomTextUsingNativeSort { var a = [self makeRandomText], - sorted = [self sortUsingNativeSort:a]; + sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingDescriptors:) withObject:descriptors]; [self checkRandomSorted:sorted]; } -- (CPArray)sortUsingMergeSort:(CPArray)anArray +- (CPArray)sort:(CPArray)anArray usingSortSelector:(SEL)aSelector withObject:(id)anObject { var sorted, start = (new Date).getTime(); for (var i = 0; i < REPEATS; ++i) - sorted = [anArray sortedArrayUsingDescriptors:descriptors]; - - var end = (new Date).getTime(); - - CPLog.warn(_cmd + ": " + (end - start) + "ms"); - - return sorted; -} - -- (void)sortUsingNativeSort:(CPArray)anArray -{ - var sorted, - start = (new Date).getTime(); - - for (var i = 0; i < REPEATS; ++i) - sorted = [anArray _native_sortedArrayUsingDescriptors:descriptors]; + sorted = [anArray performSelector:aSelector withObject:anObject]; var end = (new Date).getTime(); From 58103ce0f364928bb1e9a8154bcb322cd68d2fff Mon Sep 17 00:00:00 2001 From: Alexander Ljungberg Date: Sun, 10 Jun 2012 00:58:27 +0100 Subject: [PATCH 08/10] Re #1562. Also performance test selector sort. It looks like issue #1560 will cause a different sorting function to be used for selectors vs descriptors so performance of both should be monitored for regressions. --- Tests/Foundation/CPArrayPerformanceTest.j | 71 ++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/Tests/Foundation/CPArrayPerformanceTest.j b/Tests/Foundation/CPArrayPerformanceTest.j index 4407befe8..ece1b6b96 100644 --- a/Tests/Foundation/CPArrayPerformanceTest.j +++ b/Tests/Foundation/CPArrayPerformanceTest.j @@ -66,6 +66,51 @@ var ELEMENTS = 100, [self checkRandomSorted:sorted]; } +- (void)testAlmostSortedNumericUsingMergeSelectorSort +{ + CPLog.warn("\nNUMERIC ALMOST SORTED (SELECTOR)"); + var a = [self makeUnsorted], + sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)]; + [self checkAlmostSorted:sorted]; +} + +- (void)testAlmostSortedNumericUsingNativeSelectorSort +{ + var a = [self makeUnsorted], + sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)]; + [self checkAlmostSorted:sorted]; +} + +- (void)testRandomNumericUsingMergeSelectorSort +{ + CPLog.warn("\nNUMERIC RANDOM (SELECTOR)"); + var a = [self makeRandomNumeric], + sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)]; + [self checkRandomSorted:sorted]; +} + +- (void)testRandomNumericUsingNativeSelectorSort +{ + var a = [self makeRandomNumeric], + sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)]; + [self checkRandomSorted:sorted]; +} + +- (void)testRandomTextUsingMergeSelectorSort +{ + CPLog.warn("\nTEXT RANDOM (SELECTOR)"); + var a = [self makeRandomText], + sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)]; + [self checkRandomSorted:sorted]; +} + +- (void)testRandomTextUsingNativeSelectorSort +{ + var a = [self makeRandomText], + sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)]; + [self checkRandomSorted:sorted]; +} + - (CPArray)sort:(CPArray)anArray usingSortSelector:(SEL)aSelector withObject:(id)anObject { var sorted, @@ -76,7 +121,7 @@ var ELEMENTS = 100, var end = (new Date).getTime(); - CPLog.warn(_cmd + ": " + (end - start) + "ms"); + CPLog.warn(aSelector + ": " + (end - start) + "ms"); return sorted; } @@ -195,6 +240,23 @@ var ELEMENTS = 100, }); } +- (CPArray)_native_sortedArrayUsingSelector:(SEL)aSelector +{ + var sorted = [self copy]; + + [sorted _native_sortUsingSelector:aSelector]; + + return sorted; +} + +- (CPArray)_native_sortUsingSelector:(SEL)aSelector +{ + sort(function(lhs, rhs) + { + return [lhs performSelector:aSelector withObject:rhs]; + }); +} + @end @implementation Sortable : CPObject @@ -203,4 +265,11 @@ var ELEMENTS = 100, int b @accessors; } +- (int)compareAAscendingThenBDescending:(Sortable)other +{ + var aResult = [a compare:other.a]; + + return aResult === CPOrderedSame ? [b compare:other.b] : -aResult; +} + @end From fcf62080d68e32cd1315e7f745d13a11d14697dc Mon Sep 17 00:00:00 2001 From: Alexander Ljungberg Date: Sun, 10 Jun 2012 00:59:08 +0100 Subject: [PATCH 09/10] Re #1562. Make native comparison more realistic. This might have a small speed implication. --- Tests/Foundation/CPArrayPerformanceTest.j | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Foundation/CPArrayPerformanceTest.j b/Tests/Foundation/CPArrayPerformanceTest.j index ece1b6b96..5c1f4235a 100644 --- a/Tests/Foundation/CPArrayPerformanceTest.j +++ b/Tests/Foundation/CPArrayPerformanceTest.j @@ -233,7 +233,7 @@ var ELEMENTS = 100, result = CPOrderedSame; while (i < count) - if ((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame) + if ((result = [descriptors[i++] compareObject:lhs withObject:rhs]) !== CPOrderedSame) return result; return result; From f538cbc51d20118b81aa549a0904809441777d06 Mon Sep 17 00:00:00 2001 From: Alexander Ljungberg Date: Sun, 10 Jun 2012 01:18:56 +0100 Subject: [PATCH 10/10] Re #1560. Remove obsolete function. --- Foundation/CPArray/CPMutableArray.j | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Foundation/CPArray/CPMutableArray.j b/Foundation/CPArray/CPMutableArray.j index 42e3a88de..308390429 100644 --- a/Foundation/CPArray/CPMutableArray.j +++ b/Foundation/CPArray/CPMutableArray.j @@ -428,19 +428,6 @@ var sortArrayUsingFunction = function(array, aFunction, aContext) } } -// sort using sort descriptors -var compareObjectsUsingDescriptors = function(lhs, rhs, descriptors) -{ - var result = CPOrderedSame, - i = 0, - n = [descriptors count]; - - while (i < n && result === CPOrderedSame) - result = [descriptors[i++] compareObject:lhs withObject:rhs]; - - return result; -}; - // Observe that the sort descriptors has the reversed order by the caller var sortArrayUsingJSDescriptors = function(a, d) {