Quicksort depends heavily on the choice of pivot. The best pivot would be the median, but this runs into an "chicken and egg" problem, because you need to sort to calculate the true median. The Median-of-three estimate as a pivot would be the most efficient for large sizes. https://en.wikipedia.org/wiki/Quicksort#Choice_of_pivot
Although, doing (lo + hi) / 2 isn't really that bad.
The best thing would to actually test your program with large sets of data that follow the patterns you expect to encounter.
I think the issue with your code is that you are calling quicksort in the middle of the your partitioning logic (within your while(true) loop). You should separate the partition logic from the recursive call logic.