Even though the integer type is unbounded, in practice the vast majority of runtime values are small, thus we can implement them efficiently using tagged pointers [small values are completely contained in the pointer]. LISP and Smalltalk have been doing this since the 70'es. Sun SPARC even _had_ hardware support for doing it with no (zero) overhead, see tagged instructions (https://en.wikipedia.org/wiki/SPARC). Even on x86, it's not that bad; the overhead for the general case is two tag extractions and three predicable (not-taken) branches (we must check for overflow), about five additional instructions. Not zero, but the dynamic frequency of these is much lower in a high-level language than in, say, C.
The performance differences between Erlang and Haskell (etc) are due to many reasons, but primarily due to Erlang being a dynamic PL, with late binding and dynamic typing. This makes efficient implementation hard (heroic efforts on JavaScript not withstanding), but Erlang makes no excuses as its strengths lies elsewhere. If you need fast compute you should do it in C and interface it with Erlang (lots of ways to do that).
The performance differences between Erlang and Haskell (etc) are due to many reasons, but primarily due to Erlang being a dynamic PL, with late binding and dynamic typing. This makes efficient implementation hard (heroic efforts on JavaScript not withstanding), but Erlang makes no excuses as its strengths lies elsewhere. If you need fast compute you should do it in C and interface it with Erlang (lots of ways to do that).