Let me add another thing: You are sorting over values %{$columns[$i]} for every one of the 14M records, though the value of @columns doesn't change after the first run through @data. You should calculate @dat_val once for the 1400 columns between the two runs through @data.
From the fact that you output a percentage I guess that you have "about 100" different values in each column. 100-element-sorts are rather fast, but still, 1400 sorts of 100-element-arrays are considerably faster than 14M * 1400 sorts of 100-element arrays.