ref

Abstract

Get a reference to a variable

Description

ref and deref implement a mechanism to pass a reference to an array, allowing to share it across instruments, opcodes, etc. Refs are reference counted and deallocate themselves when out of scope and not being used by any object. It makes it possible to pass arrays by reference to user defined opcodes, allowing to modify an array in place, skip copying memory, etc.

iX[] fillarray 0, 1, 2, 3, 4
iref  ref iX
iY[] deref iref

In the case above, iY shares the same memory as iX and any modification in one array will be visible in the other.

Syntax

iref ref xArray, [iextrarefs=0]

Arguments

  • xArray: the array to be referenced
  • iextrarefs: use this for the niche case where a reference is passed to an event scheduled at a point in time later that the end of the current event. Without this, the ref would go out of scope before the deref takes place. Any extra ref must be matched with an extra deref (kArr[] deref iref, 1)

Output

  • iref: an integer identifying the reference handle.

Execution Time

  • Init

Examples

<CsoundSynthesizer>
<CsOptions>
--nosound
</CsOptions>
<CsInstruments>

;; Example file for ref - deref

sr     = 44100
ksmps  = 64
nchnls = 2
0dbfs  = 1

giA[] fillarray 0, 1000, 2000, 3000

; Example 1: take a ref from an array, deref it to create a second view of it 
instr 1
  ; a source array
  iX[] fillarray 0, 10, 20, 30, 40

  ; create a ref of iXs, return the index 
  iref ref iX
  ; now iYs points to iXs
  iY[] deref iref
  printarray iY, "", "instrument 1, iY"


  iZ[] fillarray 0, 1, 3, 5, 7

  ; create a ref, pass it to instr. 2
  schedule 2, 0, 1, ref(iZ)

  ; create another ref of iZ. In this case the event is scheduled
  ; in the future, so the source will not exist anymore when instr. 2
  ; is scheduled. This should fail.
  schedule 2, 1, 1, ref(iZ)

  turnoff
endin

instr 2
  iref = p4
  if refvalid(iref) == 1 then
    iZs[] deref iref
    printarray iZs, "", "p1=2, iZs"
  else
    prints "\n    The reference has become invalid\n"
  endif
  turnoff
endin

;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

; Example 2: extra references to keep array alive
instr 3
  ; create a source array
  kXs[] fillarray 1, 1, 2, 3, 5, 8, 13

  ; In order to bridge the time gap between the end of life of the source
  ; of a ref and the scheduled event where a deref is taken, it is possible
  ; to create a forward reference, a "promise" that one deref has been scheduled
  ; in the future.

  ; short lived event, ends before this event
  schedule 4, 0, 0.1, ref(kXs), 0

  ; starts before we end, but survives us
  schedule 4, p3-0.1, 0.2, ref(kXs), 0

  ; starts after we end, we need an extra reference 
  schedule 4, p3+1, 0.1, ref(kXs, 1), 1

  defer "prints", "  --- instr. 3 finished --- \n"
endin

instr 4
  prints "instr. 4\n   "
  kView[] deref p4, p5
  printarray kView
  defer "prints", " --- instr. 4 finished --- \n"
  ; At deinition time the memory of the `iView` array is marked as deallocated.
  ; The handle (a global structure created by the `ref` opcode) which owns the memory,
  ; is signaled that no other clients of this data are alive. It deallocates the
  ; original memory and frees itself
endin

  ;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  ; test: multiple derefs
instr 5
  iXs[] fillarray 0, 1, 4, 9
  iref ref iXs

  iYs[] deref iref
  iZs[] deref iref
  printarray iYs
  printarray iZs
  iXs[0] = 100
  printarray iZs
  turnoff
endin

; test performance of pass-by-value vs pass-by-reference
opcode arrayadd, i[], i[]i
  ; pass by value in and out
  iIn[], ix xin
  iOut[] = iIn + ix
  xout iOut
endop

opcode arrayaddref, i[], ii
  ; pass by ref in, by value out
  iref, ix xin
  iIn[] deref iref
  iOut[] = iIn + ix
  xout iOut
endop

opcode arrayadd_inplace, 0, ii
  ; in place 
  iref, ix xin
  iIn[] deref iref
  iIn += ix
endop

opcode arrayadd_byref_inout, 0, iii
  ; pass by ref in and out
  irefin, irefout, ix xin
  iIn[]  deref irefin
  iOut[] deref irefout
  if lenarray(iOut) >= lenarray(iIn) then
    iOut = iIn + ix
  endif
endop

instr testUdoPerformance1
  ; Here we test the performance gain of passing arrays by reference.
  ; Passing the input array by reference seems to produce a speedup of ~25%,
  inum = 10000
  iXs[] genarray 0, inum
  ii = 0
  it0 rtclock
  while ii < 20 do
    iYs[] arrayadd iXs, 2.0
    ii += 1
  od
  it1 rtclock
  prints "Dur UDO pass by value = %.8f \n", it1 - it0

  iref = ref(iXs)
  it0 rtclock
  iY0[] arrayaddref iref, 0.1
  iY0   arrayaddref iref, 0.2
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  iY0   arrayaddref iref, 0.3
  it1 rtclock
  prints "Dur UDO pass by ref input = %.8f \n", it1 - it0

  iZs[] genarray 0, inum
  iOut[] init lenarray(iZs)

  it0 rtclock
  irefZ = ref(iZs)
  irefOut = ref(iOut)  
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  arrayadd_byref_inout irefZ, irefOut, 0.5
  it1 rtclock

  prints "Dur UDO pass by ref in and out=%.8f \n", it1 - it0
  ; printarray iOut
endin

instr 7
  iIn[] genarray 0, 1000
  iOut[] init lenarray(iIn)
  arrayadd_byref_inout ref(iIn), ref(iOut), 0.5
  turnoff
endin

instr 8
  ; test k arrays
  ; 1. A way to convert a i-array to a k-array by taking a reference
  iXs[] genarray 0, 99
  kXs[] deref ref(iXs)

  kXs[0] = timeinsts()
  printarray kXs, metro(8)
endin

instr 9
  ; we need genarray_i because otherwise kXs is not initialized at i-time
  kXs[] genarray 0, 9
  iXs[] deref ref(kXs)
  iXs += 10
  printarray iXs
  printarray kXs
  turnoff
endin


; schedule 1, 0, 1
; schedule 3, 0, 1
; schedule 5, 0, 0.1
schedule "testUdoPerformance1", 0, 0.1
; schedule 8, 0, 4
</CsInstruments>

<CsScore>
e 10 

</CsScore>

</CsoundSynthesizer>

See also

Credits

Eduardo Moguillansky, 2019