What makes a bytecode interpreter faster than an interpreting ast-walk translator?
The most obvious reason is that the AST is generally still too high a level, whereas the semantics of the bytecode can be trivial to execute. The slowest thing in the AST-walk interpreter is usually a search by context: all variables, arguments, etc. These are referenced by their names, whereas in bytecode they are usually removed and registers or stack operations will be used instead.
Of course, the bytecode can be viewed as a special case of walking AST - with a flat, simple "AST" and, possibly, with an optimized "walker" (for example, using a transformation with a threaded code). There are many possible states between special AST and highly specialized bytecode - for example, to interpret a functional language, you can keep the structure of the AST, but replace the variable names with De Bruijn indices.
source to share