However, if you have the ram, or know the data set has a low cardinality (like, http status codes or filesnames instead of ip addresses) then something that works in memory will be much more efficient.
I threw 144,000,000 'hello' and 'world' into a file:
justin@box:~$ ls -lh words
-rw-r--r-- 1 justin justin 824M Jan 7 15:21 words
justin@box:~$ wc -l words
144000000 words
justin@box:~$ time (sort <words|uniq -c)
72000000 hello
72000000 world
real 0m22.831s
user 0m32.999s
sys 0m4.675s
Compared to doing it in memory with awk: justin@box:~$ time awk '{words[$1]++} END {for (w in words) printf("%s %d\n", w, words[w])}' < words
hello 72000000
world 72000000
real 0m10.639s
user 0m9.736s
sys 0m0.876s
so, half the time and 1/3 the cpu.