ut-parla / Parla.py

A Python based programming system for heterogeneous computing
Other
21 stars 9 forks source link

Variable capture does not workout without encapsulation #148

Open mebenstein opened 1 year ago

mebenstein commented 1 year ago

In the following code, variables are not captured if I write code directly inside the Parla context. Not sure if this is intended behavior, if so it is not documented anywhere and certainly should be mentioned in the first tutorial.

def fun():
    fun = TaskSpace('fun')
    for i in range(4):
        @spawn(fun[i])
        def print_fun():
            print(f'Fun {i}', flush=True)

if __name__ == "__main__":
    print("start")
    with Parla():
        no_fun = TaskSpace('no_fun')
        for i in range(4):
            @spawn(no_fun[i])
            def print_no_fun():
                print(f'No fun {i}', flush=True)

    with Parla():
        fun()

This program outputs:

No fun 3
No fun 3
No fun 3
No fun 3
Fun 0
Fun 2
Fun 1
Fun 3

Using nonlocal in the first part also leads to a syntax error.

nicelhc13 commented 1 year ago

Thank you for catching this! The current Parla captures variables within a function. We will discuss and fix this semantic (or let you know) soon. For now, please use "Fun" way

wlruys commented 1 year ago

Duplicate of https://github.com/ut-parla/Parla.py/issues/67 ?

wlruys commented 1 year ago

It is mentioned indirectly in the first tutorial:

"Notice that we do not directly create Parla tasks in the global scope. Instead, we define a main function and create our tasks there. To ensure correct capture of local variables, Parla tasks should not be defined in the global scope."

But I agree we could be more clear about this.

wlruys commented 1 year ago

At the moment there is no easy way to fix this.

To fix the capture semantics we would have to copy the specific variables needed from the globals array. This would both need a hack of the Python byte code (currently outside of my skillset) and/or possibly linear costs in the size of globals.

Personally, I think Parla API should probably eventually be changed to make tasks functions instead of code block capture to avoid these incompatible semantics between local and global variables.

But open to any ideas of other ways to work around it.

mebenstein commented 1 year ago

Thanks for the clarification. I agree, the syntax is confusing. Instead, it should be a function call or a function annotation to force the user to encapsulate the code

Parla(fun)

# or 

@Parla
def fun():
nicelhc13 commented 1 year ago

I agree with that this semantic is confusing. But this was a design choice and has pros and cons. The main pros that we have argued about "wrapping codes to parallelize" instead of "functions" was that we could provide easy gradual adoption to sequential codes. So users think and design task parallel programs in higher level abstraction. The cons of this argument is what you guys described. For now, the Python runtime version sticks with this semantic. In the future, we will raise this issue and discuss the semantic. But this would take time. Thanks!