6810: 1094 Activities 9
Online handouts: listings of Circle class files,
private_vs_public.cpp, and square_test.cpp;
"Using the GDB Debugger" and GDB summary sheet.
In this session we'll take a brief break from physics
and computational methods to
look at some programming tools and take a further
look at C++ classes.
Your goals for today (as time permits):
- Do a bit more with C++ classes.
- Try out the GDB debugger and
consider optimization and profiling.
- Try out rsync.
Revisiting area.cpp with a C++ Class
Our first simple C++ code from Activities 1 used procedural programming,
with the focus on the formula (which would typically be coded as a
function) for calculating the area. Now we revisit it from an
object oriented perspective. (We'll use "old" C++ and look at some
differences with C++11 in Activities 10.)
- Look at the online printout with test_Circle.cpp and the old area.cpp,
with the Circle class definitions.
Note how all of the details of the area calculation are now hidden.
Any questions on the Class definition?
Predict where in the test_Circle.cpp
code the two circles will be created and where they will be
destroyed. Why do we define get_radius and set_radius methods?
- Using make_test_Circle, compile and link test_Circle.cpp with
the class files. Run it and note where the circles are created
and destroyed. Do you understand why they are destroyed
in this order?
- Add a method to Circle.cpp (and Circle.h!) so that we can
get the circumfrence from my_circle.circumfrence(). Try it
out in test_Circle.cpp. Did you succeed?
- Take a look at the private_vs_public.cpp code, which
is a self-contained class and main program. Notice how the main
program can access and change the x value and use the xsq
function. But try changing from x to y in the statements getting,
printing, and changing the value of x.
What happens? Why does the get_y function work?
- (Extra, only do this if you are fast.)
Make a Sphere class with Sphere.cpp and Sphere.h, and try it
out by adding to test_Circle.cpp to create a Sphere and print
out its radius.
Did you succeed?
Using the GDB Debugger
We'll step through a contrived example that illustrates the basic
commands and capabilities of a debugger (in this case, the GNU debugger
gdb). We will
use the command-line ("no windows") version of gdb. There are graphical
interfaces (such as ddd) that are much nicer to use for more extensive
debugging, but it will be worthwhile to start with the simple, primitive
version.
- Cygwin and Mac users should run from fox with:
ssh -Y USERNAME@fox.mps.ohio-state.edu
The -Y allows editors and other programs to pop up windows.
Copy session09.zip to the current directory (".") and unzip:
cp /n/home00/ntg/public_html/6810/2016/1094_sessions/session09.zip .
unzip session09.zip
cd session_09
You can open another xterm on fox with xterm &
[Note: you will now need to use VPN from a non-ASC machine. See instructions on the
6810 webpage.]
- When debugging,
you may find it convenient to have two terminal windows open:
one to re-compile and link a sample code and another to run gdb in.
(Actually, you can interact with gdb directly through emacs, but
we won't go into that here.)
- Go through the example from the (printed!) handout "Using the GDB Debugger".
The code to debug is check_primes.cpp (a copy is also provided, called
check_primes_orig.cpp, so that you can go back to the original if
necessary).
We've also created a makefile that doesn't use any of our usual
warning flags (at first!).
Did you succeed?
Optimization 101: Squaring a Number
One of the most common floating-point operations is to square a number.
Two ways to square x are: pow(x,2) and x*x. Let's test how efficient
they are.
- Look at the printout for the square_test.cpp code. It implements
these two ways of squaring a number. The "clock" function from
time.h is used to find the elapsed time. Each operation is executed
a large number of times (determined by "repeat") so that we get
a reasonably accurate timing.
- We've set the optimization to its lowest value, -O0
("minus oh zero"), to
start in make_square_test.
- Compile square_test.cpp (using make_square_test) and run it.
Adjust "repeat" if the
minimum time is too small.
Record the times here. Which way to square x is more efficient?
- If you have an expression (rather than just x) to square,
coding (expression)*(expression) is awkward and hard to read.
Wouldn't it be better to call a function (e.g., squareit(expression)?
Add to square_test.cpp a function:
double squareit (double x)
that returns x*x. Add a section to the code that times how long
this takes (just copy one of the other timing sections and edit
it appropriately, making sure to keep the "final y" cout statement).
How does it compare to the others? What is the
"overhead" in calling a function (that is, how much extra time does
it take)? When is the overhead worthwhile?
- Another alternative, common from C programming:
use #define to define a macro
that squares a number. Add
#define sqr(z) ((z)*(z))
somewhere before the start of main.
(The extra ()'s are safeguards against unexpected behavior;
always include them!)
Add a section to
the code to time how long this macro takes; what do you find?
- One final alternative: add an "inline" function called square:
inline double square (double x) { return (x*x); };
that is a function prototype and the function itself.
Put it up top with the squareit prototype.
Add a section to
the code to time how long this function takes.
What is your conclusion about
which of these methods to use?
- Finally, we'll try the simplest way to optimize a code: let the
compiler do it for you! Change the compile flag -O0 (no
optimization) to -O2 (that's the uppercase
letter O, not a zero). Recompile and
run the code.
How do the times for each
operation compare to the times before you optimized?
What do you conclude?
- In your project programs, once they are debugged and running,
you'll want to use the -O2 (or maybe -O3) optimization flag.
Profiling
A "profiling" tool, such as gprof, allows you to analyze how the
execution time of your program is divided among various function calls.
This information identifies the candidate sections of code for
optimization. (You don't want to waste time optimizing a part of the
code that is only active for 1% of the run time!)
We'll use the eigen_basis_class.cpp code from an earlier
session as a guinea pig.
[Note: gprof is not supported on Mac OS X and can behave erratically on Cygwin.
For this activity, everyone should run on Linux again (either directly
or by ssh to fox).]
- To use gprof, compile and link the relevant codes with the -pg
option. You can do this most easily by editing
make_eigen_basis_class
and adding -pg to BOTH the CFLAGS and
LDFLAGS lines. (Note that make_eigen_basis_class has $(MAKEFILE) added to
two lines in section 4 to ensure that the codes are recompiled if the makefile
changes.)
- Execute the program as usual (choose a fairly large basis size
so that it takes a while to execute, building up statistics), which
generates a file called gmon.out that is used by gprof.
The program has to exit normally (e.g., you can't stop it with
control-c) and any existing gmon.out file will be overwritten.
- Run gprof and save the output to a file (e.g., gprof.out):
gprof eigen_basis_class > gprof.out
Edit gprof.out and try to figure out from the "Flat profile"
and the explanations where (i.e., in what functions) the program spends
the most time. What are the most time-consuming functions?
Would you try to speed up the part that finds the
eigenvalues?
- Now change the makefile so that -O3 optimization is
used. Run gprof and save the output to a new file:
gprof eigen_basis_class > gprof2.out
Compare gprof.out and gprof2.out.
What has -O3 done for you?
Introduction to Rsync
Rsync is a backup or mirroring tool that only copies the
differences of files that have changed. It can do this transfer
in compressed form and using ssh for security (both recommended!).
So updates are fast, efficient, and secure.
Here we'll make a trial run so that you get the basic idea.
- [The following is from Activities 5.]
Bash (tcsh) aliases. The .bashrc (.cshrc.more) file
sits in your home directory and
is "sourced" when you start an interactive terminal.
Take a look at the sample .bashrc (.cshrc.more) file printout (these files,
without the ".", are
in the session 9 zip file). Copy any aliases of interest to
your own .bashrc (.cshrc.more) or the whole thing using cp bashrc
~/.bashrc (cp cshrc.more ~/.cshrc.more)
if you don't have one already.
Activate the changes with the
command: source ~/.bashrc
(source ~/.cshrc).
- Take a look in the bashrc or cshrc.more
file for the definitions "rsyncbackup"
and "rsyncmirror".
These aliases use some of
the common rsync options. The difference is that rsyncmirror deletes
files at the destination that don't exist at the source; be careful
of this!
- Create a directory in your home directory
called "6810_backup" to use for testing rsync (i.e., "mkdir
6810_backup").
- We'll create a backup of the session 9 directory on fox.
(Since all your files are mounted on all of the physics public
computers, you don't need to copy to another computer, but this is
a demonstration!)
Go to the
parent directory of session_09.
Give this command:
rsyncbackup session_09 fox.mps.ohio-state.edu:6810_backup
If you are asked if you want to continue connecting, answer yes.
You'll probably be asked for your password as well (it is the same one
everywhere). You should see a list of files transferred and some
statistics.
- Check that the directory was transferred.
Now go back to the original session_09 directory
and change one file. (E.g.,
edit one of the .cpp files and add a comment.)
Then repeat the rsync transfer (from the appropriate directory),
using "!rsync" to avoid typing the entire alias.
You should find that only the changed file is updated.
Did you succeed? If not, what went wrong?
6810: 1094 Activities 9.
Last modified: .
furnstahl.1@osu.edu