-
Notifications
You must be signed in to change notification settings - Fork 27
Description
For sake of exposition I'm going to write in terms of the LLVM parser we're working on in llir/ll:
Please consider the ToLlvmNode function:
func ToLlvmNode(n *Node) LlvmNode {
switch n.Type() {
case ll.AShrExpr:
return &AShrExpr{n}
case ll.AShrInst:
return &AShrInst{n}
case ll.AddExpr:
[... many more elided ...]At the moment, for parsing a large input with LLVM, this is one of the larger sources of allocations. (>30% of allocations from the parser) - see llir/llvm#55 for code to reproduce the measurement for yourself.
I note that the allocation can be completely eliminated by instead returning values of the node types (e.g, return AShrExpr{n}). This seems like a reasonable thing to do because all the node types do is embed a pointer to a node, and the value of the pointer doesn't ever need to be modified later, so it is fine to pass the pointer around by value. Because the node types structs only contain this pointer, they actually fit into the second value of the interface data, and therefore the returned value doesn't require an allocation either.
I did a brief experiment on the LLVM codebase a while back and observed about a 10% wall clock speedup and a significant reduction in allocations - giving (IIRC) a significant reduction in CPU and GC cost. The number of modifications I had to make to the LLIR parser was minimal.
I've run out of time for this moment. I intend to supply more (and more specific) details later, and perhaps a patch, if I get a spare moment. But for now I want to put this proposal out there and see what the response is.
For what it's worth I have some other tricks up my sleeve to reduce allocations, though they may be a bit too much of micro-optimization for some tastes. I'd be interested if you'd like to hear of these, but for now let's just consider this one which seems like the lowest hanging fruit.