{"action":"created","issue":{"url":"https://api.github.com/repos/ruby-numo/numo-narray/issues/84","repository_url":"https://api.github.com/repos/ruby-numo/numo-narray","labels_url":"https://api.github.com/repos/ruby-numo/numo-narray/issues/84/labels{/name}","comments_url":"https://api.github.com/repos/ruby-numo/numo-narray/issues/84/comments","events_url":"https://api.github.com/repos/ruby-numo/numo-narray/issues/84/events","html_url":"https://github.com/ruby-numo/numo-narray/issues/84","id":307461988,"number":84,"title":"Optimize marshaling in parallel computation by allowing constructing Numo arrays from pre-allocated data pointers, such as coming from views.","user":{"login":"giuse","id":5106245,"avatar_url":"https://avatars3.githubusercontent.com/u/5106245?v=4","gravatar_id":"","url":"https://api.github.com/users/giuse","html_url":"https://github.com/giuse","followers_url":"https://api.github.com/users/giuse/followers","following_url":"https://api.github.com/users/giuse/following{/other_user}","gists_url":"https://api.github.com/users/giuse/gists{/gist_id}","starred_url":"https://api.github.com/users/giuse/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/giuse/subscriptions","organizations_url":"https://api.github.com/users/giuse/orgs","repos_url":"https://api.github.com/users/giuse/repos","events_url":"https://api.github.com/users/giuse/events{/privacy}","received_events_url":"https://api.github.com/users/giuse/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-03-22T00:14:00Z","updated_at":"2018-03-22T01:57:39Z","closed_at":null,"author_association":"NONE","body":"In modern machine learning, parallel computation is a necessity. In Ruby this currently implies using methods based on `#fork` rather than `#thread`, since the goal is distributing the computation on multiple cores and it is thus needed to avoid the GIL.\r\n\r\nA typical example is [the gem `parallel`](https://github.com/grosser/parallel): it launches a number of workers, send chunks of input to compute to each in turn as they are found free, then collects the return values from each. By \"collects\" I mean that the data must be passed from the worker to the original process: only the latter will continue the computation, while the workers will terminate after job completion.\r\n\r\nThis passage of data is traditionally done by marshaling. In standard marshaling (as currently implemented by Numo) the data of the matrix is passed in some string form (usually binary). This method is designed for data dumping and network communication; it is highly inefficient if the workers and the original process both reside on the same machine.\r\n\r\n**What would make a breakthrough for machine learning performance in Numo would be a marshaling technique that allows it to retrieve the result of other processes' computations by passing C data pointers.**\r\n\r\nThe major challenge here is that at child termination its allocated memory is freed. In order for this special marshaling technique to be successful, the children must receive a pointer to a memory area allocated by the parent, then build a Numo array out of it, allowing it to store the results transparently. \r\n\r\n**Example**: 2 children processes are used to compute a 3x3 matrix each: \r\n\r\n1. The parent allocates a Numo array with shape [2,3,3]\r\n2. Each child is passed the pointer to one of the elements of such array, i.e. a reference to the memory pointed by the views `[0,true,true]` and `[1,true,true]`\r\n3. The child builds a Numo array with shape `[3,3]` using the memory pointers coming from the parent\r\n4. The child writes data on its array, transparently, and terminates: no backwards marshaling is required\r\n5. The parent waits for all children to terminate, then reads their results on its original 3x2x2 array\r\n\r\nThe process might even be wrapped in a `Numo::Parallel` module for extra convenience (the whole gem `parallel` is itself [a few hundred lines](https://github.com/grosser/parallel/blob/master/lib/parallel.rb) of code)."},"comment":{"url":"https://api.github.com/repos/ruby-numo/numo-narray/issues/comments/375153285","html_url":"https://github.com/ruby-numo/numo-narray/issues/84#issuecomment-375153285","issue_url":"https://api.github.com/repos/ruby-numo/numo-narray/issues/84","id":375153285,"user":{"login":"giuse","id":5106245,"avatar_url":"https://avatars3.githubusercontent.com/u/5106245?v=4","gravatar_id":"","url":"https://api.github.com/users/giuse","html_url":"https://github.com/giuse","followers_url":"https://api.github.com/users/giuse/followers","following_url":"https://api.github.com/users/giuse/following{/other_user}","gists_url":"https://api.github.com/users/giuse/gists{/gist_id}","starred_url":"https://api.github.com/users/giuse/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/giuse/subscriptions","organizations_url":"https://api.github.com/users/giuse/orgs","repos_url":"https://api.github.com/users/giuse/repos","events_url":"https://api.github.com/users/giuse/events{/privacy}","received_events_url":"https://api.github.com/users/giuse/received_events","type":"User","site_admin":false},"created_at":"2018-03-22T01:57:39Z","updated_at":"2018-03-22T01:57:39Z","author_association":"NONE","body":"You can see it is not as easy as defining the returning data structure as a closure and send it to all children (you need to `$ gem install parallel`:\r\n\r\n```ruby\r\nrequire 'parallel'\r\nrequire 'numo/narray'\r\nary = Numo::Int32.zeros(5)\r\nParallel.map(0...ary.size) { |i| puts i; ary[i] = i } # => asynchronously prints numbers between 0 and 4\r\np ary # => Numo::Int32#shape=[5] [0, 0, 0, 0, 0]\r\n```\r\n\r\nThe stack of the parent is entirely duplicated in each child. Each child has (and accesses) its own distinct copy of `ary`. A way is needed for a child to point back into the parent's space for this optimization to work."}}
{"actor":{"display_login":"giuse"}}