jeffdeville

jeffdeville

I’ve got a macro that wasn’t impacting my project compilation speed much. I added a small feature to it, and now it’s ~50x slower. But at least to look at it, it certainly doesn’t appear as if 50x more work is being done.

Are there any tools I can use to determine why this is the case? After the AST is generated (this part is fast), I’m not sure where things ‘go’, or what tools I might have to identify the source of my slowdown.

Showing Posts 1 to 9

jeffdeville

jeffdeville OP

One piece about the updated, slow macro code, is that the new macro code calls the existing (compiled fast) macro code. The old code was all basically independent.

I’ve been gutting the macro trying to figure out where things are going awry. Turns out that adding in just this code (formatted Macro.to_string output) is enough to double my project compile time from 4-8s.

def(immediate_controls(pid, slave_id \\ 1)) do
  :ok
end
def(measurements_status(pid, slave_id \\ 1)) do
  :ok
end
def(basic_settings(pid, slave_id \\ 1)) do
  :ok
end
def(nameplate(pid, slave_id \\ 1)) do
  :ok
end
def(inverter_single_phase_float(pid, slave_id \\ 1)) do
  :ok
end
def(common(pid, slave_id \\ 1)) do
  :ok
end
josevalim

josevalim

Creator of Elixir

If this is being called a lot of times, then yes, the amount of code you generate will have a direct impact on compilation times. If you can post a snippet, we can help reduce the compilation time, but this may very well be a hint to find another approach that generating many many functions.

In any case, it is also worth mentioning that Elixir v1.5 speeds up how function definitions for large modules, so maybe try out Elixir master and see if that improves the situation.

jeffdeville

jeffdeville OP

Sorry @josevalim, I usually try and post more detail up front. In this case that’s a little hard and I didn’t want anyone to have to spend too much time following my business domain here. My apologies in advance.

Conceptually, I am making binary TCP calls to read data from a device using the Modbus protocol. I’ve created a macro that let’s you define these values like this:

defmodule ExModbus.SunSpecCommon do
  @doc """
  This simplest possible SunSpec device. Implements Model 1
  """
  use ExModbus
  field :manufacturer,    :string, 40005, 16, :r,  "Well known value registered with SunSpec for compliance"
  field :model,           :string, 40021, 16, :r,  "Manufacturer specific value (32 chars)"
  field :options,         :string, 40037,  8, :r,  "Manufacturer specific value (16 chars)"
  field :version,         :string, 40045,  8, :r,  "Manufacturer specific value (16 chars)"
  field :serial_number,   :string, 40053, 16, :r,  "Manufacturer specific value (32 chars)"
  field :device_address,  :uint16, 40069,  1, :rw, "Modbus device address"

  field_group :common,     [:manufacturer, :model, :options, :version, :serial_number, :device_address]
end

I was following (I hope) Chris’s guidelines in Metaprogramming Elixir, so I have the overall macro split across multiple files.

https://github.com/jeffdeville/ex_modbus/blob/fixing-group-fields/lib/ex_modbus.ex
https://github.com/jeffdeville/ex_modbus/blob/fixing-group-fields/lib/ex_modbus/macros.ex

Each field becomes either 1 method (:r) or 2 (:rw)
Reads request data starting at a memory address, and reading N bytes. Then I convert the bytes to the type
Writes of course do the same thing, but send data down the wire.

This all compiles quickly. It’s the ‘old’ code I mentioned.

But I often need to load groups of fields, and it’s more efficient to load them all at once.

field_group defines a method that will load the given fields all in 1 shot. This is the slow one.

https://github.com/jeffdeville/ex_modbus/blob/fixing-group-fields/lib/ex_modbus/macros.ex#L6

What it does, is define a composite memory block to load all at once. Then it will slice off the bytes for each field, and ask the ‘field’ generated code to deserialize each piece for me. Returning all of the data in a Map.

The small example above isn’t all that painful. But I’m doing this with about ~140 fields spread over 6 field_groups. What’s interesting, is that those 139 field calls are fast. It wasn’t until I added the field_groups that things slowed down.

Possibly Relevant
Because it’s the field_group that caused the speed issue, I don’t think this is relevant. But just in case…

The project that is actually slow to compile is this one, because of this test file:
https://github.com/jeffdeville/ex_sunspec/blob/enable-field-groups/lib/ex_sunspec/test_inverter.ex

defmodule ExSunspec.TestInverter do
  use ExSunspec, start: 40_001,
                 models: [1, 111, 120, 121, 122, 123],
                 model_1_length_65: true
  field :delete_data, :uint16, 212, 1, :rw, "Delete stored ratings of the current inverter by writing 0xFFFF"
  field :store_data, :uint16, 213, 1, :rw, "Rating data of all inverters connected to the Fronius Datamanager are persistently stored by writing 0xFFFF."
  field :active_state_code, :uint16, 214, 1, :r, "Current active state code of inverter - Description can be found in inverter manual"
  field :reset_event_flags, :uint16, 215, 1, :rw, "Write 0xFFFF to reset all event flags and active state code."
  field :model_type, :uint16, 216, 1, :rw, "Type of SunSpec models used for inverter and meter data. Write 1 or 2 and then immediately 6 to acknowledge setting.", enum_map: %{1 => "Floating point", 2 => "Integer & SF"}
end

Each of those model numbers is mapped to an XML file that contains all of the field definitions for SunSpec. So at compile time, the sunspec macros load the xml, and then invoke the ex_modbus macros.

josevalim

josevalim

Creator of Elixir

You are generating large function bodies over and over again. You want each generated function to be something like:

def unquote(name)(arg1, arg2, arg3) do
  SomeModule.do_something(unquote(name), arg1, arg2, arg3)
end

I am not sure this will make it faster but it will certainly lead to cleaner code.

jeffdeville

jeffdeville OP

Thanks Jose. I fixed the problem and I think I’ve got something interesting to report.

First, I made the changes you suggested. Boiled each method down to 1 line in another module. The compilation time was still 108 seconds.

The fix was to inspect all of the values I was writing into my @docs that were not strings. (atoms, and ints)

I changed my @docs from:

@doc """
#{unquote(desc)}
* Field Type: #{unquote(type)}
* Units: #{unquote(units)}
* Addr: #{unquote(addr)}
* Num Bytes: #{unquote(num_bytes)}
"""

to

@doc """
#{unquote(desc)}
* Field Type: #{unquote(inspect type)}
* Units: #{unquote(inspect units)}
* Addr: #{unquote(inspect addr)}
* Num Bytes: #{unquote(inspect num_bytes)}
"""

and my compilation times sped up 43x

josevalim

josevalim

Creator of Elixir

That’s very interesting. Thanks for reporting back. Which Erlang version were you using?

Can you also post what those strings would look like before and after your changes? I am wondering if inspecting is limiting something that would otherwise be very large or if it is forcing the string to be rendered as a binary which would affect the rest of compilation.

Compilation of large literal binaries/strings have been generally improved in OTP 20 though: Move expansion of strings in binaries to v3_core by josevalim · Pull Request #1131 · erlang/otp · GitHub

jeffdeville

jeffdeville OP

erlang 18.3
elixir 1.4.1

Here’s the result of Macro.to_string for the fixed version:

[[(
  @doc("#{"Type of SunSpec models used for inverter and meter data."}\
* Field Type: #{":uint16"}\
* Units: #{"\\"\\""}\
* Addr: #{"216"}\
* Num Bytes: #{"1"}\
")
  @spec(set_model_type(pid, integer, any) :: :ok)
  def(set_model_type(pid, slave_id, data)) do
    ExModbus.Runtime.set_field(pid, slave_id, data, 215, :uint16, %{1 => "Floating Point", 2 => "Integer & SF"})
  end
), (
  @doc("#{"Type of SunSpec models used for inverter and meter data."}\
* Field Type: #{":uint16"}\
* Units: #{"\\"\\""}\
* Addr: #{"216"}\
* Num Bytes: #{"1"}\
")
  @spec(model_type(pid, integer) :: {:ok, %{data: any(), transaction_id: integer(), unit_id: integer()}} | {:type_conversion_error, {any(), any()}} | {:enum_not_found_error, String.t()})
  def(model_type(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_field(pid, slave_id, 215, 1, :uint16, %{1 => "Floating Point", 2 => "Integer & SF"})
  end
)], (
  @doc("#{"Scale factor for reactive power percent."}\
* Field Type: #{":sunssf"}\
* Units: #{"\\"\\""}\
* Addr: #{"40263"}\
* Num Bytes: #{"1"}\
")
  @spec(varpct_sf(pid, integer) :: {:ok, %{data: any(), transaction_id: integer(), unit_id: integer()}} | {:type_conversion_error, {any(), any()}} | {:enum_not_found_error, String.t()})
  def(varpct_sf(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_field(pid, slave_id, 40262, 1, :sunssf, %{})
  end
), (
  @doc("#{"Manufacturer specific value (32 chars)"}\
* Field Type: #{":string"}\
* Units: #{"\\"\\""}\
* Addr: #{"40021"}\
* Num Bytes: #{"16"}\
")
  @spec(model(pid, integer) :: {:ok, %{data: any(), transaction_id: integer(), unit_id: integer()}} | {:type_conversion_error, {any(), any()}} | {:enum_not_found_error, String.t()})
  def(model(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_field(pid, slave_id, 40020, 16, :string, %{})
  end
), (
  @doc("#{"Well known value registered with SunSpec for compliance"}\
* Field Type: #{":string"}\
* Units: #{"\\"\\""}\
* Addr: #{"40005"}\
* Num Bytes: #{"16"}\
")
  @spec(manufacturer(pid, integer) :: {:ok, %{data: any(), transaction_id: integer(), unit_id: integer()}} | {:type_conversion_error, {any(), any()}} | {:enum_not_found_error, String.t()})
  def(manufacturer(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_field(pid, slave_id, 40004, 16, :string, %{})
  end
)]"
"[(
  @doc("#{""}")
  @spec(immediate_controls(pid, integer) :: {:ok, map()} | {:error, term()})
  def(immediate_controls(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40239, 24, [{:conn_wintms, :uint16, 1, %{}}, {:conn_rvrttms, :uint16, 1, %{}}, {:conn, :enum16, 1, %{0 => "DISCONNECT", 1 => "CONNECT"}}, {:wmaxlimpct, :uint16, 1, %{}}, {:wmaxlimpct_wintms, :uint16, 1, %{}}, {:wmaxlimpct_rvrttms, :uint16, 1, %{}}, {:wmaxlimpct_rmptms, :uint16, 1, %{}}, {:wmaxlim_ena, :enum16, 1, %{0 => "DISABLED", 1 => "ENABLED"}}, {:outpfset, :int16, 1, %{}}, {:outpfset_wintms, :uint16, 1, %{}}, {:outpfset_rvrttms, :uint16, 1, %{}}, {:outpfset_rmptms, :uint16, 1, %{}}, {:outpfset_ena, :enum16, 1, %{0 => "DISABLED", 1 => "ENABLED"}}, {:varwmaxpct, :int16, 1, %{}}, {:varmaxpct, :int16, 1, %{}}, {:varavalpct, :int16, 1, %{}}, {:varpct_wintms, :uint16, 1, %{}}, {:varpct_rvrttms, :uint16, 1, %{}}, {:varpct_rmptms, :uint16, 1, %{}}, {:varpct_mod, :enum16, 1, %{0 => "NONE", 1 => "WMax", 2 => "VArMax", 3 => "VArAval"}}, {:varpct_ena, :enum16, 1, %{0 => "DISABLED", 1 => "ENABLED"}}, {:wmaxlimpct_sf, :sunssf, 1, %{}}, {:outpfset_sf, :sunssf, 1, %{}}, {:varpct_sf, :sunssf, 1, %{}}])
  end
), (
  @doc("#{""}")
  @spec(measurements_status(pid, integer) :: {:ok, map()} | {:error, term()})
  def(measurements_status(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40193, 44, [{:pvconn, :bitfield16, 1, %{0 => "CONNECTED", 1 => "AVAILABLE", 2 => "OPERATING", 3 => "TEST"}}, {:storconn, :bitfield16, 1, %{0 => "CONNECTED", 1 => "AVAILABLE", 2 => "OPERATING", 3 => "TEST"}}, {:ecpconn, :bitfield16, 1, %{0 => "CONNECTED"}}, {:actwh, :acc64, 4, %{}}, {:actvah, :acc64, 4, %{}}, {:actvarhq1, :acc64, 4, %{}}, {:actvarhq2, :acc64, 4, %{}}, {:actvarhq3, :acc64, 4, %{}}, {:actvarhq4, :acc64, 4, %{}}, {:varaval, :int16, 1, %{}}, {:varaval_sf, :sunssf, 1, %{}}, {:waval, :uint16, 1, %{}}, {:waval_sf, :sunssf, 1, %{}}, {:stsetlimmsk, :bitfield32, 2, %{0 => "WMax", 1 => "VAMax", 2 => "VArAval", 3 => "VArMaxQ1", 4 => "VArMaxQ2", 5 => "VArMaxQ3", 6 => "VArMaxQ4", 7 => "PFMinQ1", 8 => "PFMinQ2", 9 => "PFMinQ3", 10 => "PFMinQ4"}}, {:stactctl, :bitfield32, 2, %{0 => "FixedW", 1 => "FixedVAR", 2 => "FixedPF", 3 => "Volt-VAr", 4 => "Freq-Watt-Param", 5 => "Freq-Watt-Curve", 6 => "Dyn-Reactive-Current", 7 => "LVRT", 8 => "HVRT", 9 => "Watt-PF", 10 => "Volt-Watt", 12 => "Scheduled", 13 => "LFRT", 14 => "HFRT"}}, {:tmsrc, :string, 4, %{}}, {:tms, :uint32, 2, %{}}, {:rtst, :bitfield16, 1, %{0 => "LVRT_ACTIVE", 1 => "HVRT_ACTIVE", 2 => "LFRT_ACTIVE", 3 => "HFRT_ACTIVE"}}, {:ris, :uint16, 1, %{}}, {:ris_sf, :sunssf, 1, %{}}])
  end
), (
  @doc("#{""}")
  @spec(basic_settings(pid, integer) :: {:ok, map()} | {:error, term()})
  def(basic_settings(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40161, 30, [{:wmax, :uint16, 1, %{}}, {:vref, :uint16, 1, %{}}, {:vrefofs, :int16, 1, %{}}, {:vmax, :uint16, 1, %{}}, {:vmin, :uint16, 1, %{}}, {:vamax, :uint16, 1, %{}}, {:varmaxq1, :int16, 1, %{}}, {:varmaxq2, :int16, 1, %{}}, {:varmaxq3, :int16, 1, %{}}, {:varmaxq4, :int16, 1, %{}}, {:wgra, :uint16, 1, %{}}, {:pfminq1, :int16, 1, %{}}, {:pfminq2, :int16, 1, %{}}, {:pfminq3, :int16, 1, %{}}, {:pfminq4, :int16, 1, %{}}, {:varact, :enum16, 1, %{1 => "SWITCH", 2 => "MAINTAIN"}}, {:clctotva, :enum16, 1, %{1 => "VECTOR", 2 => "ARITHMETIC"}}, {:maxrmprte, :uint16, 1, %{}}, {:ecpnomhz, :uint16, 1, %{}}, {:connph, :enum16, 1, %{1 => "A", 2 => "B", 3 => "C"}}, {:wmax_sf, :sunssf, 1, %{}}, {:vref_sf, :sunssf, 1, %{}}, {:vrefofs_sf, :sunssf, 1, %{}}, {:vminmax_sf, :sunssf, 1, %{}}, {:vamax_sf, :sunssf, 1, %{}}, {:varmax_sf, :sunssf, 1, %{}}, {:wgra_sf, :sunssf, 1, %{}}, {:pfmin_sf, :sunssf, 1, %{}}, {:maxrmprte_sf, :sunssf, 1, %{}}, {:ecpnomhz_sf, :sunssf, 1, %{}}])
  end
), (
  @doc("#{""}")
  @spec(nameplate(pid, integer) :: {:ok, map()} | {:error, term()})
  def(nameplate(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40133, 26, [{:dertyp, :enum16, 1, %{4 => "PV", 82 => "PV_STOR"}}, {:wrtg, :uint16, 1, %{}}, {:wrtg_sf, :sunssf, 1, %{}}, {:vartg, :uint16, 1, %{}}, {:vartg_sf, :sunssf, 1, %{}}, {:varrtgq1, :int16, 1, %{}}, {:varrtgq2, :int16, 1, %{}}, {:varrtgq3, :int16, 1, %{}}, {:varrtgq4, :int16, 1, %{}}, {:varrtg_sf, :sunssf, 1, %{}}, {:artg, :uint16, 1, %{}}, {:artg_sf, :sunssf, 1, %{}}, {:pfrtgq1, :int16, 1, %{}}, {:pfrtgq2, :int16, 1, %{}}, {:pfrtgq3, :int16, 1, %{}}, {:pfrtgq4, :int16, 1, %{}}, {:pfrtg_sf, :sunssf, 1, %{}}, {:whrtg, :uint16, 1, %{}}, {:whrtg_sf, :sunssf, 1, %{}}, {:ahrrtg, :uint16, 1, %{}}, {:ahrrtg_sf, :sunssf, 1, %{}}, {:maxcharte, :uint16, 1, %{}}, {:maxcharte_sf, :sunssf, 1, %{}}, {:maxdischarte, :uint16, 1, %{}}, {:maxdischarte_sf, :sunssf, 1, %{}}, {:pad, :pad, 1, %{}}])
  end
), (
  @doc("#{""}")
  @spec(inverter_single_phase_float(pid, integer) :: {:ok, map()} | {:error, term()})
  def(inverter_single_phase_float(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40071, 60, [{:amps, :float32, 2, %{}}, {:amps_phasea, :float32, 2, %{}}, {:amps_phaseb, :float32, 2, %{}}, {:amps_phasec, :float32, 2, %{}}, {:phase_voltage_ab, :float32, 2, %{}}, {:phase_voltage_bc, :float32, 2, %{}}, {:phase_voltage_ca, :float32, 2, %{}}, {:phase_voltage_an, :float32, 2, %{}}, {:phase_voltage_bn, :float32, 2, %{}}, {:phase_voltage_cn, :float32, 2, %{}}, {:watts, :float32, 2, %{}}, {:hz, :float32, 2, %{}}, {:va, :float32, 2, %{}}, {:var, :float32, 2, %{}}, {:pf, :float32, 2, %{}}, {:watthours, :float32, 2, %{}}, {:dc_amps, :float32, 2, %{}}, {:dc_voltage, :float32, 2, %{}}, {:dc_watts, :float32, 2, %{}}, {:cabinet_temperature, :float32, 2, %{}}, {:heat_sink_temperature, :float32, 2, %{}}, {:transformer_temperature, :float32, 2, %{}}, {:other_temperature, :float32, 2, %{}}, {:operating_state, :enum16, 1, %{1 => "ggOFF", 2 => "ggSLEEPING", 3 => "ggSTARTING", 4 => "ggMPPT", 5 => "ggTHROTTLED", 6 => "ggSHUTTING_DOWN", 7 => "ggFAULT", 8 => "ggSTANDBY"}}, {:vendor_operating_state, :enum16, 1, %{}}, {:event1, :bitfield32, 2, %{0 => "GROUND_FAULT", 1 => "DC_OVER_VOLT", 2 => "AC_DISCONNECT", 3 => "DC_DISCONNECT", 4 => "GRID_DISCONNECT", 5 => "CABINET_OPEN", 6 => "MANUAL_SHUTDOWN", 7 => "OVER_TEMP", 8 => "OVER_FREQUENCY", 9 => "UNDER_FREQUENCY", 10 => "AC_OVER_VOLT", 11 => "AC_UNDER_VOLT", 12 => "BLOWN_STRING_FUSE", 13 => "UNDER_TEMP", 14 => "MEMORY_LOSS", 15 => "HW_TEST_FAILURE"}}, {:event_bitfield_2, :bitfield32, 2, %{}}, {:vendor_event_bitfield_1, :bitfield32, 2, %{}}, {:vendor_event_bitfield_2, :bitfield32, 2, %{}}, {:vendor_event_bitfield_3, :bitfield32, 2, %{}}, {:vendor_event_bitfield_4, :bitfield32, 2, %{}}])
  end
), (
  @doc("#{""}")
  @spec(common(pid, integer) :: {:ok, map()} | {:error, term()})
  def(common(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40004, 65, [{:manufacturer, :string, 16, %{}}, {:model, :string, 16, %{}}, {:options, :string, 8, %{}}, {:version, :string, 8, %{}}, {:serial_number, :string, 16, %{}}, {:device_address, :uint16, 1, %{}}])
  end
)]

and the slow version:

[[(
  @doc("#{"Type of SunSpec models used for inverter and meter data."}\
* Field Type: #{:uint16}\
* Units: #{""}\
* Addr: #{216}\
* Num Bytes: #{1}\
")
  @spec(set_model_type(pid, integer, any) :: :ok)
  def(set_model_type(pid, slave_id, data)) do
    ExModbus.Runtime.set_field(pid, slave_id, data, 215, :uint16, %{1 => "Floating Point", 2 => "Integer & SF"})
  end
), (
  @doc("#{"Type of SunSpec models used for inverter and meter data."}\
* Field Type: #{:uint16}\
* Units: #{""}\
* Addr: #{216}\
* Num Bytes: #{1}\
")
  @spec(model_type(pid, integer) :: {:ok, %{data: any(), transaction_id: integer(), unit_id: integer()}} | {:type_conversion_error, {any(), any()}} | {:enum_not_found_error, String.t()})
  def(model_type(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_field(pid, slave_id, 215, 1, :uint16, %{1 => "Floating Point", 2 => "Integer & SF"})
  end
)], (
  @doc("#{"Scale factor for reactive power percent."}\
* Field Type: #{:sunssf}\
* Units: #{""}\
* Addr: #{40263}\
* Num Bytes: #{1}\
")
  @spec(varpct_sf(pid, integer) :: {:ok, %{data: any(), transaction_id: integer(), unit_id: integer()}} | {:type_conversion_error, {any(), any()}} | {:enum_not_found_error, String.t()})
  def(varpct_sf(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_field(pid, slave_id, 40262, 1, :sunssf, %{})
  end
), (
  @doc("#{"Scale factor for power factor."}\
* Field Type: #{:sunssf}\
* Units: #{""}\
* Addr: #{40262}\
* Num Bytes: #{1}\
")
  @spec(outpfset_sf(pid, integer) :: {:ok, %{data: any(), transaction_id: integer(), unit_id: integer()}} | {:type_conversion_error, {any(), any()}} | {:enum_not_found_error, String.t()})
  def(outpfset_sf(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_field(pid, slave_id, 40261, 1, :sunssf, %{})
  end
), (
  @doc("#{"Scale factor for power output percent."}\
* Field Type: #{:sunssf}\
* Units: #{""}\
* Addr: #{40261}\
* Num Bytes: #{1}\
")
  @spec(wmaxlimpct_sf(pid, integer) :: {:ok, %{data: any(), transaction_id: integer(), unit_id: integer()}} | {:type_conversion_error, {any(), any()}} | {:enum_not_found_error, String.t()})
  def(wmaxlimpct_sf(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_field(pid, slave_id, 40260, 1, :sunssf, %{})
  end
), [(
  @doc("#{"Enumerated valued.  Percent limit VAr enable/disable control."}\
* Field Type: #{:enum16}\
* Units: #{""}\
* Addr: #{40260}\
* Num Bytes: #{1}\
")
  @spec(set_varpct_ena(pid, integer, any) :: :ok)
  def(set_varpct_ena(pid, slave_id, data)) do
    ExModbus.Runtime.set_field(pid, slave_id, data, 40259, :enum16, %{0 => "DISABLED", 1 => "ENABLED"})
  end
), (
  @doc("#{"Manufacturer specific value (16 chars)"}\
* Field Type: #{:string}\
* Units: #{""}\
* Addr: #{40037}\
* Num Bytes: #{8}\
")
  @spec(options(pid, integer) :: {:ok, %{data: any(), transaction_id: integer(), unit_id: integer()}} | {:type_conversion_error, {any(), any()}} | {:enum_not_found_error, String.t()})
  def(options(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_field(pid, slave_id, 40036, 8, :string, %{})
  end
), (
  @doc("#{"Manufacturer specific value (32 chars)"}\
* Field Type: #{:string}\
* Units: #{""}\
* Addr: #{40021}\
* Num Bytes: #{16}\
")
  @spec(model(pid, integer) :: {:ok, %{data: any(), transaction_id: integer(), unit_id: integer()}} | {:type_conversion_error, {any(), any()}} | {:enum_not_found_error, String.t()})
  def(model(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_field(pid, slave_id, 40020, 16, :string, %{})
  end
), (
  @doc("#{"Well known value registered with SunSpec for compliance"}\
* Field Type: #{:string}\
* Units: #{""}\
* Addr: #{40005}\
* Num Bytes: #{16}\
")
  @spec(manufacturer(pid, integer) :: {:ok, %{data: any(), transaction_id: integer(), unit_id: integer()}} | {:type_conversion_error, {any(), any()}} | {:enum_not_found_error, String.t()})
  def(manufacturer(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_field(pid, slave_id, 40004, 16, :string, %{})
  end
)]"
"[(
  @doc("#{""}")
  @spec(immediate_controls(pid, integer) :: {:ok, map()} | {:error, term()})
  def(immediate_controls(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40239, 24, [{:conn_wintms, :uint16, 1, %{}}, {:conn_rvrttms, :uint16, 1, %{}}, {:conn, :enum16, 1, %{0 => "DISCONNECT", 1 => "CONNECT"}}, {:wmaxlimpct, :uint16, 1, %{}}, {:wmaxlimpct_wintms, :uint16, 1, %{}}, {:wmaxlimpct_rvrttms, :uint16, 1, %{}}, {:wmaxlimpct_rmptms, :uint16, 1, %{}}, {:wmaxlim_ena, :enum16, 1, %{0 => "DISABLED", 1 => "ENABLED"}}, {:outpfset, :int16, 1, %{}}, {:outpfset_wintms, :uint16, 1, %{}}, {:outpfset_rvrttms, :uint16, 1, %{}}, {:outpfset_rmptms, :uint16, 1, %{}}, {:outpfset_ena, :enum16, 1, %{0 => "DISABLED", 1 => "ENABLED"}}, {:varwmaxpct, :int16, 1, %{}}, {:varmaxpct, :int16, 1, %{}}, {:varavalpct, :int16, 1, %{}}, {:varpct_wintms, :uint16, 1, %{}}, {:varpct_rvrttms, :uint16, 1, %{}}, {:varpct_rmptms, :uint16, 1, %{}}, {:varpct_mod, :enum16, 1, %{0 => "NONE", 1 => "WMax", 2 => "VArMax", 3 => "VArAval"}}, {:varpct_ena, :enum16, 1, %{0 => "DISABLED", 1 => "ENABLED"}}, {:wmaxlimpct_sf, :sunssf, 1, %{}}, {:outpfset_sf, :sunssf, 1, %{}}, {:varpct_sf, :sunssf, 1, %{}}])
  end
), (
  @doc("#{""}")
  @spec(measurements_status(pid, integer) :: {:ok, map()} | {:error, term()})
  def(measurements_status(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40193, 44, [{:pvconn, :bitfield16, 1, %{0 => "CONNECTED", 1 => "AVAILABLE", 2 => "OPERATING", 3 => "TEST"}}, {:storconn, :bitfield16, 1, %{0 => "CONNECTED", 1 => "AVAILABLE", 2 => "OPERATING", 3 => "TEST"}}, {:ecpconn, :bitfield16, 1, %{0 => "CONNECTED"}}, {:actwh, :acc64, 4, %{}}, {:actvah, :acc64, 4, %{}}, {:actvarhq1, :acc64, 4, %{}}, {:actvarhq2, :acc64, 4, %{}}, {:actvarhq3, :acc64, 4, %{}}, {:actvarhq4, :acc64, 4, %{}}, {:varaval, :int16, 1, %{}}, {:varaval_sf, :sunssf, 1, %{}}, {:waval, :uint16, 1, %{}}, {:waval_sf, :sunssf, 1, %{}}, {:stsetlimmsk, :bitfield32, 2, %{0 => "WMax", 1 => "VAMax", 2 => "VArAval", 3 => "VArMaxQ1", 4 => "VArMaxQ2", 5 => "VArMaxQ3", 6 => "VArMaxQ4", 7 => "PFMinQ1", 8 => "PFMinQ2", 9 => "PFMinQ3", 10 => "PFMinQ4"}}, {:stactctl, :bitfield32, 2, %{0 => "FixedW", 1 => "FixedVAR", 2 => "FixedPF", 3 => "Volt-VAr", 4 => "Freq-Watt-Param", 5 => "Freq-Watt-Curve", 6 => "Dyn-Reactive-Current", 7 => "LVRT", 8 => "HVRT", 9 => "Watt-PF", 10 => "Volt-Watt", 12 => "Scheduled", 13 => "LFRT", 14 => "HFRT"}}, {:tmsrc, :string, 4, %{}}, {:tms, :uint32, 2, %{}}, {:rtst, :bitfield16, 1, %{0 => "LVRT_ACTIVE", 1 => "HVRT_ACTIVE", 2 => "LFRT_ACTIVE", 3 => "HFRT_ACTIVE"}}, {:ris, :uint16, 1, %{}}, {:ris_sf, :sunssf, 1, %{}}])
  end
), (
  @doc("#{""}")
  @spec(basic_settings(pid, integer) :: {:ok, map()} | {:error, term()})
  def(basic_settings(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40161, 30, [{:wmax, :uint16, 1, %{}}, {:vref, :uint16, 1, %{}}, {:vrefofs, :int16, 1, %{}}, {:vmax, :uint16, 1, %{}}, {:vmin, :uint16, 1, %{}}, {:vamax, :uint16, 1, %{}}, {:varmaxq1, :int16, 1, %{}}, {:varmaxq2, :int16, 1, %{}}, {:varmaxq3, :int16, 1, %{}}, {:varmaxq4, :int16, 1, %{}}, {:wgra, :uint16, 1, %{}}, {:pfminq1, :int16, 1, %{}}, {:pfminq2, :int16, 1, %{}}, {:pfminq3, :int16, 1, %{}}, {:pfminq4, :int16, 1, %{}}, {:varact, :enum16, 1, %{1 => "SWITCH", 2 => "MAINTAIN"}}, {:clctotva, :enum16, 1, %{1 => "VECTOR", 2 => "ARITHMETIC"}}, {:maxrmprte, :uint16, 1, %{}}, {:ecpnomhz, :uint16, 1, %{}}, {:connph, :enum16, 1, %{1 => "A", 2 => "B", 3 => "C"}}, {:wmax_sf, :sunssf, 1, %{}}, {:vref_sf, :sunssf, 1, %{}}, {:vrefofs_sf, :sunssf, 1, %{}}, {:vminmax_sf, :sunssf, 1, %{}}, {:vamax_sf, :sunssf, 1, %{}}, {:varmax_sf, :sunssf, 1, %{}}, {:wgra_sf, :sunssf, 1, %{}}, {:pfmin_sf, :sunssf, 1, %{}}, {:maxrmprte_sf, :sunssf, 1, %{}}, {:ecpnomhz_sf, :sunssf, 1, %{}}])
  end
), (
  @doc("#{""}")
  @spec(nameplate(pid, integer) :: {:ok, map()} | {:error, term()})
  def(nameplate(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40133, 26, [{:dertyp, :enum16, 1, %{4 => "PV", 82 => "PV_STOR"}}, {:wrtg, :uint16, 1, %{}}, {:wrtg_sf, :sunssf, 1, %{}}, {:vartg, :uint16, 1, %{}}, {:vartg_sf, :sunssf, 1, %{}}, {:varrtgq1, :int16, 1, %{}}, {:varrtgq2, :int16, 1, %{}}, {:varrtgq3, :int16, 1, %{}}, {:varrtgq4, :int16, 1, %{}}, {:varrtg_sf, :sunssf, 1, %{}}, {:artg, :uint16, 1, %{}}, {:artg_sf, :sunssf, 1, %{}}, {:pfrtgq1, :int16, 1, %{}}, {:pfrtgq2, :int16, 1, %{}}, {:pfrtgq3, :int16, 1, %{}}, {:pfrtgq4, :int16, 1, %{}}, {:pfrtg_sf, :sunssf, 1, %{}}, {:whrtg, :uint16, 1, %{}}, {:whrtg_sf, :sunssf, 1, %{}}, {:ahrrtg, :uint16, 1, %{}}, {:ahrrtg_sf, :sunssf, 1, %{}}, {:maxcharte, :uint16, 1, %{}}, {:maxcharte_sf, :sunssf, 1, %{}}, {:maxdischarte, :uint16, 1, %{}}, {:maxdischarte_sf, :sunssf, 1, %{}}, {:pad, :pad, 1, %{}}])
  end
), (
  @doc("#{""}")
  @spec(inverter_single_phase_float(pid, integer) :: {:ok, map()} | {:error, term()})
  def(inverter_single_phase_float(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40071, 60, [{:amps, :float32, 2, %{}}, {:amps_phasea, :float32, 2, %{}}, {:amps_phaseb, :float32, 2, %{}}, {:amps_phasec, :float32, 2, %{}}, {:phase_voltage_ab, :float32, 2, %{}}, {:phase_voltage_bc, :float32, 2, %{}}, {:phase_voltage_ca, :float32, 2, %{}}, {:phase_voltage_an, :float32, 2, %{}}, {:phase_voltage_bn, :float32, 2, %{}}, {:phase_voltage_cn, :float32, 2, %{}}, {:watts, :float32, 2, %{}}, {:hz, :float32, 2, %{}}, {:va, :float32, 2, %{}}, {:var, :float32, 2, %{}}, {:pf, :float32, 2, %{}}, {:watthours, :float32, 2, %{}}, {:dc_amps, :float32, 2, %{}}, {:dc_voltage, :float32, 2, %{}}, {:dc_watts, :float32, 2, %{}}, {:cabinet_temperature, :float32, 2, %{}}, {:heat_sink_temperature, :float32, 2, %{}}, {:transformer_temperature, :float32, 2, %{}}, {:other_temperature, :float32, 2, %{}}, {:operating_state, :enum16, 1, %{1 => "ggOFF", 2 => "ggSLEEPING", 3 => "ggSTARTING", 4 => "ggMPPT", 5 => "ggTHROTTLED", 6 => "ggSHUTTING_DOWN", 7 => "ggFAULT", 8 => "ggSTANDBY"}}, {:vendor_operating_state, :enum16, 1, %{}}, {:event1, :bitfield32, 2, %{0 => "GROUND_FAULT", 1 => "DC_OVER_VOLT", 2 => "AC_DISCONNECT", 3 => "DC_DISCONNECT", 4 => "GRID_DISCONNECT", 5 => "CABINET_OPEN", 6 => "MANUAL_SHUTDOWN", 7 => "OVER_TEMP", 8 => "OVER_FREQUENCY", 9 => "UNDER_FREQUENCY", 10 => "AC_OVER_VOLT", 11 => "AC_UNDER_VOLT", 12 => "BLOWN_STRING_FUSE", 13 => "UNDER_TEMP", 14 => "MEMORY_LOSS", 15 => "HW_TEST_FAILURE"}}, {:event_bitfield_2, :bitfield32, 2, %{}}, {:vendor_event_bitfield_1, :bitfield32, 2, %{}}, {:vendor_event_bitfield_2, :bitfield32, 2, %{}}, {:vendor_event_bitfield_3, :bitfield32, 2, %{}}, {:vendor_event_bitfield_4, :bitfield32, 2, %{}}])
  end
), (
  @doc("#{""}")
  @spec(common(pid, integer) :: {:ok, map()} | {:error, term()})
  def(common(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40004, 65, [{:manufacturer, :string, 16, %{}}, {:model, :string, 16, %{}}, {:options, :string, 8, %{}}, {:version, :string, 8, %{}}, {:serial_number, :string, 16, %{}}, {:device_address, :uint16, 1, %{}}])
  end
)]"
"[(
  @doc("#{""}")
  @spec(immediate_controls(pid, integer) :: {:ok, map()} | {:error, term()})
  def(immediate_controls(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40229, 24, [{:conn_wintms, :uint16, 1, %{}}, {:conn_rvrttms, :uint16, 1, %{}}, {:conn, :enum16, 1, %{0 => "DISCONNECT", 1 => "CONNECT"}}, {:wmaxlimpct, :uint16, 1, %{}}, {:wmaxlimpct_wintms, :uint16, 1, %{}}, {:wmaxlimpct_rvrttms, :uint16, 1, %{}}, {:wmaxlimpct_rmptms, :uint16, 1, %{}}, {:wmaxlim_ena, :enum16, 1, %{0 => "DISABLED", 1 => "ENABLED"}}, {:outpfset, :int16, 1, %{}}, {:outpfset_wintms, :uint16, 1, %{}}, {:outpfset_rvrttms, :uint16, 1, %{}}, {:outpfset_rmptms, :uint16, 1, %{}}, {:outpfset_ena, :enum16, 1, %{0 => "DISABLED", 1 => "ENABLED"}}, {:varwmaxpct, :int16, 1, %{}}, {:varmaxpct, :int16, 1, %{}}, {:varavalpct, :int16, 1, %{}}, {:varpct_wintms, :uint16, 1, %{}}, {:varpct_rvrttms, :uint16, 1, %{}}, {:varpct_rmptms, :uint16, 1, %{}}, {:varpct_mod, :enum16, 1, %{0 => "NONE", 1 => "WMax", 2 => "VArMax", 3 => "VArAval"}}, {:varpct_ena, :enum16, 1, %{0 => "DISABLED", 1 => "ENABLED"}}, {:wmaxlimpct_sf, :sunssf, 1, %{}}, {:outpfset_sf, :sunssf, 1, %{}}, {:varpct_sf, :sunssf, 1, %{}}])
  end
), (
  @doc("#{""}")
  @spec(measurements_status(pid, integer) :: {:ok, map()} | {:error, term()})
  def(measurements_status(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40183, 44, [{:pvconn, :bitfield16, 1, %{0 => "CONNECTED", 1 => "AVAILABLE", 2 => "OPERATING", 3 => "TEST"}}, {:storconn, :bitfield16, 1, %{0 => "CONNECTED", 1 => "AVAILABLE", 2 => "OPERATING", 3 => "TEST"}}, {:ecpconn, :bitfield16, 1, %{0 => "CONNECTED"}}, {:actwh, :acc64, 4, %{}}, {:actvah, :acc64, 4, %{}}, {:actvarhq1, :acc64, 4, %{}}, {:actvarhq2, :acc64, 4, %{}}, {:actvarhq3, :acc64, 4, %{}}, {:actvarhq4, :acc64, 4, %{}}, {:varaval, :int16, 1, %{}}, {:varaval_sf, :sunssf, 1, %{}}, {:waval, :uint16, 1, %{}}, {:waval_sf, :sunssf, 1, %{}}, {:stsetlimmsk, :bitfield32, 2, %{0 => "WMax", 1 => "VAMax", 2 => "VArAval", 3 => "VArMaxQ1", 4 => "VArMaxQ2", 5 => "VArMaxQ3", 6 => "VArMaxQ4", 7 => "PFMinQ1", 8 => "PFMinQ2", 9 => "PFMinQ3", 10 => "PFMinQ4"}}, {:stactctl, :bitfield32, 2, %{0 => "FixedW", 1 => "FixedVAR", 2 => "FixedPF", 3 => "Volt-VAr", 4 => "Freq-Watt-Param", 5 => "Freq-Watt-Curve", 6 => "Dyn-Reactive-Current", 7 => "LVRT", 8 => "HVRT", 9 => "Watt-PF", 10 => "Volt-Watt", 12 => "Scheduled", 13 => "LFRT", 14 => "HFRT"}}, {:tmsrc, :string, 4, %{}}, {:tms, :uint32, 2, %{}}, {:rtst, :bitfield16, 1, %{0 => "LVRT_ACTIVE", 1 => "HVRT_ACTIVE", 2 => "LFRT_ACTIVE", 3 => "HFRT_ACTIVE"}}, {:ris, :uint16, 1, %{}}, {:ris_sf, :sunssf, 1, %{}}])
  end
), (
  @doc("#{""}")
  @spec(basic_settings(pid, integer) :: {:ok, map()} | {:error, term()})
  def(basic_settings(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40151, 30, [{:wmax, :uint16, 1, %{}}, {:vref, :uint16, 1, %{}}, {:vrefofs, :int16, 1, %{}}, {:vmax, :uint16, 1, %{}}, {:vmin, :uint16, 1, %{}}, {:vamax, :uint16, 1, %{}}, {:varmaxq1, :int16, 1, %{}}, {:varmaxq2, :int16, 1, %{}}, {:varmaxq3, :int16, 1, %{}}, {:varmaxq4, :int16, 1, %{}}, {:wgra, :uint16, 1, %{}}, {:pfminq1, :int16, 1, %{}}, {:pfminq2, :int16, 1, %{}}, {:pfminq3, :int16, 1, %{}}, {:pfminq4, :int16, 1, %{}}, {:varact, :enum16, 1, %{1 => "SWITCH", 2 => "MAINTAIN"}}, {:clctotva, :enum16, 1, %{1 => "VECTOR", 2 => "ARITHMETIC"}}, {:maxrmprte, :uint16, 1, %{}}, {:ecpnomhz, :uint16, 1, %{}}, {:connph, :enum16, 1, %{1 => "A", 2 => "B", 3 => "C"}}, {:wmax_sf, :sunssf, 1, %{}}, {:vref_sf, :sunssf, 1, %{}}, {:vrefofs_sf, :sunssf, 1, %{}}, {:vminmax_sf, :sunssf, 1, %{}}, {:vamax_sf, :sunssf, 1, %{}}, {:varmax_sf, :sunssf, 1, %{}}, {:wgra_sf, :sunssf, 1, %{}}, {:pfmin_sf, :sunssf, 1, %{}}, {:maxrmprte_sf, :sunssf, 1, %{}}, {:ecpnomhz_sf, :sunssf, 1, %{}}])
  end
), (
  @doc("#{""}")
  @spec(nameplate(pid, integer) :: {:ok, map()} | {:error, term()})
  def(nameplate(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40123, 26, [{:dertyp, :enum16, 1, %{4 => "PV", 82 => "PV_STOR"}}, {:wrtg, :uint16, 1, %{}}, {:wrtg_sf, :sunssf, 1, %{}}, {:vartg, :uint16, 1, %{}}, {:vartg_sf, :sunssf, 1, %{}}, {:varrtgq1, :int16, 1, %{}}, {:varrtgq2, :int16, 1, %{}}, {:varrtgq3, :int16, 1, %{}}, {:varrtgq4, :int16, 1, %{}}, {:varrtg_sf, :sunssf, 1, %{}}, {:artg, :uint16, 1, %{}}, {:artg_sf, :sunssf, 1, %{}}, {:pfrtgq1, :int16, 1, %{}}, {:pfrtgq2, :int16, 1, %{}}, {:pfrtgq3, :int16, 1, %{}}, {:pfrtgq4, :int16, 1, %{}}, {:pfrtg_sf, :sunssf, 1, %{}}, {:whrtg, :uint16, 1, %{}}, {:whrtg_sf, :sunssf, 1, %{}}, {:ahrrtg, :uint16, 1, %{}}, {:ahrrtg_sf, :sunssf, 1, %{}}, {:maxcharte, :uint16, 1, %{}}, {:maxcharte_sf, :sunssf, 1, %{}}, {:maxdischarte, :uint16, 1, %{}}, {:maxdischarte_sf, :sunssf, 1, %{}}, {:pad, :pad, 1, %{}}])
  end
), (
  @doc("#{""}")
  @spec(inverter_single_phase(pid, integer) :: {:ok, map()} | {:error, term()})
  def(inverter_single_phase(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40071, 50, [{:amps, :uint16, 1, %{}}, {:amps_phasea, :uint16, 1, %{}}, {:amps_phaseb, :uint16, 1, %{}}, {:amps_phasec, :uint16, 1, %{}}, {:a_sf, :sunssf, 1, %{}}, {:phase_voltage_ab, :uint16, 1, %{}}, {:phase_voltage_bc, :uint16, 1, %{}}, {:phase_voltage_ca, :uint16, 1, %{}}, {:phase_voltage_an, :uint16, 1, %{}}, {:phase_voltage_bn, :uint16, 1, %{}}, {:phase_voltage_cn, :uint16, 1, %{}}, {:v_sf, :sunssf, 1, %{}}, {:watts, :int16, 1, %{}}, {:w_sf, :sunssf, 1, %{}}, {:hz, :uint16, 1, %{}}, {:hz_sf, :sunssf, 1, %{}}, {:va, :int16, 1, %{}}, {:va_sf, :sunssf, 1, %{}}, {:var, :int16, 1, %{}}, {:var_sf, :sunssf, 1, %{}}, {:pf, :int16, 1, %{}}, {:pf_sf, :sunssf, 1, %{}}, {:watthours, :acc32, 2, %{}}, {:wh_sf, :sunssf, 1, %{}}, {:dc_amps, :uint16, 1, %{}}, {:dca_sf, :sunssf, 1, %{}}, {:dc_voltage, :uint16, 1, %{}}, {:dcv_sf, :sunssf, 1, %{}}, {:dc_watts, :int16, 1, %{}}, {:dcw_sf, :sunssf, 1, %{}}, {:cabinet_temperature, :int16, 1, %{}}, {:heat_sink_temperature, :int16, 1, %{}}, {:transformer_temperature, :int16, 1, %{}}, {:other_temperature, :int16, 1, %{}}, {:tmp_sf, :sunssf, 1, %{}}, {:operating_state, :enum16, 1, %{1 => "OFF", 2 => "SLEEPING", 3 => "STARTING", 4 => "MPPT", 5 => "THROTTLED", 6 => "SHUTTING_DOWN", 7 => "FAULT", 8 => "STANDBY"}}, {:vendor_operating_state, :enum16, 1, %{}}, {:event1, :bitfield32, 2, %{0 => "GROUND_FAULT", 1 => "DC_OVER_VOLT", 2 => "AC_DISCONNECT", 3 => "DC_DISCONNECT", 4 => "GRID_DISCONNECT", 5 => "CABINET_OPEN", 6 => "MANUAL_SHUTDOWN", 7 => "OVER_TEMP", 8 => "OVER_FREQUENCY", 9 => "UNDER_FREQUENCY", 10 => "AC_OVER_VOLT", 11 => "AC_UNDER_VOLT", 12 => "BLOWN_STRING_FUSE", 13 => "UNDER_TEMP", 14 => "MEMORY_LOSS", 15 => "HW_TEST_FAILURE"}}, {:event_bitfield_2, :bitfield32, 2, %{}}, {:vendor_event_bitfield_1, :bitfield32, 2, %{}}, {:vendor_event_bitfield_2, :bitfield32, 2, %{}}, {:vendor_event_bitfield_3, :bitfield32, 2, %{}}, {:vendor_event_bitfield_4, :bitfield32, 2, %{}}])
  end
), (
  @doc("#{""}")
  @spec(common(pid, integer) :: {:ok, map()} | {:error, term()})
  def(common(pid, slave_id \\\\ 1)) do
    ExModbus.Runtime.get_fields(pid, slave_id, 40004, 65, [{:manufacturer, :string, 16, %{}}, {:model, :string, 16, %{}}, {:options, :string, 8, %{}}, {:version, :string, 8, %{}}, {:serial_number, :string, 16, %{}}, {:device_address, :uint16, 1, %{}}])
  end
)]

Bear in mind that the real files were a lot longer. I shorted these to prevent exceeding the forum length restrictions.

I’ll try it OTP 20 in the morning and update w/ my results.

jeffdeville

jeffdeville OP

Results for:
erlang 20.0
elixir 1.4.5

No ‘inspect’ compile time: 117s
With ‘inspect’ compile time: 11.42s

This was a different, larger project, which is why the timing’s a little different. Clearly though the difference remains. @jose would you like to see the AST’s generated by this version, or is that likely to be the same as what I posted previously?

OvermindDL1

OvermindDL1

/me wonders if the original version is slow because it is doing compile-time protocol dispatch for the to_string call so the protocol dispatch is not optimized yet, but why is the inspect dispatch fast, is inspect already a more simple protocol like a single module in most cases?

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews