Every sort pays for two things: asking "is this bigger?" and moving elements. Pick an algorithm and an input order, then watch the counters against what theory says is the least possible.
Sorts n = 16 to 512 (4 inputs each, current input order), and fits comparisons ~ np. Curves are comparisons / (n log2 n): flat means n log n, rising means quadratic.
The science
Cost model. A comparison asks whether one element is greater than another. A write is any assignment into the array, including the auxiliary copy that merge sort makes. Bubble, insertion, selection, shell, comb, merge, quick (Lomuto partition, last element as pivot) and heap sort (Floyd heap build) are all implemented from scratch and the counters are incremented inside the algorithm, not estimated.
Information bound. A comparison sort is a binary decision tree with at least n! leaves, one per ordering, so its depth is at least ceil(log2 n!), about n log2 n − 1.443 n. The same tree argument gives the same figure as the lower bound on the average number of comparisons over random inputs.
Exact counts. Selection sort always makes n(n−1)/2 comparisons. Insertion sort on a reversed array makes exactly n(n−1)/2, on a sorted one n−1. Merge sort on n = 2k makes between (n/2) log2 n and n log2 n − n + 1. Quicksort with the last element as pivot has expected 2(n+1)Hn − 4n comparisons on random inputs (Hn the harmonic number) but degrades to n(n−1)/2 on sorted input.
Simplifications. Counts are in abstract operations; a real machine also pays for cache misses and branch mispredictions, which this page does not model. Non-comparison sorts (counting, radix) are outside the model because they beat the bound by not comparing. Shell sort uses Knuth gaps (3g+1); comb sort uses shrink factor 1.3. The sweep uses only four inputs per size, so small differences between curves are noise. Models built with Blender, scene by Three.js.