Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[query] Add tree_matmul, matrix multiply in case of large inner dimension #9063

Merged

Conversation

johnc1231
Copy link
Contributor

@johnc1231 johnc1231 commented Jul 8, 2020

This PR introduces tree_matmul, a method on BlockMatrix that allows for greater parallelism when multiplying two large matrices that result in a small matrix (i.e. the inner dimension is much larger than the outer dimensions).

In order to do this, this PR takes a split_on_inner parameter. This parameter defines how many subdivisions to break the two matrices into. Corresponding subdivisions are multiplied, written to disk, then read back in and summed.

For example, if you are multiplying a 4k by 500k matrix by its transpose, your answer will be a 4k by 4k block. With normal matrix multiply, you'd get no parallelism at all, since the result is only a single partition. If you instead use tree_matmul with split_on_inner = 5, hail will do the following:

  1. Break up 4k by 500k matrix into five 4k by 100k matrices.
  2. Break up 500k by 4k matrix into five 100k by 4k matrices.
  3. Multiply corresponding matrices together, creating five 4k by 4k matrices.
  4. Write them all to disk, then read them back in.
  5. Sum the 5 read in matrices together to get your result.

This PR also introduces a write_block_matrices function that writes out a list of BlockMatrix in parallel. This was necessary to write out all of the intermediate matrices in tree_matmul.

@konradjk
@danking

danking
danking previously requested changes Jul 9, 2020
Copy link
Collaborator

@danking danking left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is absolutely awesome John! Great work

self._assert_eq(m.T.tree_matmul(m, 2, new_temp_file()), nm.T @ nm)
self._assert_eq(m.T.tree_matmul(nm, 2, new_temp_file()), nm.T @ nm)
self._assert_eq(row.T.tree_matmul(row, 2, new_temp_file()), nrow.T @ nrow)
self._assert_eq(row.T.tree_matmul(nrow, 2, new_temp_file()), nrow.T @ nrow)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call me paranoid, but it would be nice to have at least one example of x @ y where x and y are both block matrices and neither is the transpose of the other.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh, I just copied the current matmul tests plus that looping of different block sizes, but you're right, these are only transposes. I'll add another test.

header: Option[String],
addIndex: Boolean,
compression: Option[String],
customFilenames: Option[Array[String]]): Unit = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is an odd formatting change? Maybe add a newline before the ) to get the desired formatting from IntelliJ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this was an accident.

@@ -1427,6 +1459,47 @@ def __matmul__(self, b):

return BlockMatrix(BlockMatrixDot(self._bmir, b._bmir))

@typecheck_method(b=oneof(np.ndarray, block_matrix_type), split_on_inner=int, path_prefix=str)
def tree_matmul(self, b, split_on_inner, path_prefix):
"""Matrix multiplication in situations with large inner dimension. This function splits a single matrix
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally prefer a one sentence description on its own line, followed by a full description. Some python tools use this in tooltips

@@ -1427,6 +1459,47 @@ def __matmul__(self, b):

return BlockMatrix(BlockMatrixDot(self._bmir, b._bmir))

@typecheck_method(b=oneof(np.ndarray, block_matrix_type), split_on_inner=int, path_prefix=str)
def tree_matmul(self, b, split_on_inner, path_prefix):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think adding a default argument for path_prefix of None and creating a temp file in that case will improve usability.

@@ -1427,6 +1459,47 @@ def __matmul__(self, b):

return BlockMatrix(BlockMatrixDot(self._bmir, b._bmir))

@typecheck_method(b=oneof(np.ndarray, block_matrix_type), split_on_inner=int, path_prefix=str)
def tree_matmul(self, b, split_on_inner, path_prefix):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should make either the last or the last two parameters keyword-only arguments, so: (self, b, *, split_on_inner, path_prefix).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be willing to make path_prefix a keyword only argument, but can you explain why you'd prefer that? I think split_on_inner I'll leave the way it is, since you always have to specify it. path_prefix with a default value of None as your above comment suggests makes the keyword only argument sound better.

@johnc1231
Copy link
Contributor Author

I addressed most of your comments, I'm not sure why you want the keyword only arguments though.

@danking
Copy link
Collaborator

danking commented Jul 9, 2020

It's kind of a gut reaction for me. There's some conversation at PEP 1302's rationale. For me, everything is about backwards compatibility and the two cases are:

  • changing from positional arguments without varargs to varargs and keywords is backwards-incompatible.
  • changing a positional-without-default to positional-with-default values is backwards-incompatible unless the positional argument is the last one.

In this concrete example, I worry about two possible evolutions of this function.

  1. Accept multiple other matrices:
def tree_matmul(self, *others, split_on_inner, path_prefix)
  1. Allow the compiler/python code to choose a good value for split_on_inner
def tree_matmul(self, b, split_on_inner=None, path_prefix=None) # now path prefix *must* be optional as well.

@danking
Copy link
Collaborator

danking commented Jul 9, 2020

I might also call split_on_inner splits for brevity in the keyword case.

@patrick-schultz
Copy link
Collaborator

@danking Should I review this too, or do you feel like you covered it?

@danking
Copy link
Collaborator

danking commented Jul 20, 2020

@patrick-schultz I only looked at the Python interface, so I would much appreciate your thoughts on the Scala stuff.

@johnc1231 johnc1231 added the WIP label Jul 21, 2020
@johnc1231
Copy link
Contributor Author

Tagging WIP because I have two tiny changes Dan asked for to make (argument name and keyword only)

@johnc1231 johnc1231 removed the WIP label Jul 22, 2020
@danking danking merged commit 6360d5b into hail-is:master Jul 22, 2020
Dania-Abuhijleh pushed a commit to Dania-Abuhijleh/hail that referenced this pull request Jul 23, 2020
…sion (hail-is#9063)

* Set up some range computations

* Did most of the python work, stuck now beacuse I need to be able to write many RDDs in parallel

* Kept Dan's coschedule actions thing somewhere I won't lose it

* Fixed bug in copy of BlockMatrixMultiWrite

* Python side BlockMatrixMultiWriter exists

* Python to scala connection for block matrix native writer is working

* Metadata and success files being written, just need to write partition files

* Organized correctly now

* Write multiple works

* Split multiplying works

* Fixed last_rows and last_cols computation

* Added a print

* Now it's tree_matmul

* Began documenting the new functions

* Delete coscheduleActions comment

* Delete old comments

* Python half of supporting stage_locally

* Deleted unusued partitionURI

* WIP

* Updated to use using fs.create

* Some typechecks fixed

* Added tests for tree_matmul

* Refactored, passing tests

* Refactored to pass along ExecuteContext to get localTmpDir

* Deleted print

* pylint fixes

* Restored tempfile import

* Fixed indentation

* Removed more accidental formatting changes

* Added default path

* Update the tests

* Rename split_on_inner to splits

* Keyword only arguments

* Fix typecheck on splits
annamiraotoole pushed a commit to annamiraotoole/hail that referenced this pull request Aug 3, 2020
…sion (hail-is#9063)

* Set up some range computations

* Did most of the python work, stuck now beacuse I need to be able to write many RDDs in parallel

* Kept Dan's coschedule actions thing somewhere I won't lose it

* Fixed bug in copy of BlockMatrixMultiWrite

* Python side BlockMatrixMultiWriter exists

* Python to scala connection for block matrix native writer is working

* Metadata and success files being written, just need to write partition files

* Organized correctly now

* Write multiple works

* Split multiplying works

* Fixed last_rows and last_cols computation

* Added a print

* Now it's tree_matmul

* Began documenting the new functions

* Delete coscheduleActions comment

* Delete old comments

* Python half of supporting stage_locally

* Deleted unusued partitionURI

* WIP

* Updated to use using fs.create

* Some typechecks fixed

* Added tests for tree_matmul

* Refactored, passing tests

* Refactored to pass along ExecuteContext to get localTmpDir

* Deleted print

* pylint fixes

* Restored tempfile import

* Fixed indentation

* Removed more accidental formatting changes

* Added default path

* Update the tests

* Rename split_on_inner to splits

* Keyword only arguments

* Fix typecheck on splits
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants