@@ -122,8 +122,16 @@ defmodule Emily.Backend do
122122 defp ensure_binary ( bs ) when is_bitstring ( bs ) , do: :erlang . list_to_bitstring ( [ bs ] )
123123
124124 @ impl true
125- def to_binary ( % T { data: % B { ref: r } , type: { _ , bits } } = tensor , limit ) do
126- bin = Native . to_binary ( worker ( ) , r )
125+ def to_binary ( % T { data: % B { ref: r } , shape: shape , type: type } = tensor , limit ) do
126+ metadata = % { shape: shape , dtype: type }
127+
128+ bin =
129+ :telemetry . span ( [ :emily , :to_binary ] , metadata , fn ->
130+ bytes = Native . to_binary ( worker ( ) , r )
131+ { bytes , Map . put ( metadata , :byte_size , byte_size ( bytes ) ) }
132+ end )
133+
134+ { _ , bits } = type
127135 elem_bits = effective_elem_bits ( bits )
128136 size = Nx . size ( tensor )
129137
@@ -668,7 +676,7 @@ defmodule Emily.Backend do
668676 Native . reshape ( w , r , Tuple . to_list ( out . shape ) ) |> wrap ( out , w )
669677
670678 true ->
671- via_binary ( out , [ input , indices ] , & Nx . gather ( & 1 , & 2 , opts ) )
679+ via_binary ( :gather , out , [ input , indices ] , & Nx . gather ( & 1 , & 2 , opts ) )
672680 end
673681 end
674682
@@ -753,7 +761,7 @@ defmodule Emily.Backend do
753761 Native . unquote ( native_name ) ( w , ref ( t ) , axis , reverse , true ) |> wrap ( out , w )
754762 else
755763 nx_fun = unquote ( nx_name )
756- via_binary ( out , [ t ] , & apply ( Nx , nx_fun , [ & 1 , opts ] ) )
764+ via_binary ( nx_fun , out , [ t ] , & apply ( Nx , nx_fun , [ & 1 , opts ] ) )
757765 end
758766 end
759767 end
@@ -779,7 +787,7 @@ defmodule Emily.Backend do
779787 if float_like? ( type ) do
780788 batched_matmul ( out , a , contract_a , batch_a , b , contract_b , batch_b )
781789 else
782- via_binary ( out , [ a , b ] , & Nx . dot ( & 1 , contract_a , batch_a , & 2 , contract_b , batch_b ) )
790+ via_binary ( :dot , out , [ a , b ] , & Nx . dot ( & 1 , contract_a , batch_a , & 2 , contract_b , batch_b ) )
783791 end
784792 end
785793
@@ -990,10 +998,10 @@ defmodule Emily.Backend do
990998 def conv ( out , input , kernel , opts ) do
991999 cond do
9921000 opts [ :batch_group_size ] > 1 ->
993- via_binary ( out , [ input , kernel ] , & Nx . conv ( & 1 , & 2 , opts ) )
1001+ via_binary ( :conv , out , [ input , kernel ] , & Nx . conv ( & 1 , & 2 , opts ) )
9941002
9951003 match? ( { :c , _ } , out . type ) ->
996- via_binary ( out , [ input , kernel ] , & Nx . conv ( & 1 , & 2 , opts ) )
1004+ via_binary ( :conv , out , [ input , kernel ] , & Nx . conv ( & 1 , & 2 , opts ) )
9971005
9981006 true ->
9991007 w = worker ( )
@@ -1062,40 +1070,68 @@ defmodule Emily.Backend do
10621070 # those scalars land on the current global default — which is
10631071 # `Emily.Backend` during conformance tests — and the resulting
10641072 # mixed-backend operand list crashes inside BinaryBackend's op.
1065- defp via_binary ( % T { } = out , tensors , fun ) when is_list ( tensors ) do
1066- result =
1067- Nx . with_default_backend ( Nx.BinaryBackend , fn ->
1068- tensors |> transfer_all ( ) |> then ( & apply ( fun , & 1 ) )
1069- end )
1073+ defp via_binary ( op , % T { } = out , tensors , fun ) when is_atom ( op ) and is_list ( tensors ) do
1074+ metadata = fallback_metadata ( op , tensors )
1075+ Emily.Telemetry . maybe_warn_fallback ( op , metadata . input_shapes )
10701076
1071- from_binary ( out , Nx . to_binary ( result ) , [ ] )
1077+ :telemetry . span ( [ :emily , :fallback ] , metadata , fn ->
1078+ result =
1079+ Nx . with_default_backend ( Nx.BinaryBackend , fn ->
1080+ tensors |> transfer_all ( ) |> then ( & apply ( fun , & 1 ) )
1081+ end )
1082+
1083+ { from_binary ( out , Nx . to_binary ( result ) , [ ] ) , metadata }
1084+ end )
10721085 end
10731086
10741087 # Same pattern, but the op returns a tuple of tensors. `outs` is a
10751088 # tuple of output templates matching arity; positions are zipped.
1076- defp via_binary_tuple ( outs , tensors , fun ) when is_tuple ( outs ) and is_list ( tensors ) do
1077- result_tuple =
1078- Nx . with_default_backend ( Nx.BinaryBackend , fn ->
1079- tensors |> transfer_all ( ) |> then ( & apply ( fun , & 1 ) )
1080- end )
1089+ defp via_binary_tuple ( op , outs , tensors , fun )
1090+ when is_atom ( op ) and is_tuple ( outs ) and is_list ( tensors ) do
1091+ metadata = fallback_metadata ( op , tensors )
1092+ Emily.Telemetry . maybe_warn_fallback ( op , metadata . input_shapes )
1093+
1094+ :telemetry . span ( [ :emily , :fallback ] , metadata , fn ->
1095+ result_tuple =
1096+ Nx . with_default_backend ( Nx.BinaryBackend , fn ->
1097+ tensors |> transfer_all ( ) |> then ( & apply ( fun , & 1 ) )
1098+ end )
1099+
1100+ result =
1101+ outs
1102+ |> Tuple . to_list ( )
1103+ |> Enum . zip ( Tuple . to_list ( result_tuple ) )
1104+ |> Enum . map ( fn { out , r } -> from_binary ( out , Nx . to_binary ( r ) , [ ] ) end )
1105+ |> List . to_tuple ( )
1106+
1107+ { result , metadata }
1108+ end )
1109+ end
10811110
1082- outs
1083- |> Tuple . to_list ( )
1084- |> Enum . zip ( Tuple . to_list ( result_tuple ) )
1085- |> Enum . map ( fn { out , r } -> from_binary ( out , Nx . to_binary ( r ) , [ ] ) end )
1086- |> List . to_tuple ( )
1111+ defp fallback_metadata ( op , tensors ) do
1112+ % {
1113+ op: op ,
1114+ input_shapes: Enum . map ( tensors , & & 1 . shape ) ,
1115+ input_dtypes: Enum . map ( tensors , & & 1 . type )
1116+ }
10871117 end
10881118
10891119 defp transfer_all ( tensors ) ,
10901120 do: Enum . map ( tensors , & Nx . backend_transfer ( & 1 , Nx.BinaryBackend ) )
10911121
10921122 @ impl true
10931123 def reduce ( out , t , acc , opts , fun ) ,
1094- do: via_binary ( out , [ t , acc ] , & Nx . reduce ( & 1 , & 2 , opts , fun ) )
1124+ do: via_binary ( :reduce , out , [ t , acc ] , & Nx . reduce ( & 1 , & 2 , opts , fun ) )
10951125
10961126 @ impl true
10971127 def window_reduce ( out , t , acc , window_shape , opts , fun ) ,
1098- do: via_binary ( out , [ t , acc ] , & Nx . window_reduce ( & 1 , & 2 , window_shape , opts , fun ) )
1128+ do:
1129+ via_binary (
1130+ :window_reduce ,
1131+ out ,
1132+ [ t , acc ] ,
1133+ & Nx . window_reduce ( & 1 , & 2 , window_shape , opts , fun )
1134+ )
10991135
11001136 # M17: window reductions lifted off via_binary. MLX has no native
11011137 # window_* primitive — each op is composed as pad → as_strided
@@ -1234,15 +1270,33 @@ defmodule Emily.Backend do
12341270 # indices; correctness on duplicates with indexed_put is best-effort.
12351271 @ impl true
12361272 def indexed_add ( out , t , indices , updates , opts ) do
1237- apply_scatter ( out , t , indices , updates , opts , :scatter_add , & Nx . indexed_add ( & 1 , & 2 , & 3 , opts ) )
1273+ apply_scatter (
1274+ :indexed_add ,
1275+ out ,
1276+ t ,
1277+ indices ,
1278+ updates ,
1279+ opts ,
1280+ :scatter_add ,
1281+ & Nx . indexed_add ( & 1 , & 2 , & 3 , opts )
1282+ )
12381283 end
12391284
12401285 @ impl true
12411286 def indexed_put ( out , t , indices , updates , opts ) do
1242- apply_scatter ( out , t , indices , updates , opts , :scatter , & Nx . indexed_put ( & 1 , & 2 , & 3 , opts ) )
1287+ apply_scatter (
1288+ :indexed_put ,
1289+ out ,
1290+ t ,
1291+ indices ,
1292+ updates ,
1293+ opts ,
1294+ :scatter ,
1295+ & Nx . indexed_put ( & 1 , & 2 , & 3 , opts )
1296+ )
12431297 end
12441298
1245- defp apply_scatter ( out , t , indices , updates , opts , native_fun , fallback ) do
1299+ defp apply_scatter ( op , out , t , indices , updates , opts , native_fun , fallback ) do
12461300 axes = opts [ :axes ] || Enum . to_list ( 0 .. ( tuple_size ( t . shape ) - 1 ) )
12471301 indices_shape = Tuple . to_list ( indices . shape )
12481302
@@ -1255,7 +1309,7 @@ defmodule Emily.Backend do
12551309 apply ( Native , native_fun , [ w , ref ( t ) , idx_refs , updates_ref , axes ] )
12561310 |> wrap ( out , w )
12571311 else
1258- via_binary ( out , [ t , indices , updates ] , fallback )
1312+ via_binary ( op , out , [ t , indices , updates ] , fallback )
12591313 end
12601314 end
12611315
@@ -1388,7 +1442,7 @@ defmodule Emily.Backend do
13881442 { wrap ( q_ref , q_out , w ) , wrap ( r_ref , r_out , w ) }
13891443
13901444 :complete ->
1391- via_binary_tuple ( { q_out , r_out } , [ t ] , & Nx.LinAlg . qr ( & 1 , opts ) )
1445+ via_binary_tuple ( :qr , { q_out , r_out } , [ t ] , & Nx.LinAlg . qr ( & 1 , opts ) )
13921446 end
13931447 end
13941448
0 commit comments