diff --git a/.github/workflows/pytest-gpu.yml b/.github/workflows/pytest-gpu.yml new file mode 100644 index 0000000000..aee31354f0 --- /dev/null +++ b/.github/workflows/pytest-gpu.yml @@ -0,0 +1,54 @@ +# Adapted from https://github.com/weeyin83/vm-actions +name: Deploy VM on Azure and run GPU tests + +env: + OUTPUT_PATH: ${{ github.workspace }} + +on: [push] + +jobs: + + # Deploy VM in Azure + DeployVM: + runs-on: ubuntu-latest + + steps: + # checkout code from repo + - name: checkout repo + uses: actions/checkout@v1 + + - name: look for ps1 file + run: | + ls '${{ env.OUTPUT_PATH }}/PSA' + - name: deploy VM + env: + RESOURCE_GROUP: RhodriGpu + RESOURCE_GROUP_REGION: uksouth + SERVER_NAME: testgpu + run: > + pwsh -command "& '${{ env.OUTPUT_PATH }}\PSA\deployVM.ps1'" + -servicePrincipal ${{ secrets.SERVICE_PRINCIPAL_APPID }} + -servicePrincipalSecret ${{ secrets.SERVICE_PRINCIPAL_SECRET }} + -servicePrincipalTenantId ${{ secrets.SERVICE_PRINCIPAL_TENANTID }} + -azureSubscriptionName ${{ secrets.AZURE_SUBSCRIPTION_ID }} + -resourceGroupName $RESOURCE_GROUP + -resourceGroupNameRegion $RESOURCE_GROUP_REGION + -serverName $SERVER_NAME + -adminLogin ${{ secrets.ADMIN_LOGIN }} + -adminPassword ${{ secrets.ADMIN_PASSWORD }} + - name: set host + run: echo ::set-output name=action_host::$(az vm show -d -g RhodriGpu -n testgpu --query publicIps -o tsv) + id: host + - name: executing remote ssh commands test + uses: fifsky/ssh-action@master + with: + command: | + export PATH=~/programs/install/bin:$PATH + export LD_LIBRARY_PATH=~/programs/install/lib:$LD_LIBRARY_PATH + source ~/anaconda3/etc/profile.d/conda.sh + conda activate devito + cd ~/programs/devito + DEVITO_ARCH=clang DEVITO_PLATFORM=nvidiaX py.test -s -x --cov devito tests/test_gpu.py + host: ${{ steps.host.outputs.action_host }} + user: ${{ secrets.ADMIN_LOGIN }} + pass: ${{ secrets.ADMIN_PASSWORD }} diff --git a/PSA/deployVM.ps1 b/PSA/deployVM.ps1 new file mode 100644 index 0000000000..42a57c218c --- /dev/null +++ b/PSA/deployVM.ps1 @@ -0,0 +1,93 @@ +# Adapted from https://github.com/weeyin83/vm-actions +# Provision a VM within Azure (test) +# +[CmdletBinding()] +param( + [Parameter(Mandatory = $True)] + [string] + $servicePrincipal, + + [Parameter(Mandatory = $True)] + [string] + $servicePrincipalSecret, + + [Parameter(Mandatory = $True)] + [string] + $servicePrincipalTenantId, + + [Parameter(Mandatory = $True)] + [string] + $azureSubscriptionName, + + [Parameter(Mandatory = $True)] + [string] + $resourceGroupName, + + [Parameter(Mandatory = $True)] + [string] + $resourceGroupNameRegion, + + [Parameter(Mandatory = $True)] + [string] + $serverName, + + [Parameter(Mandatory = $True)] + [string] + $adminLogin, + + [Parameter(Mandatory = $True)] + [String] + $adminPassword +) + + +#region Login +# This logs into Azure with a Service Principal Account +# +Write-Output "Logging in to Azure with a service principal..." +az login ` + --service-principal ` + --username $servicePrincipal ` + --password $servicePrincipalSecret ` + --tenant $servicePrincipalTenantId +Write-Output "Done" +Write-Output "" +#endregion + +#region Subscription +#This sets the subscription the resources will be created in + +Write-Output "Setting default azure subscription..." +az account set ` + --subscription $azureSubscriptionName +Write-Output "Done" +Write-Output "" +#endregion + +#region Create Resource Group +# This creates the resource group used to house the VM +Write-Output "Creating resource group $resourceGroupName in region $resourceGroupNameRegion..." +az group create ` + --name $resourceGroupName ` + --location $resourceGroupNameRegion + Write-Output "Done creating resource group" + Write-Output "" + #endregion + +#region Create VM +# Create a VM in the resource group +Write-Output "Creating VM..." +try { + az vm create ` + --resource-group $resourceGroupName ` + --name $serverName ` + --image RhodriGpuVM-image-20200210110731 ` + --admin-username $adminLogin ` + --admin-password $adminPassword + } +catch { + Write-Output "VM already exists" + } +Write-Output "Done creating VM" +Write-Output "" +#endregion diff --git a/devito/archinfo.py b/devito/archinfo.py index f6ed15445f..85547dd6c3 100644 --- a/devito/archinfo.py +++ b/devito/archinfo.py @@ -275,7 +275,7 @@ def __init__(self, name, cores_logical=1, cores_physical=1, isa='cpp'): POWER9 = Power('power9') # Devices -NVIDIAX = Device('nvidiax') +NVIDIAX = Device('nvidiaX') platform_registry = { diff --git a/tests/test_dle.py b/tests/test_dle.py index 0933e91875..24d8f5bebe 100644 --- a/tests/test_dle.py +++ b/tests/test_dle.py @@ -7,7 +7,7 @@ from conftest import skipif from devito import (Grid, Function, TimeFunction, SparseTimeFunction, SubDimension, - Eq, Operator, switchconfig) + Eq, Operator) from devito.exceptions import InvalidArgument from devito.ir.iet import Call, Iteration, Conditional, FindNodes, retrieve_iteration_tree from devito.passes import BlockDimension, NThreads, NThreadsNonaffine @@ -508,51 +508,3 @@ def test_multiple_subnests(self): assert trees[1][2].pragmas[0].value == ('omp parallel for collapse(1) ' 'schedule(dynamic,1) ' 'num_threads(nthreads_nested)') - - -class TestOffloading(object): - - @switchconfig(platform='nvidiaX') - def test_basic(self): - grid = Grid(shape=(3, 3, 3)) - - u = TimeFunction(name='u', grid=grid) - - op = Operator(Eq(u.forward, u + 1), dle=('advanced', {'openmp': True})) - - trees = retrieve_iteration_tree(op) - assert len(trees) == 1 - - assert trees[0][1].pragmas[0].value ==\ - 'omp target teams distribute parallel for collapse(3)' - assert op.body[1].header[1].value ==\ - ('omp target enter data map(to: u[0:u_vec->size[0]]' - '[0:u_vec->size[1]][0:u_vec->size[2]][0:u_vec->size[3]])') - assert op.body[1].footer[0].value ==\ - ('omp target exit data map(from: u[0:u_vec->size[0]]' - '[0:u_vec->size[1]][0:u_vec->size[2]][0:u_vec->size[3]])') - - @switchconfig(platform='nvidiaX') - def test_multiple_eqns(self): - grid = Grid(shape=(3, 3, 3)) - - u = TimeFunction(name='u', grid=grid) - v = TimeFunction(name='v', grid=grid) - - op = Operator([Eq(u.forward, u + v + 1), Eq(v.forward, u + v + 4)], - dle=('advanced', {'openmp': True})) - - trees = retrieve_iteration_tree(op) - assert len(trees) == 1 - - assert trees[0][1].pragmas[0].value ==\ - 'omp target teams distribute parallel for collapse(3)' - for i, f in enumerate([u, v]): - assert op.body[2].header[2 + i].value ==\ - ('omp target enter data map(to: %(n)s[0:%(n)s_vec->size[0]]' - '[0:%(n)s_vec->size[1]][0:%(n)s_vec->size[2]][0:%(n)s_vec->size[3]])' % - {'n': f.name}) - assert op.body[2].footer[i].value ==\ - ('omp target exit data map(from: %(n)s[0:%(n)s_vec->size[0]]' - '[0:%(n)s_vec->size[1]][0:%(n)s_vec->size[2]][0:%(n)s_vec->size[3]])' % - {'n': f.name}) diff --git a/tests/test_gpu.py b/tests/test_gpu.py new file mode 100644 index 0000000000..ca736525e3 --- /dev/null +++ b/tests/test_gpu.py @@ -0,0 +1,118 @@ +import numpy as np +import pytest + +from conftest import skipif +from devito import Grid, TimeFunction, Eq, Operator, switchconfig +from devito.ir.iet import retrieve_iteration_tree + +pytestmark = skipif(['yask', 'ops']) + + +class TestOffloading(object): + + @switchconfig(platform='nvidiaX') + def test_basic(self): + grid = Grid(shape=(3, 3, 3)) + + u = TimeFunction(name='u', grid=grid) + + op = Operator(Eq(u.forward, u + 1), dle=('advanced', {'openmp': True})) + + trees = retrieve_iteration_tree(op) + assert len(trees) == 1 + + assert trees[0][1].pragmas[0].value ==\ + 'omp target teams distribute parallel for collapse(3)' + assert op.body[1].header[1].value ==\ + ('omp target enter data map(to: u[0:u_vec->size[0]]' + '[0:u_vec->size[1]][0:u_vec->size[2]][0:u_vec->size[3]])') + assert op.body[1].footer[0].value ==\ + ('omp target exit data map(from: u[0:u_vec->size[0]]' + '[0:u_vec->size[1]][0:u_vec->size[2]][0:u_vec->size[3]])') + + @switchconfig(platform='nvidiaX') + def test_multiple_eqns(self): + grid = Grid(shape=(3, 3, 3)) + + u = TimeFunction(name='u', grid=grid) + v = TimeFunction(name='v', grid=grid) + + op = Operator([Eq(u.forward, u + v + 1), Eq(v.forward, u + v + 4)], + dle=('advanced', {'openmp': True})) + + trees = retrieve_iteration_tree(op) + assert len(trees) == 1 + + assert trees[0][1].pragmas[0].value ==\ + 'omp target teams distribute parallel for collapse(3)' + for i, f in enumerate([u, v]): + assert op.body[2].header[2 + i].value ==\ + ('omp target enter data map(to: %(n)s[0:%(n)s_vec->size[0]]' + '[0:%(n)s_vec->size[1]][0:%(n)s_vec->size[2]][0:%(n)s_vec->size[3]])' % + {'n': f.name}) + assert op.body[2].footer[i].value ==\ + ('omp target exit data map(from: %(n)s[0:%(n)s_vec->size[0]]' + '[0:%(n)s_vec->size[1]][0:%(n)s_vec->size[2]][0:%(n)s_vec->size[3]])' % + {'n': f.name}) + + @switchconfig(platform='nvidiaX') + def test_op_apply(self): + grid = Grid(shape=(3, 3, 3)) + + u = TimeFunction(name='u', grid=grid, dtype=np.int32) + + op = Operator(Eq(u.forward, u + 1), dle=('advanced', {'openmp': True})) + + time_steps = 1000 + op.apply(time_M=time_steps) + + assert np.all(np.array(u.data[0, :, :, :]) == time_steps) + + @pytest.mark.xfail + @switchconfig(platform='nvidiaX') + def test_iso_ac(self): + from examples.seismic import TimeAxis, RickerSource, Receiver + from devito import Function, solve, norm + + shape = (101, 101) + extent = (1000, 1000) + origin = (0., 0.) + + v = np.empty(shape, dtype=np.float32) + v[:, :51] = 1.5 + v[:, 51:] = 2.5 + + grid = Grid(shape=shape, extent=extent, origin=origin) + + t0 = 0. + tn = 1000. + dt = 1.6 + time_range = TimeAxis(start=t0, stop=tn, step=dt) + + f0 = 0.010 + src = RickerSource(name='src', grid=grid, f0=f0, + npoint=1, time_range=time_range) + + domain_size = np.array(extent) + + src.coordinates.data[0, :] = domain_size*.5 + src.coordinates.data[0, -1] = 20. + + rec = Receiver(name='rec', grid=grid, npoint=101, time_range=time_range) + rec.coordinates.data[:, 0] = np.linspace(0, domain_size[0], num=101) + rec.coordinates.data[:, 1] = 20. + + u = TimeFunction(name="u", grid=grid, time_order=2, space_order=2) + m = Function(name='m', grid=grid) + m.data[:] = 1./(v*v) + + pde = m * u.dt2 - u.laplace + stencil = Eq(u.forward, solve(pde, u.forward)) + + src_term = src.inject(field=u.forward, expr=src * dt**2 / m) + rec_term = rec.interpolate(expr=u.forward) + + op = Operator([stencil] + src_term + rec_term, dle=('advanced', {'openmp': True})) + op(time=time_range.num-1, dt=dt) + + assert np.isclose(norm(rec), 490.5477, atol=1e-3, rtol=0)