Sunday, June 21, 2015

On a component's behaviour

I have been busy implementing more of the commands that are part of the FBP Network protocol which is described here. If an FBP runtime speaks this protocol then one is able to use the neat development environment that has been developed by the NoFlo folks. You can run their interface locally, as a node.js program or you can use the online version: app.flowhub.io. I've been able to use the online version to create a small graph that I can then execute.

The current implementation of ElixirFBP has its components hard-wired into the runtime. This is, of course, temporary. I eventually want the runtime to be able to find and load components dynamically. NoFlo has the idea of a ComponentLoader for their node.js implementation:
"Node.js version of the Component Loader finds components and graphs by traversing the NPM dependency tree from a given root directory on the file system."
Any component-based framework, such as ElixirFBP, becomes generally useful if it is possible to construct components that can be "plugged" into the framework so long as the components obey rules as defined by an API. In other words, the component must behave properly. Elixir and Erlang have the concept of a behaviour, basically the specification of an api that a Elixir/Erlang module must implement. Let's see how this might possibly be useful.

A typical ElixirFBP component, Math.Add looks like the following:
defmodule Math.Add do
  def description, do: "Add two integers"
  def inports, do: [addend: :integer, augend: :integer]
  def outports, do: [sum: :integer]

  def loop(augend, addend, sum) do
    receive do
      {:augend, value} when addend != nil ->
        sum = ElixirFBP.Component.send_ip(sum, addend + value)
        loop(nil, nil, sum)
      {:augend, value} ->
        loop(value, addend, sum)
      {:addend, value} when augend != nil ->
        sum = ElixirFBP.Component.send_ip(sum, value + augend)
        loop(nil, nil, sum)
      {:addend, value} ->
        loop(augend, value, sum)
    end
  end
end

Notice the four functions definitions: description, inports, outports, and loop. With the exception of the description function, they must all be present for this component to operate correctly. In the process of building an FBP graph and connecting components, ElixirFBP, given the module name of a component, can retrieve the inports and outports of a component by executing the following code:
    component_inports = elem(Code.eval_string(component <> ".inports"), 0)
    component_outports = elem(Code.eval_string(component <> ".outports"), 0)
Eventually, this component is started by spawning a process:
process_pid = spawn(module, :loop, inport_args ++ outport_args)
These three functions. at least at this point in ElixirFBP's development, define the API for an ElixirFBP component or, in Elixir/Erlang terms, they describe the behaviour of the component. In Elixir, a behaviour is specified using the Behaviour macros. Here is what the ElxirFBP Component behaviour might look like:
defmodule Component do
  use Behaviour

  defcallback inports() :: [ElixirAtom: ElixirAtom]
  defcallback outports() :: [ElixirAtom: ElixirAtom]
end
A component must implement two functions, inports() and outports() that both will return a list of tuples. In Elixir, if the tuples are structured a certain way, the list can be treated as a Keyword list.

It was at this point that I realized that the way I have been defining the loop function of a component was not general enough. If you look at the Math.Add component above, you'll see that the loop function takes three arguments. I could define a loop behaviour callback that accepts three arguments but there are two problems: Which arguments are for the in ports and which are for the out ports? And, of course, a component can have any number of in and out ports. A solution is to generalize the loop function to accept two maps: one for the in ports and one for the out ports. Its behaviour could look like:
  defcallback loop(%{}, %{}) :: any
The component's loop function must have two arguments both of which are Elixir Maps. This change means that the way an ElixirFBP component is written needs to change. Here's what Math.Add looks like when rewritten to conform to the Component behaviour:
defmodule Math.Add do
  def description, do: "Add two integers"
  def inports, do: [addend: :integer, augend: :integer]
  def outports, do: [sum: :integer]

  def loop(inports, outports) do
    %{:augend => augend, :addend => addend} = inports
    %{:sum => sum} = outports
    receive do
      {:augend, value} when not is_nil(addend) ->
        sum = ElixiFBP.Component.send_ip(sum, addend + value)
        outports = %{outports | :sum => sum}
        inports = %{inports | :augend => nil, :addend => nil}
        loop(inports, outports)
      {:augend, value} ->
        inports = %{inports | :augend => value}
        loop(inports, outports)
      {:addend, value} when not is_nil(augend) ->
        sum = ElixiFBP.Component.send_ip(sum, value + augend)
        outports = %{outports | :sum => sum}
        inports = %{inports | :augend => nil, :addend => nil}
        loop(inports, outports)
      {:addend, value} ->
        inports = %{inports | :addend => value}
        loop(inports, outports)
    end
  end
end
I'll explain what's going on in this new version of Math.Add in the next post.

Monday, June 15, 2015

A couple of things...

A couple of things came to mind as I read the messages at the flow-based programming group and a discussion at Hacker News. I learned of the HN conversation via an interesting blog post by Samuel Lampa. Among other things, Samuel points out some of the problems associated with what is called "back pressure" in a flow- or stream-based system. This is where the receiver of information becomes overwhelmed and needs to tell the information source that "that's enough, for now" before running out of memory or some other kind of nasty thing occurs.

This got me to thinking about what I'm trying to accomplish with ElixirFBP. It is an experiment to see what a flow-based system written Elixir behaves like and how it can best be designed to offer the user some compelling reason to use such a system - other that it's written in some new language. I think the reason will be the overall speed of computation. This, despite some inherent speed issues with Erlang. Samuel describes how Elixir/Erlang could best be utilized as a "control plane". So, as the discussions above point out, there are plenty of issues to be faced and dealt with. That's a Good Thing! I hope this experiment will result in a base for investigating solutions to these issues.

Stay tuned as I continue the implementation of ElixirFBP support for NoFlo's network protocol. Having this goal of supporting the noflo-ui requires my having to provide a lot of support for the various intricacies of creating and running flow-based programs - something I'm not sure I would have been able to think of on my own.

Finally, it turns out that I can get my code to work with NoFlo's online version of their development environment so one doesn't have to install noflo-ui as I described in my previous post.

Sunday, June 14, 2015

Although still in its early stages, I have managed to get ElixirFBP talking with NoFlo's Development Environment. This browser-based tool allows one to build flow-based programs and execute them. Below is partial screen shot of the tool showing the graph that I built:



In the picture I have added two components; set the initial values for the Math.Add component; and connected its output to Core.Output's in port. When you press on the start button (in the upper right hand corner), the value of the addition is printed in the console window where ElixirFBP is running.

NoFlo's tool works with any server/runtime that supports the NoFlo Network Protocol. If you recall, I made the decision, early on, to model the underlying architecture of ElixirFBP along the lines of this protocol. So, it wasn't too difficult to make the tool work with ElixirFBP. I haven't implemented all of the protocol, only those commands that allow you to add and remove components, edges and initial values and start the graph.

ElixirFBP now runs as a network server that is listening on a websocket connection for these Network Protocol commands. I'm using the websocket facilities offered by the Cowboy Erlang module described here. I am also using the Elixir Poison library for encoding and decoding the JSON messages. To start ElixirFBP, be in its top level directory and enter iex -S mix at the command prompt. I use the Elixir Logger to display information as the server receives commands.

I'm still learning how to use NoFlo's tool, so the following description of how to set up NoFlo's tool may not be completely accurate. I am running a local version of the development tool rather than the one available online. This version, called the noflo-ui, is available on Github here. I followed the directions found at this page. You will need to have node.js installed.

Once noflo-ui is installed and started, you can open it up in a browser by going to http://localhost:3005/index.html. On the opening page you will see a section entitled Runtimes. You need to let noflo-ui know that the ElixirFBP runtime is available. Click the Register button and in the dialog that shows, click on the Add manually button. Enter elixir-fbp as the Runtime name and for the Type field, choose Custom. Click the Create button and Close the original dialog. The elixir-fbp runtime will appear as a black rectangle. Click the rectangle - you should start seeing activity from the ElixirFBP server. noflo-ui is asking for the list of components that the elixir-fbp has available - only two at this point. The browser will now display a drawing area upon which you can begin assembling your program.

To start creating a flow-based program, click on the area in the upper left hand corner - a noflo-ui-assigned graph name. A list of components should appear - again, only two. Clicking on a component will place it in the drawing area. From this point on, you would add other components, begin connecting out ports to in ports and establishing initial values. Right-clicking on a component provides other facilities. I would suggest that you read the documentation that is available for using NoFlo's Development Environment.

My Github repository has all the code needed for building and running a limited FBP graph using NoFlo's Development Environment. Be warned: things are probably a little fragile!

Wednesday, June 3, 2015

The current design and implementation of ElixirFBP does not take advantage of Elixir's ability to spawn many processes cheaply and quickly. There will undoubtedly be times where it would be desirable to have multiple processes supporting a single component. Right now, each component is supported by one Elixir process.

To give ElixirFBP this capability, I used the metadata part of the flow-based network protocol to specify the number of Elixir processes that you want running in support of a component. In code for creating a graph, this looks like:
    Graph.add_node(fbp_graph_reg_name, "copier", "Jsfbp.Copier",
                   %{:number_of_processes => 4})
In this example, when the FBP graph is started, four separate processes will be spawned for this component. Any component that sends an Information Packet (IP) to this component will automatically have this IP sent to one of the spawned processes, in a round-robin manner. Note that, with this method, there is no guarantee that the order of the IPs will be preserved as they move through the remainder of the flow. I will discuss techniques for ensuring order in subsequent posts.

We should expect to see some improvement in the execution speed of a graph that uses multiple processes for a component. Recall that the Elixir/Erlang virtual machine will take advantage of multiple cores in a processor automatically. Here's an experiment to see this in action: I've created an example program - Examples.Fbptestv1Multi - that uses a component to create IPs and sends them to a "faker" component which sends them on to a discarder component that simply ignores them. The faker component will sleep for a certain number of milliseconds to simulate processing. The example is written so that the faker component can be set to run in a specified number of processes (by default one).

The example program has a timing function that takes two arguments: the number of IPs to create and send and the number of processes to assign to the faker component. I ran the following tests on my four-core, AMD processor running at 3.60 Ghz processor (the time is in seconds and the faker process was set to sleep for 20 milliseconds):

No. of IPs No. of Processes Time
1,000 1 31.282
1,000 2 15.648
1,000 4 7.830
1,000 8 3.924
1,000 16 1.972
1,000 32 1.001

You can see that as the number of processes assigned to the faker component increases the overall speed of the graph execution goes down. All the code to run this example is in my github repository.  To run it, perform the following steps after downloading the ElixirFBP code:

  1. cd to the ElixirFBP directory.
  2. You may have to run mix deps.get in order to download and install certain libraries
  3. Run iex -S mix
  4. At the iex prompt enter: Examples.Fbptestv1Multi.time_it - this will, by default, run 1,000 IPs utilizing one processor for the faker component. The time will be displayed in microseconds - not milliseconds.
  5. Enter Examples.Fbptestv1Multi.time_it(1000, 4) to use 4 faker processes.
All we needed to do to utilize the inherent concurrency present in Elixir/Erlang systems was to specify the number of processes that we wanted a component to be run in. The changes that I had to make to ElixirFBP were to simply create a tuple of  the registered names of the spawned processes and maintain an index that points to the next process to use - to send an IP to. The only other change was to how the components are written. Now, all sends return an updated value for the target out port. So,
def loop(in_port, out) do
    receive do
      {:IN, value} ->
        # Do some processing
        out = Component.send_ip(out, value)
        loop(value, out)
    end
  end
became
def loop(in_port, out) do
    receive do
      {:IN, value} ->
        # Do some processing 
        out = Component.send_ip(out, value)
        loop(value, out)
    end
  end