개발 과정 최적화 하기 내부툴로 더욱 강력한 개발하기 Stephen kennedy _(11시40분_103호)
1. Avoiding Developer Taxes
(Making development more robust
with introspection tools. )
Stephen.Kennedy @ havok.com
Principal Engineer
github:clang-extract
18. Build Integration
Prebuild step runs Clang if necessary
Plugins run on the database
That’s it Generate Reflect.cpp
Reflection
ClangAST DB
Static
Analysis
R 18
19. Runtime Introspection
struct X
{ float
float time; bool
bool used; float
float pos;
bool canmove; bool short float
short flags; short bool bool
}; float
R 19
23. Sample Interface
class Timeline
{
/// Adds a label at the given time and
/// returns an id for that label
int addLabel( float time, const char* label );
};
B 23
24. What is a Binding?
Lua State
int addLabel(
Lua_String float time,
“GDC” const char* label)
{
Lua_Num // ...
1.4 }
B 24
25. Three Parts of a Binding
Lua State float time = lua_...
char* label = lua_...
Lua_String
“GDC” push time
Lua_Num push label
1.4 call addLabel
pop id
Lua_Int
ret = 64
lua_set_return( id )
B 25
26. Manual Bindings
int wrap_timeLine_addLabel(lua_state* s) {
TimeLine* tline = lua_checkuserdata(s,1);
int arg0 = lua_checkint(s,2);
const char* arg1 = lua_checkstring(s,3);
int id = tline->addLabel( arg0, arg1 );
lua_pushint(id);
return 1;
}
timeLine:addLabel(1,”x”)
B 26
27. Normal “Fat” Bindings
timeLine:addLabel(1,”x”)
1:1 wrapper:native
wrap_addLabel() Manual or Generated
~400b per wrapper
TimeLine::addLabel()
B 27
30. Reflected Function
struct FunctionReflection {
const char* name;
Callable callable; // “function pointer”
TypeReflection* argTypes;
int numArgs; // Including return type
};
B 30
34. Fat vs Slim Memory Cost
N functions
T distinct trampolines (T≤N)
N * wrap_func() 1 lua_bridge() N * Reflection
T * trampoline()
N * func()
N * func()
~400*N ~40*N + ~64*T
B 34
37. Memory Reporting Goals
More than just block sizes
Account for every byte
Low maintenance burden
Customizable output
M 37
38. Memory Reporting
Manual getMemoryStatistics()
void hkpMeshShape::calcContentStatistics
( hkStatisticsCollector* collector ) const
Buggy {
collector->addArray( "SubParts", this->m_subparts );
for( int i = 0; i < this->m_childInfo.getSize(); i++ )
{
collector->addReferencedObject( "Child", m_childInfo[i].m_shape );
Tedious
}
hkpShapeCollection::calcContentStatistics(collector);
}
M 38
39. Automatic Reports
Aim for 100% automatic coverage
Provide debugging for missing
Leapfrog Technique:
Remember types of (some) allocations
Know offsets of pointers in each type
M 39
47. Memory Report Conclusion
Label root blocks & grow with reflection
We found offline analysis useful
Low maintenance
Accounts for all reachable memory
Or tells you where it came from
47
48. Versioning
Change Happens
Hitting a moving target.
Not much in literature.
V 48
49. Manual Conditionals
xtream.TokenMustBe("POINT_A"); xtream>>point_A;
bool has_two_bodies = true;
Version number
if (xtream.GetVersion()>=1350)
{
Conditionals
xtream.TokenMustBe("HAS_TWO_BODIES");
xtream>>has_two_bodies;
Inter-object changes?
}
if (has_two_bodies)
{
Large refactor? xtream.TokenMustBe("BODY_B");
xtream>>prot_buffer;
priv_rigid_body_B = s->GetRigidBody(prot_buffer);
if (!priv_rigid_body_B)
throw_exception("Rigidbody unknown in Spring");
}
xtream.TokenMustBe("POINT_B"); xtream>>point_B;
V
//…
49
50. Ideal Versioning System
Don’t slow me down
Don’t constrain my changes
Help me fix it up
Don’t make me think
Don’t make me revisit
V 50
51. Snapshot Versioning
Compare all previous vs current metadata
Function updates changed members
Check up-to-date with CRC of reflection
V 51
52. Snapshot example
A { int x; int y; } A { int y; }
B { A* a; } B { A* a; int x;}
void Update_B( void* oldB, void* newB ) {
// newB->x = oldB->a->x; }
{ “A”, 0x1234, 0x8989, NULL }
{ “B”, 0xabcd, 0xfed4, Update_B }
V 52
53. Version Function
A { int x; int y; } A { int y; }
B { A* a; } B { A* a; int x;}
void Update_B( Object& oldB, Object& newB )
{
newB[“x”] = oldB[“a”].asObject()[“x”].asInt();
}
V 53
54. Chaining Snapshot Updates
V1 → V2 V2 → Current
A { int x; int y; } A { int y; } A { int y; } A { int y; int z; }
B { A* a; } B { A* a; int x;} B { A* a; int x; } B { A* a; int x; }
Current
void Update_B( void* oldB, void* newB ) { void Update_A( void* oldA, void* newA ) {
// newB->x = oldB->a->x; } // newA->z = 1000; }
{ “A”, 0x1234, 0x8989, NULL } { “A”, 0x8989, 0x52c1, Update_A }
{ “B”, 0xabcd, 0xfed4, Update_B } { “B”, 0xfed4, 0xf115, NULL }
V 54
55. What Have We Gained?
Manual if version<1
if version<1 if version<2
if version<1 if version<2 if version<3
v0 v1 v2 Now
Snapshots
A { int x; int y; } A { int y; } A { int x; int y; } A { int y; } A { int x; int y; } A { int y; }
B { A* a; } B { A* a; int x;} B { A* a; } B { A* a; int x;} B { A* a; } B { A* a; int x;}
void Update_B( void* oldB, void* newB ) { void Update_B( void* oldB, void* newB ) { void Update_B( void* oldB, void* newB ) {
// newB->x = oldB->a->x; } // newB->x = oldB->a->x; } // newB->x = oldB->a->x; }
{ “A”, 0x1234, 0x8989, NULL } { “A”, 0x1234, 0x8989, NULL } { “A”, 0x1234, 0x8989, NULL }
{ “B”, 0xabcd, 0xfed4, Update_B } { “B”, 0xabcd, 0xfed4, Update_B } { “B”, 0xabcd, 0xfed4, Update_B }
v0 v1 v2 Now
V 55
56. Finer Grained Changes
We hired artists
Full snapshots too heavy
New products, lots of branches
• No global timeline
V 56
57. Patching (1)
A0 { int x; } A0 { int x; } A1 { int x; int y; }
B0 { A* a; } B1 { A* a; int y; } B2 { A* a; }
B0→B1
B.add(int,“y”)
V 57
58. Patching (2)
A0 { int x; } A0 { int x; } A1 { int x; int y; }
B0 { A* a; } B1 { A* a; int y; } B2 { A* a; }
A0→A1
B1→B2
B0→B1
A.add(int,“y”)
B.add(int,“y”)
A.y = B.y
B.rem(“y”)
V 58
59. Sample Patch
// This block is generated by the metadata comparison.
// It is pasted into the source and expands out to a few constant structures.
PATCH_BEGIN("hkbLookAtModifier", 2, "hkbLookAtModifier", 3)
// Require hkbEventBase version 3 before running this patch
PATCH_DEPENDS("hkbEventBase", 3)
PATCH_MEMBER_ADDED_VEC_4("neckForwardLS", 0.0f,1.0f,0.0f,0.0f)
// user edit: add & remove changed to rename
PATCH_MEMBER_RENAMED("headForwardHS", "headForwardLS")
// user edit: call a C function here
PATCH_FUNCTION( hkbLookAtModifier_2_to_3 )
PATCH_END()
V 59
60. Verification
Previous Apply Patched
Reflection Patches Reflection
Patches Compare
Patches
Wrong/ against current
reflection OK
Missing
V 60
61. Versioning Workflow
Make your changes
Run a reflection diff utility
Copy & paste the suggested patch
Edit (add&remove → rename)
Write version function if necessary
Done
V 61