In your insertion sort, you are being more complicated,
for (int pos = 0; pos < arrayToBeSorted.length; pos++) {
for (int secondMarker = pos; secondMarker > 0; secondMarker--) {
int currentValue = arrayToBeSorted[secondMarker];
int valueBeingCheckedAgainst = arrayToBeSorted[secondMarker - 1];
if (currentValue > valueBeingCheckedAgainst) {
break;
}
arrayToBeSorted[secondMarker] = arrayToBeSorted[secondMarker - 1];
arrayToBeSorted[secondMarker - 1] = currentValue;
}
}
You read the value from the array in the inner loop, and while the value at the preceding position is not smaller, you write two values to the array.
In the shell sort,
for (int i = gap; i < a.length; i++) {
int tmp = a[i];
int j = i;
for (; j >= gap && tmp < (a[j - gap]); j -= gap) {
a[j] = a[j - gap];
}
a[j] = tmp;
}
you read the value to be placed once, outside the inner loop, and only have a single write in the inner loop body, writing the value only once after the inner loop.
That is more efficient, and so it's understandable that the shell sort is faster. That the two first shell sorts are slower is probably because the wrapping
for (int gap = a.length / a.length; gap > 0; gap = (gap / 2)) {
confuses the JIT for a while before it notices that gap
can be replaced with 1 and the wrapping loop eliminated.