Tradeoffs around Node::Opcode()?
Vladimir Kozlov
Vladimir.Kozlov at Sun.COM
Tue Mar 10 17:53:59 PDT 2009
Peter,
if you wish to add an additional field into Node you may instead
extend _class_id field to 4 bytes to cover all classes and use it
(n->class_id() == Node::Class_SafePoint)
instead of
(n->Opcode() == Op_SafePoint)
Sorry, I forgot to mention that class_id can work as Opcode() also.
The problems with this are, first, maintenance - you need to add
several lines in node.hpp instead of one line in classes.hpp for
each node, and, second, 32 bits could be not enough for all classes.
I agree that it would be nice to remove virtual Opcode() method.
Peter B. Kessler wrote:
> Thanks for the answers.
>
> I didn't understand why you say that a subclass would overwrite an
> _opcode field. If each AddI constructor initializes my proposed
> (const?) _opcode field in Node to Op_AddI, and if AddF initializes the
> _opcode field to Op_AddF, then who would overwrite it? Obviously node
> instances can't change their opcodes, or we wouldn't be able to use a
> virtual function for Opcode() now, unless we change the vtable for
> instances.
I meant super class and subclass, for example, Op_ConI will overwrite Op_Con:
class ConNode : public TypeNode {
public:
ConNode( const Type *t ) : TypeNode(t,1) {
init_req(0, (Node*)Compile::current()->root());
init_flags(Flag_is_Con);
_opcode = Op_Con;
}
virtual int Opcode() const;
...
class ConINode : public ConNode {
public:
ConINode( const TypeInt *t ) : ConNode(t) {
_opcode = Op_ConI;
}
virtual int Opcode() const;
>
> Then there could be a map from Opcodes to what's now in _class_id and
> _flags. E.g., there would be a _class_id table someplace whose Op_AddI
> entry would contain Class_Add. As would the Op_AddF entry. But the
> Op_AddP index would would contain Class_AddP. Do the _flags fields
This approach requires 2 loads instead of current one to read _class_id.
> change over the life of a Node instance? Or could those also be in a
No, _flags field does not change over time.
Vladimir
> map indexed by Opcode? (Having separate maps from Opcode to other
> properties of the Node means that information about the Node subclass
> isn't visible in text of the subclass constructor. Sigh.)
>
> I hear from Chris Vick that making the edges between the nodes
> bi-directional, which increased the size of the Node instances, didn't
> have any noticeable effect on the space used collectively by the nodes
> during a compilation, and didn't have any effect on the speed of the
> compiler, except maybe to speed it up because traversals that used to be
> hard with one-way edges became easier with bi-directional edges.
>
> ... peter
>
> Vladimir Kozlov wrote:
>> Yes, it is history :)
>>
>> The _opcode field will not work since subclasses will overwrite it.
>>
>> The _class_id field is not direct map to Opcode, it is bitmap:
>> n->is_SafePoint() will be true for all ideal Call and SafePoint nodes,
>> but n->Opcode() == Op_SafePoint only true for SafePointNode.
>> So we need both.
>>
>> And yes, I want to fit in a jushort :-)
>> When I did implementation of _class_id I was also concern about
>> increasing Node size significantly so I decided to add only 4 bytes:
>> 2 for class_id and 2 for flags.
>>
>> I also did profiling of Opcode() usage. So only hottest Opcode()
>> calls were replaced with class_id code. Adding other nodes did not
>> improve performance.
>>
>> Vladimir
>>
>> Peter B. Kessler wrote:
>>> Cool! Thanks. So much for engineering: we already have *both* the
>>> virtual dispatch *and* the data slot per instance.
>>>
>>> Oh, but those are only the class id's (e.g., Add), rather than the
>>> opcode (e.g., AddI). It's confusing that they are both referred to
>>> as Node "classes", e.g., in classes.hpp, opcodes.cpp, etc.
>>>
>>> So I guess that begs the question: why *isn't* there an _opcode
>>> field, initialized in the constructors, like (or instead of)
>>> _class_id? Why aren't the leaf classes (AddI, etc.) in the _class_id
>>> maps? (Other than that the _class_id maps want to fit in a jushort.
>>> :-) Couldn't one map (e.g., with a table) from an opcode to a
>>> _class_id, if one wanted to spend the time as opposed to the space?
>>> Would an inline method to access a _class_id table be faster than a
>>> virtual dispatch to Opcode()?
>>>
>>> A possible answer as to why it is the way it is: "History".
>>>
>>> I'm still trying to understand the tradeoffs between space and time.
>>>
>>> ... peter
>>>
>>> Vladimir Kozlov wrote:
>>>> Peter,
>>>>
>>>> There is already such functionality exactly for such purpose.
>>>> The field is jushort _class_id; which use enum NodeClasses
>>>> and initializer in the constructors init_class_id().
>>>>
>>>> Vladimir
>>>>
>>>> Peter B. Kessler wrote:
>>>>> A lot of things in class Node are organized around having a dense
>>>>> integer range to identify the Node. That's Node::Opcode().
>>>>>
>>>>> I'm wondering about the engineering around that method.
>>>>> Node::Opcode() is a virtual call, to get from a Node instance to a
>>>>> piece of data on the Node subclass of the instance. An alternative
>>>>> would be to add a data field in Node to hold the opcode, and
>>>>> replace the virtual Node::Opcode() calls with an inline method to
>>>>> return the contents of that slot. That trades the virtual dispatch
>>>>> cost for each call with a space cost in each instance.
>>>>>
>>>>> Does anyone have any feeling about whether that's a good tradeoff?
>>>>> How often is Node::Opcode() called? What effect would it have to
>>>>> make it run faster? How important is it to minimize the size of
>>>>> Node instances? (There are other places where effort is expended
>>>>> to keep Node's small.)
>>>>>
>>>>> Thanks for any opinions on this.
>>>>>
>>>>> ... peter
>>>
>
More information about the hotspot-compiler-dev
mailing list