0

I have read in many places that the best case for those algorithms is when the array is already sorted and so making the program run them faster. For some reason, When I test the algorithms with unsorted and the same but sorted values, they run faster in the first case. Why?

This is the code for bubble sort:

// ----------------- (1) Bubble Sort -----------------------------
        // Unsorted
        long startTime11 = System.nanoTime();
        Sorting.bubbleSort(wordsToSort);
        long endTime11 = System.nanoTime();
        long timeLapsed11 = endTime11 - startTime11;
        // Already sorted
        long startTime12 = System.nanoTime();
        Sorting.bubbleSort(wordsToSort);
        long endTime12 = System.nanoTime();
        long timeLapsed12 = endTime12 - startTime12;
        // formats time with commas
        String formated11 = formatter.format(timeLapsed11);
        String formated12 = formatter.format(timeLapsed12);

bubbleSort method:

private static void bubbleSort(String[] arr, int n) {

        for (int i = 0; i < n - 1; ++i)
        {
            for (int j = 0; j < n - i - 1; ++j)
            {
                if (arr[j + 1].compareTo(arr[j]) < 0)
                {
                    String temp = arr[j + 1];
                    arr[j + 1] = arr[j];
                    arr[j] = temp;
                }
            }
        }
    }

CPU Time:

Unsorted: 11, 372, 240, 025, 900 nanoseconds.

Sorted: 22, 020, 911, 806, 900 nanoseconds.

The array has more than 600,000 elements in it.

More case results:

Test two of the array of +600000 elements:

Unsorted: 11, 392, 317, 750, 900 nanoseconds.

Sorted: 22, 024, 687, 559, 200 nanoseconds.


Array of 15000 words:

Unsorted: 1, 407, 057, 900 nanoseconds.

Sorted: 1, 650, 519, 600 nanoseconds.

Unsorted: 1, 430, 736, 200 nanoseconds.

Sorted: 1, 645, 250, 000 nanoseconds.


Array of 15000 words (Warmup phase added; advised from the comments)

Unsorted: 1, 453, 011, 900 nanoseconds.

Sorted: 1, 660, 550, 800 nanoseconds.

Unsorted: 2, 025, 017, 800 nanoseconds.

Sorted: 2, 466, 999, 700 nanoseconds.


Array of 2500 words

Unsorted: 44, 016, 900 nanoseconds.

Sorted: 20, 703, 300 nanoseconds.

Unsorted: 47, 074, 400 nanoseconds.

Sorted: 36, 121, 700 nanoseconds.


Array of 600 words

Unsorted: 7, 104, 600 nanoseconds.

Sorted: 1, 392, 100 nanoseconds.

Unsorted: 7, 144, 500 nanoseconds.

Sorted: 1, 324, 600 nanoseconds.


Here is the code with some txt files to test them and know better how to answer: https://github.com/Obed-Padilla/Sorting-algorithms

2 Answers2

1

Given the long runtimes of 3 and 6 hours, I see two possible interesting explanations:

  1. You always compare neighbor elements. When the array is unsorted, neighbor strings likely already differ at the first character, which makes the comparisons fast. When the array is sorted, neighbor strings likely share a common prefix, which makes comparisons slower.

  2. You created the string objects in unsorted order, for example reading a file of unsorted strings into memory. I'm not much familiar with Java, but I suspect it does create them mostly sequentially in memory. So at the start, going through the strings in array order accesses the memory sequentially, which is cache-friendly. But then the sorting changes the array order. So going through the strings of the sorted array accesses the memory "randomly", which is cache-unfriendly.

Update after your question update with the additional timings and link to code/data: Both of the above reasons apply, although I suspect the cache one is the main one. Your timings show that with fewer and fewer words, the "sorted" case gets better and better compared to the "unsorted" case. That's expected, as for small inputs, more/everything fits in caches, and common prefixes are short (unless you choose similar words, but you didn't), so those two disadvantages of "sorted" go away. And the "unsorted" case still suffers from having to do the swap work and from branch prediction failures, making it slower than "sorted" when "sorted" doesn't have its disadvantages anymore. Also see Why is processing a sorted array slower than an unsorted array? as well as Why is processing a sorted array faster than processing an unsorted array? (Note that one says slower and the other says faster, and both are correct, for different usages and reasons).

I was able to reliably reproduce the "unsorted" case being faster than the "sorted" case (1.2 seconds vs 2.0 seconds, doing five rounds and ignoring the first round for warmup) with 10000 artificially created strings of 100 characters each. Still hoping that you'll share more infos about your data and runnable complete code, but (see update paragraph above) I think the above aspects are interesting anyway.

Sample benchmark results:

unsorted 1.263458034
sorted   1.985379993

unsorted 1.115940408
sorted   1.830749365

unsorted 1.157192938
sorted   2.175659597

unsorted 0.950845856
sorted   1.712224688

unsorted 1.243945757
sorted   1.979670624

Code (Attempt This Online!):

import java.util.*;

public class Main {

  public static void main(String[] args) {
    for(int j=0; j<5; j++){
        int n = 10000;
        String[] wordsToSort = new String[n];
        for (int i=0;i<n;i++)
            wordsToSort[i] = (""+new Character((char)(i%26+65))).repeat(100)+i;

        long start = System.nanoTime();
        bubbleSort(wordsToSort, n);
        System.out.println("unsorted " + (System.nanoTime() - start) / 1e9);

        start = System.nanoTime();
        bubbleSort(wordsToSort, n);
        System.out.println("sorted   " + (System.nanoTime() - start) / 1e9 + "\n");
    }
  }

    private static void bubbleSort(String[] arr, int n) {

        for (int i = 0; i < n - 1; ++i)
        {
            for (int j = 0; j < n - i - 1; ++j)
            {
                if (arr[j + 1].compareTo(arr[j]) < 0)
                {
                    String temp = arr[j + 1];
                    arr[j + 1] = arr[j];
                    arr[j] = temp;
                }
            }
        }
    }
}
Kelly Bundy
  • 23,480
  • 7
  • 29
  • 65
  • Holy water! That makes a lot of sense actually... I think the first theory could be the main reason. You know? it makes too much sense haha. I did two days ago what the other guys in the comments told me about, and I read a little bit that convoluted post on benchmarks they gave me when they closed my question for my dummy beginner mind. I tried to make a benchmark, and the results do differ (pretty much randomly) as the iterations increase. I am really confused haha. well, at least I pushed this piece of homework to greatness, and I am satisfied nonetheless – Sheep_Walker Mar 22 '23 at 04:18
  • Do you only want the other tests I made and a code to copy and run as more info, right? In fact, I even have it already in the github with some txt files filled with many many words – Sheep_Walker Mar 22 '23 at 04:22
  • @Sheep_Walker Thanks fo the additional timings/code/data. It's all as expected. See the "Update" paragraph I added to my answer now. – Kelly Bundy Mar 22 '23 at 12:33
  • thank you very much. Now I understand better. You know? really cool you fought your way to the reopening of my question, and posted this good answer – Sheep_Walker Mar 23 '23 at 02:04
0

Aren't you using the same array in the variable field by the function ? same variable ?.

This may have to do with machine load . Usually , your machine may spend different amount of CPU cycles on each task , and perfoming certain task may push the priority for the execution down a bit , which lead to some delay .

If you perform raw execution of each algorithm around 50 times , the difference is uncanny on recorded time.

  • 1
    Also, he only runs each test once, so the results are liable to be distorted JVM warmup issues; e.g. class loading, heap resizing, JIT compilation, and so on. – Stephen C Mar 19 '23 at 04:48
  • Ok, I tested it with a smaller array of 6 elements, and now -as expected- it sorts faster the sorted version of the array.... I think it is because of what you both said.... JVM issues... is there a way to make java start each algorithm the same way as the previous one? like cleaning the cache or something? – Sheep_Walker Mar 19 '23 at 18:08
  • It really is impossible to rule out all exceptions , and get the precise execution time . We can only prove an algorithm is faster logically . – frozenproof Mar 20 '23 at 13:12
  • @StephenC How long does it take to warm up? The times were **3 hours** and **6 hours**. I think you guys all dismissed this incorrectly. – Kelly Bundy Mar 20 '23 at 16:25
  • @frozenproof Your first paragraph doesn't make sense. Yes, they're using the same variable, so the second time it's already sorted. But that's *intentional*. Remember what the question is about! And I don't think it's reasonable to run this 50 times. That's 450 hours! You don't need that many times. – Kelly Bundy Mar 20 '23 at 16:37
  • @KellyBundy if someone is making a micro test benchmark for a program , rigrid testing is a requirement . its time consuming during testing phase so that we can save more time using it in real situations later. – frozenproof Mar 21 '23 at 00:06
  • Sure, but I think you really don't need anywhere that much testing. If you put their code in a loop that runs let's say three times, and every time the first case takes 3 hours and the second case takes 6 hours, isn't that enough? And I still think this really isn't a *micro*-benchmark. – Kelly Bundy Mar 21 '23 at 00:48