Skip to content

Commit 6c7c37e

Browse files
committed
Improved detail of epoch timing reports, and performance measurement now uses only the model timings
Fixed performance issue with Tensor::pokeLastDimension Added a benchmark for studying the weak scaling of a fully-connected deep neural net, with cmdline tunable sizes Added a stopwatch/timer class for easier timing
1 parent 4bc8a09 commit 6c7c37e

5 files changed

Lines changed: 184 additions & 19 deletions

File tree

benchmark/Makefile.am

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include
33
AM_LDFLAGS = -Wl,-rpath=$(prefix)/lib
44
LDADD = -L$(top_builddir)/src -lhpcortex
55
benchmarkdir = $(prefix)/benchmark
6-
benchmark_PROGRAMS = benchmark_matrixBatchTensorContractRight benchmark_dnn benchmark_matrixTensorContractComponent benchmark_activated_deriv benchmarkBatchTensorDNNcomponentStep benchmark_tensor_reorder benchmark_thin_mul_mat_mattranspose benchmark_axpy_mat_thinmat benchmark_matrixBatchTensorContractLeft benchmark_batch3tensorContract benchmark_mul_mattranspose_thinmat benchmark_batchTensorContractToMatrix benchmark_matrixBatchTensorAxpy benchmark_decoder benchmark_multihead_self_attention
6+
benchmark_PROGRAMS = benchmark_matrixBatchTensorContractRight benchmark_dnn benchmark_matrixTensorContractComponent benchmark_activated_deriv benchmarkBatchTensorDNNcomponentStep benchmark_tensor_reorder benchmark_thin_mul_mat_mattranspose benchmark_axpy_mat_thinmat benchmark_dnn_weak_scaling benchmark_matrixBatchTensorContractLeft benchmark_batch3tensorContract benchmark_mul_mattranspose_thinmat benchmark_batchTensorContractToMatrix benchmark_matrixBatchTensorAxpy benchmark_decoder benchmark_multihead_self_attention
77
benchmark_matrixBatchTensorContractRight_SOURCES = benchmark_matrixBatchTensorContractRight.cpp
88
benchmark_matrixBatchTensorContractRight_LDADD = $(LDADD)
99
benchmark_matrixBatchTensorContractRight_LINK = $(CXXLD) $(AM_LDFLAGS) $(LDFLAGS) -o $@
@@ -28,6 +28,9 @@ benchmark_thin_mul_mat_mattranspose_LINK = $(CXXLD) $(AM_LDFLAGS) $(LDFLAGS) -o
2828
benchmark_axpy_mat_thinmat_SOURCES = benchmark_axpy_mat_thinmat.cpp
2929
benchmark_axpy_mat_thinmat_LDADD = $(LDADD)
3030
benchmark_axpy_mat_thinmat_LINK = $(CXXLD) $(AM_LDFLAGS) $(LDFLAGS) -o $@
31+
benchmark_dnn_weak_scaling_SOURCES = benchmark_dnn_weak_scaling.cpp
32+
benchmark_dnn_weak_scaling_LDADD = $(LDADD)
33+
benchmark_dnn_weak_scaling_LINK = $(CXXLD) $(AM_LDFLAGS) $(LDFLAGS) -o $@
3134
benchmark_matrixBatchTensorContractLeft_SOURCES = benchmark_matrixBatchTensorContractLeft.cpp
3235
benchmark_matrixBatchTensorContractLeft_LDADD = $(LDADD)
3336
benchmark_matrixBatchTensorContractLeft_LINK = $(CXXLD) $(AM_LDFLAGS) $(LDFLAGS) -o $@
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//A strong-scaling benchmark using a deep fully-connected network of variable size
2+
//The total batch size is scaled to the number of ranks
3+
4+
#include<HPCortex.hpp>
5+
6+
7+
int main(int argc, char** argv){
8+
initialize(argc,argv);
9+
communicators().reportSetup();
10+
11+
int hidden_layers = 5;
12+
int input_features = 32;
13+
int output_features = 32;
14+
int hidden_neurons = 128;
15+
int rank_batch_size = 32;
16+
int nbatch_per_epoch = 100;
17+
int nepoch = 10;
18+
19+
int arg=1;
20+
while(arg < argc){
21+
std::string sarg(argv[arg]);
22+
if(sarg == "--hidden_layers"){
23+
hidden_layers = std::stoi(argv[arg+1]);
24+
arg+=2;
25+
}else if(sarg == "--input_features"){
26+
input_features = std::stoi(argv[arg+1]);
27+
arg+=2;
28+
}else if(sarg == "--output_features"){
29+
output_features = std::stoi(argv[arg+1]);
30+
arg+=2;
31+
}else if(sarg == "--hidden_neurons"){
32+
hidden_neurons = std::stoi(argv[arg+1]);
33+
arg+=2;
34+
}else if(sarg == "--rank_batch_size"){
35+
rank_batch_size = std::stoi(argv[arg+1]);
36+
arg+=2;
37+
}else if(sarg == "--nbatch_per_epoch"){
38+
nbatch_per_epoch = std::stoi(argv[arg+1]);
39+
arg+=2;
40+
}else if(sarg == "--nepoch"){
41+
nepoch = std::stoi(argv[arg+1]);
42+
arg+=2;
43+
}else{
44+
std::cout << "Unknown cmdline argument: " << sarg << std::endl;
45+
assert(0);
46+
}
47+
}
48+
49+
50+
std::mt19937 rng(1234);
51+
52+
auto model_body = enwrap( dnn_layer(hidden_neurons, input_features, ReLU<float>(),
53+
input_layer< confSingle, Matrix<float> >()
54+
)
55+
);
56+
for(int l=0;l<hidden_layers-2;l++) //the first dnn_layer has one hidden layer, as does the last
57+
model_body = enwrap( dnn_layer(hidden_neurons,hidden_neurons, ReLU<float>(), std::move(model_body)) );
58+
59+
auto model = dnn_layer(output_features, hidden_neurons, noActivation<float>(), model_body);
60+
61+
std::cout << "Params: " << model.nparams() << std::endl;
62+
63+
int nrank = communicators().ddpNrank();
64+
int ndata = rank_batch_size * nrank;
65+
std::vector<XYpair<float,1,1> > data(ndata);
66+
for(auto &d : data){
67+
d.x = Vector<float>(input_features);
68+
d.y = Vector<float>(output_features);
69+
uniformRandom(d.x,rng);
70+
uniformRandom(d.y,rng);
71+
}
72+
73+
auto loss = mse_cost(model);
74+
75+
AdamOptimizer<float> opt(0.01);
76+
XYpairDataLoader<float,1,1> loader(data);
77+
train(loss, loader, opt, nepoch, ndata);
78+
}

include/Timing.hpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,45 @@ inline double since(const std::chrono::system_clock::time_point &when){
2121
return double(usCountSince(when)) / 1e6;
2222
}
2323

24+
/**
25+
* @brief A simple timer class
26+
*/
27+
class Timer{
28+
private:
29+
double ttot;
30+
std::chrono::system_clock::time_point tp;
31+
public:
32+
/**
33+
* @brief Reset the accumulated time to zero and (optionally) begin timing from now
34+
*/
35+
inline void restart(bool start = false){
36+
ttot = 0;
37+
if(start) tp = now();
38+
}
39+
/**
40+
* @brief Resume a timer when paused
41+
*/
42+
inline void resume(){
43+
tp = now();
44+
}
45+
/**
46+
* @brief Pause a timer, adding the time since start/resume to the accumulated time
47+
*/
48+
inline void pause(){
49+
ttot += since(tp);
50+
}
51+
52+
/**
53+
* @brief Get the accumulated time
54+
*/
55+
inline double time() const{
56+
return ttot;
57+
}
2458

59+
/**
60+
* @brief Construct and (optionally) start the timer
61+
*/
62+
Timer(bool start_on_create = false){ restart(start_on_create); }
63+
};
2564

65+
#define TIME(into, ...) into.resume(); __VA_ARGS__; into.pause();

include/implementation/Optimizers.tcc

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
1+
struct train_stage_time_report{
2+
Timer total;
3+
Timer loader;
4+
Timer model;
5+
Timer reduce;
6+
7+
train_stage_time_report(): total(true){}
8+
9+
std::string report(double FLOPS){
10+
double Gflops = FLOPS/1e9 /model.time();
11+
std::ostringstream os;
12+
double other = total.time() - loader.time() - model.time() - reduce.time();
13+
os << "loader: " << loader.time() << "s, "
14+
<< "model: " << model.time() << "s (" << Gflops << " Gflops), "
15+
<< "reduction: " << reduce.time() << "s, "
16+
<< "other: " << other << "s | "
17+
<< "total: " << total.time();
18+
return os.str();
19+
}
20+
};
21+
22+
23+
124
template<typename DataLoader, typename LossWrappedModelType, typename Optimizer>
225
std::pair<
326
std::vector<typename LossWrappedModelType::FloatType>,
@@ -50,8 +73,8 @@ train(LossWrappedModelType &loss_func, const DataLoader &train_data, DataLoader
5073
//////////// train epoch ///////////////
5174
optimizer.epochStart(epoch, do_print);
5275
std::random_shuffle ( didx_train.begin(), didx_train.end(), [&](const int l){ return dist(gen); } ); //shuffle training data indices
53-
FloatType lmax_train=std::numeric_limits<FloatType>::lowest(), lmin_train = std::numeric_limits<FloatType>::max(), lavg_train = 0.;
54-
auto ts=now();
76+
FloatType lmax_train=std::numeric_limits<FloatType>::lowest(), lmin_train = std::numeric_limits<FloatType>::max(), lavg_train = 0.;
77+
train_stage_time_report t_train;
5578

5679
for(int block=0;block<nblocks_ddp_train;block++){
5780
int ddp_blocksize_actual = std::min(nbatch_train - block*ddp_blocksize, ddp_blocksize);
@@ -63,14 +86,21 @@ train(LossWrappedModelType &loss_func, const DataLoader &train_data, DataLoader
6386
int bidx = block*ddp_blocksize + me; //which batch are we doing?
6487

6588
//Get the batch
66-
auto bxy = train_data.batch(didx_train.data() + bidx*batch_size, batch_size);
89+
TIME(t_train.loader,
90+
auto bxy = train_data.batch(didx_train.data() + bidx*batch_size, batch_size);
91+
);
6792

93+
TIME(t_train.model,
6894
loss = loss_func.loss(bxy.x, bxy.y, DerivYes);
6995
deriv = loss_func.deriv();
96+
);
7097
}
98+
99+
TIME(t_train.reduce,
71100
ddpAverage(&loss,1,false); //no need to bcast the loss to the pipeline ranks
72101
ddpAverage(deriv,true); //share the deriv over all pipeline ranks
73-
102+
)
103+
74104
//if(do_print) std::cout << epoch << "-" << block << " : "<< loss << std::endl;
75105
lmax_train = std::max(lmax_train,loss);
76106
lmin_train = std::min(lmin_train,loss);
@@ -84,45 +114,59 @@ train(LossWrappedModelType &loss_func, const DataLoader &train_data, DataLoader
84114
losses_train[block+nblocks_ddp_train*epoch] = loss;
85115
}
86116
lavg_train /= nblocks_ddp_train;
87-
double train_time = since(ts);
88-
double train_Tflops = nbatch_train * double(loss_func.FLOPS(0) + loss_func.FLOPS(1) + 2*loss_func.nparams())/1.0e12 / train_time;
117+
118+
t_train.total.pause();
119+
double train_FLOPS = nbatch_train * double(loss_func.FLOPS(0) + loss_func.FLOPS(1));
89120

90121
//////////// end train epoch ///////////////
91122

92123
//////////// validate epoch ///////////////
93124
if(valid_data){
94125
FloatType lmax_valid=std::numeric_limits<FloatType>::lowest(), lmin_valid = std::numeric_limits<FloatType>::max(), lavg_valid = 0.;
95-
ts=now();
126+
train_stage_time_report t_valid;
96127

97128
for(int block=0;block<nblocks_ddp_valid;block++){
98129
int ddp_blocksize_actual = std::min(nbatch_valid - block*ddp_blocksize, ddp_blocksize);
99130

100131
FloatType loss = 0;
101132
if(me < ddp_blocksize_actual){
102-
int bidx = block*ddp_blocksize + me;
133+
int bidx = block*ddp_blocksize + me;
134+
TIME(t_valid.loader,
103135
auto bxy = valid_data->batch(didx_valid.data() + bidx*batch_size, batch_size); //no need to shuffle
136+
);
137+
TIME(t_valid.model,
104138
loss = loss_func.loss(bxy.x, bxy.y, DerivNo);
139+
);
105140
}
106-
141+
142+
TIME(t_valid.reduce,
107143
ddpAverage(&loss,1,false);
108-
144+
);
145+
109146
lmax_valid = std::max(lmax_valid,loss);
110147
lmin_valid = std::min(lmin_valid,loss);
111148
lavg_valid += loss;
112149

113150
losses_valid[block+nblocks_ddp_valid*epoch] = loss;
114-
}
151+
}
115152
lavg_valid /= nblocks_ddp_valid;
116-
double valid_time = since(ts);
117-
double valid_Tflops = nbatch_valid * double(loss_func.FLOPS(0))/1.0e12 / valid_time;
153+
t_valid.total.pause();
154+
155+
double valid_FLOPS = nbatch_valid * double(loss_func.FLOPS(0));
118156

119157
//////////// end validate epoch ///////////////
120158

121159
if(do_print) std::cout << "Epoch : " << epoch << std::endl
122-
<< "training time : " << train_time <<"s (" << train_Tflops << " Tflops) loss min: " << lmin_train << " avg: " << lavg_train << " max: " << lmax_train << std::endl
123-
<< "validation time : " << valid_time <<"s (" << valid_Tflops << " Tflops) loss min: " << lmin_valid << " avg: " << lavg_valid << " max: " << lmax_valid << std::endl;
124-
}else{ //if not validating, just print info on the training losses
125-
if(do_print) std::cout << "Epoch : " << epoch << " time : " << train_time <<"s ("<< train_Tflops << " Tflops) loss min: " << lmin_train << " avg: " << lavg_train << " max: " << lmax_train << std::endl;
160+
<< "training loss min: " << lmin_train << " avg: " << lavg_train << " max: " << lmax_train << std::endl
161+
<< "validation loss min: " << lmin_valid << " avg: " << lavg_valid << " max: " << lmax_valid << std::endl
162+
<< "training timings: " << t_train.report(train_FLOPS) << std::endl
163+
<< "validation timings: " << t_valid.report(valid_FLOPS) << std::endl;
164+
165+
166+
}else{ //if not validating, just print info on the training losses
167+
if(do_print) std::cout << "Epoch : " << epoch << std::endl
168+
<< "loss min: " << lmin_train << " avg: " << lavg_train << " max: " << lmax_train << std::endl
169+
<< "timings: " << t_train.report(train_FLOPS) << std::endl;
126170
}
127171
}//epoch
128172

include/implementation/Tensors.tcc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ void Tensor<FloatType,Dim>::pokeLastDimension(const Tensor<FloatType,Dim-1> &ins
7272

7373
autoView(ins_v,ins,DeviceRead);
7474
autoView(t_v,(*this),DeviceReadWrite);
75-
accelerator_for2d(dummy1,1, i,other_size,32,{
75+
accelerator_for_gen(1,0,splitBlock<32>(), i,other_size,{
7676
t_v.data()[idx + size_last *i] = ins_v.data()[i];
7777
});
7878
}

0 commit comments

Comments
 (0)