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

r.texture: support parallel computing by OpenMP #3857

Merged
merged 27 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0815f85
refactor to use local vars instead of global vars
cyliang368 Jun 7, 2024
3c7e89b
refactor to seperate execute part for parallelization
cyliang368 Jun 7, 2024
9b54ad3
remove unused variable
cyliang368 Jun 8, 2024
d883baf
divide h_mearsure.h into paired header files
cyliang368 Jun 9, 2024
7b6147e
refactor parameters and flags in main.c
cyliang368 Jun 9, 2024
9f1f379
remove unused variable 'threads' in main.c
cyliang368 Jun 9, 2024
99678d9
add execute_parallel.c
cyliang368 Jun 14, 2024
37f03b5
parallelize execute, add tests and benchmarks
cyliang368 Jun 15, 2024
ee5e0ce
Merge remote-tracking branch 'upstream/main' into parallel_rtexture2
cyliang368 Jun 15, 2024
5dd1b1e
rename test and benchmark files, integrate parallel part in execute.c
cyliang368 Jun 15, 2024
47cd271
Run benchmark on all methods
cyliang368 Jun 15, 2024
c66b368
Merge branch 'OSGeo:main' into parallel_rtexture2
cyliang368 Jun 15, 2024
8a1c868
Merge branch 'OSGeo:main' into parallel_rtexture2
cyliang368 Jun 16, 2024
5a1ab21
get thread id only if OpenMP is defined
cyliang368 Jun 16, 2024
46cc8b3
replace omp firstprivate with omp private
cyliang368 Jun 16, 2024
bc849bd
remove tid, and only use trow to aviod no OpenMP support situation
cyliang368 Jun 16, 2024
b56722e
Merge branch 'main' into parallel_rtexture2
cyliang368 Jun 16, 2024
0f3d0c8
add keyword "parallel" in main.c
cyliang368 Jun 17, 2024
3fecd5f
Merge branch 'main' into parallel_rtexture2
cyliang368 Jun 17, 2024
09a34ad
replace the list of functions with the -a flag in benchmark
cyliang368 Jun 18, 2024
111d71f
add benchmark for window sizes, plot speedup and efficiency
cyliang368 Jun 18, 2024
8cd45a3
remove basename list in benchmark, add benchmark results to html
cyliang368 Jun 18, 2024
97695db
replace 'x' with '&#215' for window sizes in html
cyliang368 Jun 18, 2024
5fd03cd
breakdown long lines in html
cyliang368 Jun 18, 2024
765c869
remove .DS_Store file
cyliang368 Jun 18, 2024
58f0a51
remove known issue in r.texture.html
cyliang368 Jun 19, 2024
8b328d9
modify the formats and paths of figures
cyliang368 Jun 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion raster/r.texture/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ MODULE_TOPDIR = ../..

PGM = r.texture

LIBES = $(RASTERLIB) $(GISLIB) $(MATHLIB)
LIBES = $(RASTERLIB) $(GISLIB) $(MATHLIB) $(OPENMP_LIBPATH) $(OPENMP_LIB)
DEPENDENCIES = $(RASTERDEP) $(GISDEP)
EXTRA_CFLAGS = $(OPENMP_CFLAGS)
EXTRA_INC = $(OPENMP_INCPATH)

include $(MODULE_TOPDIR)/include/Make/Module.make

Expand Down
60 changes: 60 additions & 0 deletions raster/r.texture/benchmark/benchmark_rtexture.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""Benchmarking of r.texture

@author Aaron Saw Min Sern
Chung-Yuan Liang
"""

from grass.exceptions import CalledModuleError
from grass.pygrass.modules import Module
from subprocess import DEVNULL

import grass.benchmark as bm


def main():
results = []
mapsizes = [1e6, 2e6, 4e6, 8e6]
metrics = ["time", "speedup", "efficiency"]

for mapsize in mapsizes:
benchmark(int(mapsize**0.5), f"r.texture_{int(mapsize/1e6)}M", results)

for metric in metrics:
bm.nprocs_plot(
results,
title=f"r.texture -a {metric}",
metric=metric,
)


def benchmark(size, label, results):
reference = "r_texture_reference_map"
output = "benchmark_r_texture"
generate_map(rows=size, cols=size, fname=reference)
module = Module(
"r.texture",
input=reference,
output=output,
a=True,
run_=False,
stdout_=DEVNULL,
overwrite=True,
)
results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=8, repeat=3))
Module("g.remove", quiet=True, flags="f", type="raster", name=reference)
Module("g.remove", quiet=True, flags="f", type="raster", pattern=f"{output}_*")


def generate_map(rows, cols, fname):
Module("g.region", flags="p", rows=rows, cols=cols, res=1)
# Generate using r.random.surface if r.surf.fractal fails
try:
print("Generating reference map using r.surf.fractal...")
Module("r.surf.fractal", output=fname, overwrite=True)
except CalledModuleError:
print("r.surf.fractal fails, using r.random.surface instead...")
Module("r.random.surface", output=fname, overwrite=True)


if __name__ == "__main__":
main()
62 changes: 62 additions & 0 deletions raster/r.texture/benchmark/benchmark_rtexture_window.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Benchmarking of r.texture

@author Aaron Saw Min Sern
Chung-Yuan Liang
"""

from grass.exceptions import CalledModuleError
from grass.pygrass.modules import Module
from subprocess import DEVNULL

import grass.benchmark as bm


def main():
results = []
mapsize = 1e6
window_sizes = [3, 9, 15, 27]
metrics = ["time", "speedup", "efficiency"]

for window in window_sizes:
benchmark(int(mapsize**0.5), window, f"r.texture_{window}x{window}", results)

for metric in metrics:
bm.nprocs_plot(
results,
title=f"r.texture -a {metric}",
metric=metric,
)


def benchmark(size, window, label, results):
reference = "r_texture_reference_map"
output = "benchmark_r_texture"
generate_map(rows=size, cols=size, fname=reference)
module = Module(
"r.texture",
input=reference,
output=output,
size=window,
a=True,
run_=False,
stdout_=DEVNULL,
overwrite=True,
)
results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=8, repeat=3))
Module("g.remove", quiet=True, flags="f", type="raster", name=reference)
Module("g.remove", quiet=True, flags="f", type="raster", pattern=f"{output}_*")


def generate_map(rows, cols, fname):
Module("g.region", flags="p", rows=rows, cols=cols, res=1)
# Generate using r.random.surface if r.surf.fractal fails
try:
print("Generating reference map using r.surf.fractal...")
Module("r.surf.fractal", output=fname, overwrite=True)
except CalledModuleError:
print("r.surf.fractal fails, using r.random.surface instead...")
Module("r.random.surface", output=fname, overwrite=True)


if __name__ == "__main__":
main()
146 changes: 89 additions & 57 deletions raster/r.texture/execute.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
#if defined(_OPENMP)
#include <omp.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Expand All @@ -17,8 +21,7 @@
***************************************************************************************************/
int execute_texture(CELL **data, struct dimensions *dim,
struct menu *measure_menu, int *measure_idx,
struct output_setting *out_set)

struct output_setting *out_set, int threads)
{
int size = dim->size;
int dist = dim->dist;
Expand All @@ -27,23 +30,34 @@ int execute_texture(CELL **data, struct dimensions *dim,
int n_outputs = dim->n_outputs;
int n_measures = dim->n_measures;
int *outfd = out_set->outfd;

RASTER_MAP_TYPE out_data_type = out_set->out_data_type;
struct Flag *flag_null = out_set->flag_null;
struct Flag *flag_ind = out_set->flag_ind;

int offset = size / 2;
int i, j, row, col, first_row, first_col, last_row, last_col;
int trow;
int have_px, have_py, have_pxpys, have_pxpyd;
FCELL **fbuf;
FCELL measure; /* Containing measure done */
struct matvec *mv;

fbuf = G_malloc(n_outputs * sizeof(FCELL *));
for (i = 0; i < n_outputs; i++)
fbuf[i] = Rast_allocate_buf(out_data_type);
FCELL ***fbuf_threads; /* Buffer for each thread */
FCELL measure; /* Containing measure done */
struct matvec **mvs; /* matrices and vectors for each thread */

/* allocate memory*/
/* fbuf_threads[0] is used for writing out when program is not in loops*/
fbuf_threads = (FCELL ***)G_malloc(sizeof(FCELL **) * threads);
for (i = 0; i < threads; i++) {
fbuf_threads[i] = (FCELL **)G_malloc(n_outputs * sizeof(FCELL *));
for (j = 0; j < n_outputs; j++)
fbuf_threads[i][j] = Rast_allocate_buf(out_data_type);
}

mv = G_malloc(sizeof(struct matvec));
alloc_vars(size, mv);
mvs = (struct matvec **)G_malloc(sizeof(struct matvec *) * threads);
for (i = 0; i < threads; i++) {
mvs[i] = G_malloc(sizeof(struct matvec));
alloc_vars(size, mvs[i]);
}

/* variables needed */
if (measure_menu[2].useme || measure_menu[11].useme ||
Expand Down Expand Up @@ -76,11 +90,11 @@ int execute_texture(CELL **data, struct dimensions *dim,
last_col = ncols;
}

Rast_set_f_null_value(fbuf[0], ncols);
Rast_set_f_null_value(fbuf_threads[0][0], ncols);

for (row = 0; row < first_row; row++) {
for (i = 0; i < n_outputs; i++) {
Rast_put_row(outfd[i], fbuf[0], out_data_type);
Rast_put_row(outfd[i], fbuf_threads[0][0], out_data_type);
}
}
if (n_measures > 1)
Expand All @@ -90,62 +104,80 @@ int execute_texture(CELL **data, struct dimensions *dim,
else
G_message(_("Calculating %s..."), measure_menu[measure_idx[0]].desc);

for (row = first_row; row < last_row; row++) {
G_percent(row, nrows, 2);
for (i = 0; i < n_outputs; i++)
Rast_set_f_null_value(fbuf[i], ncols);

/*process the data */
for (col = first_col; col < last_col; col++) {
if (!set_vars(mv, data, row, col, size, offset, dist,
flag_null->answer)) {
for (i = 0; i < n_outputs; i++)
Rast_set_f_null_value(&(fbuf[i][col]), 1);
continue;
}
/* for all angles (0, 45, 90, 135) */
for (i = 0; i < 4; i++) {
set_angle_vars(mv, i, have_px, have_py, have_pxpys, have_pxpyd);
/* for all requested textural measures */
for (j = 0; j < n_measures; j++) {

measure =
(FCELL)h_measure(measure_menu[measure_idx[j]].idx, mv);

if (flag_ind->answer) {
/* output for each angle separately */
fbuf[j * 4 + i][col] = measure;
}
else {
/* use average over all angles for each measure */
if (i == 0)
fbuf[j][col] = measure;
else if (i < 3)
fbuf[j][col] += measure;
else
fbuf[j][col] = (fbuf[j][col] + measure) / 4.0;
#pragma omp parallel private(row, col, i, j, measure, trow) default(shared)
{
#pragma omp for schedule(static, 1) ordered
for (row = first_row; row < last_row; row++) {
trow = row % threads; /* Obtain thread row id */
G_percent(row, nrows, 2);

/* initialize the output row */
for (i = 0; i < n_outputs; i++)
Rast_set_f_null_value(fbuf_threads[trow][i], ncols);

/*process the data */
for (col = first_col; col < last_col; col++) {
if (!set_vars(mvs[trow], data, row, col, size, offset, dist,
flag_null->answer)) {
for (i = 0; i < n_outputs; i++)
Rast_set_f_null_value(&(fbuf_threads[trow][i][col]), 1);
continue;
}
/* for all angles (0, 45, 90, 135) */
for (i = 0; i < 4; i++) {
set_angle_vars(mvs[trow], i, have_px, have_py, have_pxpys,
have_pxpyd);
/* for all requested textural measures */
for (j = 0; j < n_measures; j++) {
measure = (FCELL)h_measure(
measure_menu[measure_idx[j]].idx, mvs[trow]);
if (flag_ind->answer) {
/* output for each angle separately */
fbuf_threads[trow][j * 4 + i][col] = measure;
}
else {
/* use average over all angles for each measure */
if (i == 0)
fbuf_threads[trow][j][col] = measure;
else if (i < 3)
fbuf_threads[trow][j][col] += measure;
else
fbuf_threads[trow][j][col] =
(fbuf_threads[trow][j][col] + measure) /
4.0;
}
}
}
}
#pragma omp ordered
{
for (i = 0; i < n_outputs; i++)
Rast_put_row(outfd[i], fbuf_threads[trow][i],
out_data_type);
}
}
} /* end of parallel section */

for (i = 0; i < n_outputs; i++)
Rast_put_row(outfd[i], fbuf[i], out_data_type);
}

Rast_set_f_null_value(fbuf[0], ncols);
Rast_set_f_null_value(fbuf_threads[0][0], ncols);
for (row = last_row; row < nrows; row++) {
for (i = 0; i < n_outputs; i++) {
Rast_put_row(outfd[i], fbuf[0], out_data_type);
Rast_put_row(outfd[i], fbuf_threads[0][0], out_data_type);
}
}
G_percent(nrows, nrows, 1);

for (i = 0; i < n_outputs; i++)
G_free(fbuf[i]);
G_free(fbuf);
dealloc_vars(mv);
G_free(mv);
for (i = 0; i < threads; i++) {
for (j = 0; j < n_outputs; j++)
G_free(fbuf_threads[i][j]);
G_free(fbuf_threads[i]);
}
G_free(fbuf_threads);

for (i = 0; i < threads; i++) {
dealloc_vars(mvs[i]);
G_free(mvs[i]);
}
G_free(mvs);

return 0;
}
2 changes: 1 addition & 1 deletion raster/r.texture/execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ typedef struct output_setting {

int execute_texture(CELL **data, struct dimensions *dim,
struct menu *measure_menu, int *measure_idx,
struct output_setting *out_set);
struct output_setting *out_set, int threads);
Loading
Loading