Roku OS 15.0에서 고성능 데이터 오케스트레이션
Source: Dev.to
MoveIntoField와 MoveFromField
전통적으로 node.field = myAA와 같은 할당은 깊은 복사를 수행했습니다. Roku OS 15.0에서는 MoveIntoField()와 MoveFromField()가 소유권을 실제로 이전합니다. 데이터가 노드로 이동하고 로컬 변수는 비게 됩니다.
' --- BEFORE (The Blocking Copy) ---
' ...do some work
' m.top.content = hugeAA
' 1. Task thread stops.
' 2. Render thread stops.
' 3. Both wait for a deep copy to finish.
' 4. Breakpointing here kills the app because
' the Render thread is "stuck" waiting.
' --- AFTER (The Non-Blocking Move) ---
sub finishTask(hugeAA)
' ...do some work
' MoveIntoField transfers the memory pointer instantly.
m.top.MoveIntoField("content", hugeAA)
' hugeAA is now empty. The Task can exit or move on without
' holding a redundant copy in memory.
? "Task complete. hugeAA is now empty: " ; hugeAA.IsEmpty()
end sub
Render Thread References: SetRef and GetRef
때때로 데이터를 이동하거나 복사하고 싶지 않을 때가 있습니다—단지 Render 스레드가 객체의 단일 인스턴스를 바라보게 하고 싶을 때 말이죠. 이제 Render 스레드에만 제한된 Reference API를 통해 이것이 가능해졌습니다.
SetRef(): AA를 필드에 참조 형태로 할당합니다(복사 없음).GetRef(): 해당 참조를 가져와 사용합니다.CanGetRef(): 참조가 존재하는지 확인하는 안전 검사입니다.
Note: SetRef()를 사용할 때 필드 옵저버는 알림을 받지 않습니다. 이는 관측자 알림의 오버헤드를 피하고자 하는 고주파 데이터(예: 좌표 추적)에 이상적입니다.
' --- BEFORE (Memory Inefficiency) ---
' m.childA.data = m.top.content ' Copy 1
' m.childB.data = m.top.content ' Copy 2 (Memory usage doubled)
' --- AFTER (Robust Reference Sharing) ---
' This must happen on the Render Thread (e.g., in an observer or queue handler)
sub shareTaskData()
if m.top.CanGetRef("content")
ref = m.top.GetRef("content")
' Pass the pointer to children. No deep copies are made.
m.childA.SetRef("sharedContent", ref)
m.childB.SetRef("sharedContent", ref)
end if
end sub
roRenderThreadQueue
roRenderThreadQueue API는 Task 스레드가 UI 스레드에 데이터를 전송할 때 Rendezvous( UI를 멈추게 하는 동기 잠금)를 발생시키지 않음으로써 “SceneGraph Hole” 문제를 해결합니다. 이를 스레드‑안전 메일함이라고 생각하면 됩니다: Task 스레드는 메시지를 넣고 바로 진행하고, Render 스레드는 프레임 사이에 메시지를 처리합니다.
핵심 API
roRenderThreadQueue– 매니저 노드. Render 스레드(수신)와 Task 스레드(전송) 모두에서 초기화합니다.AddMessageHandler(message_id, handler_name)– 지정된 ID와 일치하는 메시지가 도착했을 때 실행할 함수를 등록합니다. 하나의 ID에 여러 핸들러를 연결할 수 있으며, 등록된 순서대로 실행됩니다.PostMessage(message_id, data)– 데이터를 이동시킵니다(고성능).CopyMessage(message_id, data)– 전송 전에 데이터를 깊은 복사합니다.
' --- BEFORE (Rendezvous Dependency) ---
' m.top.updateData = hugeAA
' Scene.brs has an observer on task.updateData.
' The moment this assignment happens, the threads are "locked."
' --- AFTER (Decoupled Messaging) ---
' [Inside Task Thread]
sub runTask()
hugeAA = loadMassiveJson()
queue = CreateObject("roRenderThreadQueue")
queue.PostMessage("FEED_UPDATE", hugeAA)
end sub
' [Inside Component/Scene .brs]
sub init()
m.queue = CreateObject("roRenderThreadQueue")
m.queue.AddMessageHandler("FEED_UPDATE", "onFeedUpdate")
end sub
sub onFeedUpdate(data as Object, info as Object)
' data is the moved AA. No Rendezvous ever occurred.
m.myGrid.content = data
end sub
결론
이 아키텍처 변화는 SceneGraph 자체 이후 Roku 개발에서 가장 중요한 변화라고 할 수 있습니다. 새로운 API를 사용하면 대형 데이터 구조를 복사하는 대신 단순히 가리키게 할 수 있어 여러 실질적인 이점을 제공합니다:
- 저사양 디바이스 구원: 제한된 RAM을 가진 디바이스(예: Roku Express)에서 깊은 복사로 인한 메모리 급증이 사라져 대용량 JSON 페이로드를 저가 하드웨어에서도 처리할 수 있습니다.
- ‘잔크’ 종말:
RowList또는 기타 UI 컴포넌트에서 발생하던 미세한 끊김이MoveIntoField와PostMessage가 O(1) 연산이기 때문에 사라져 Render 스레드가 반응성을 유지합니다.