Scientific Computing, TU Berlin, WS 2019/2020, Lecture 10
Jürgen Fuhrmann, WIAS Berlin
Left boundary condition: \begin{align*} -u'(0)+ \alpha u(0) &=0\\ C+\alpha D&=0\\ C&=-\alpha D \end{align*}
Right boundary condition:
u(x,alpha)=0.5*(-x*x +x + 1/alpha)
u_exact(N,alpha)=u.(collect(0:1/(N-1):1),alpha)
function setup(N,alpha)
h=1.0/(N-1)
a=[-1/h for i=1:N-1]
b=[2/h for i=1:N]
c=[-1/h for i=1:N-1]
b[1]=alpha+1/h
b[N]=alpha+1/h
f=[h for i=1:N]
f[1]=h/2
f[N]=h/2
return a,b,c,f
end
Correctness check
check(N,alpha,solver)=norm(solver(setup(N,alpha)...)-u_exact(N,alpha))
Setup tools
using LinearAlgebra
using SparseArrays
using BenchmarkTools
Progonka adapted from Daniel Kind, Alon Cohn
function progonka(u,a,b,c,f,Alpha,Beta)
@inbounds @fastmath begin
N = size(f,1)
Alpha[2] = -c[1]/b[1]
Beta[2] = f[1]/b[1]
for i in 2:N-1 #Forward Sweep
Alpha[i+1]=-c[i]/(a[i-1]*Alpha[i]+b[i])
Beta[i+1]=(f[i]-a[i-1]*Beta[i])/(a[i-1]*Alpha[i]+b[i])
end
u[N]=(f[N]-a[N-1]*Beta[N])/(a[N-1]*Alpha[N]+b[N])
for i in N-1:-1:1 #Backward Sweep
u[i]=Alpha[i+1]*u[i+1]+Beta[i+1]
end
end
end
Wrapper with allocations
function julia_progonka(a,b,c,f)
N = size(f,1)
u=Vector{eltype(a)}(undef,N)
Alpha=Vector{eltype(a)}(undef,N)
Beta=Vector{eltype(a)}(undef,N)
progonka(u,a,b,c,f,Alpha,Beta)
return u
end
Setup data
alpha=1
N=1000
a,b,c,f=setup(N,alpha)
Check correctness of solution
@show check(N,alpha,julia_progonka)
Benchmark
@btime julia_progonka(a,b,c,f);
Progonka in C
open("progonka.c", "w") do io
write(io, """
#include <time.h>
void progonka(int N,double* u,double* a,double* b,double* c,double* f,double* Alpha,double* Beta)
{
int i;
/* Adjust indexing:
This is C pointer arithmetic. Shifting the start addresses by 1
allows to keep the indexing from 1.
*/
u--;
a--;
b--;
c--;
f--;
Alpha--;
Beta--;
Alpha[2] = -c[1]/b[1];
Beta[2] = f[1]/b[1];
for(i=2;i<=N-1;i++)
{
Alpha[i+1]=-c[i]/(a[i-1]*Alpha[i]+b[i]);
Beta[i+1]=(f[i]-a[i-1]*Beta[i])/(a[i-1]*Alpha[i]+b[i]);
}
u[N]=(f[N]-a[N-1]*Beta[N])/(a[N-1]*Alpha[N]+b[N]);
for(i=N-1;i>=1;i--)
{
u[i]=Alpha[i+1]*u[i+1]+Beta[i+1];
}
}
double tmem; /* time memory variable */
void tstart(void) /* Start time measurement */
{
tmem=(double)clock()/(double)CLOCKS_PER_SEC;
}
void tstop(void) /* Stop time measurement */
{
tmem=(double)clock()/(double)CLOCKS_PER_SEC-tmem;
}
double tget(void) /* Return value of timer */
{
return tmem;
}
/* Measure time in C. Call one million times. */
void c_progonka_with_timing(int N,double* u,double* a,double* b,double* c,double* f,double* Alpha,double* Beta)
{
int itime;
int ntime;
ntime=1000000;
tstart();
for(itime=0;itime<ntime;itime++)
{
progonka(N,u,a,b,c,f,Alpha,Beta);
}
tstop();
}
""")
end
run(`clang -fPIC -Ofast -march=native --shared progonka.c -o progonka.so`)
tstart()=ccall( (:tstart,"progonka"),Cvoid,())
tstop()=ccall( (:tstop,"progonka"),Cvoid,())
tget()=ccall( (:tget,"progonka"),Cdouble,())
function c_progonka(a,b,c,f)
u=Vector{eltype(a)}(undef,N)
Alpha=Vector{eltype(a)}(undef,N)
Beta=Vector{eltype(a)}(undef,N)
ccall( (:progonka,"progonka"), Cvoid,(Cint,Ptr{Cdouble},Ptr{Cdouble},Ptr{Cdouble},Ptr{Cdouble},Ptr{Cdouble},Ptr{Cdouble},Ptr{Cdouble},),
N,u,a,b,c,f,Alpha,Beta)
return u
end
@show check(N,alpha,c_progonka)
@btime c_progonka(a,b,c,f);
function julia_progonka_with_timing(a,b,c,f)
N = size(f,1)
u=Vector{eltype(a)}(undef,N)
Alpha=Vector{eltype(a)}(undef,N)
Beta=Vector{eltype(a)}(undef,N)
tstart()
for itime=1:1000000
progonka(u,a,b,c,f,Alpha,Beta)
end
tstop()
return u
end
julia_progonka_with_timing(a,b,c,f)
print("time per call: $(tget())μs")
function c_progonka_with_timing(a,b,c,f)
u=Vector{eltype(a)}(undef,N)
Alpha=Vector{eltype(a)}(undef,N)
Beta=Vector{eltype(a)}(undef,N)
ccall( (:c_progonka_with_timing,"progonka"), Cvoid,(Cint,Ptr{Cdouble},Ptr{Cdouble},Ptr{Cdouble},Ptr{Cdouble},Ptr{Cdouble},Ptr{Cdouble},Ptr{Cdouble},),
N,u,a,b,c,f,Alpha,Beta)
end
c_progonka_with_timing(a,b,c,f)
print("time per call: $(tget()) μs")
function c_progonka_with_timing_from_julia(a,b,c,f)
u=Vector{eltype(a)}(undef,N)
Alpha=Vector{eltype(a)}(undef,N)
Beta=Vector{eltype(a)}(undef,N)
tstart()
for itime=1:1000000
ccall( (:progonka,:progonka), Cvoid,(Cint,Ptr{Cdouble},Ptr{Cdouble},Ptr{Cdouble},Ptr{Cdouble},Ptr{Cdouble},Ptr{Cdouble},Ptr{Cdouble},),
N,u,a,b,c,f,Alpha,Beta)
end
tstop()
end
c_progonka_with_timing_from_julia(a,b,c,f)
print("time per call: $(tget()) μs")
This notebook was generated using Literate.jl.