2929#include " absl/flags/parse.h" // from @com_google_absl
3030#include " absl/log/absl_log.h" // from @com_google_absl
3131#include " absl/strings/str_format.h" // from @com_google_absl
32+ #include " absl/strings/string_view.h" // from @com_google_absl
3233#include " absl/types/span.h" // from @com_google_absl
3334#include " litert/c/litert_common.h"
3435#include " litert/cc/litert_compiled_model.h"
4041#include " litert/cc/litert_options.h"
4142#include " litert/cc/litert_tensor_buffer.h"
4243#include " litert/cc/options/litert_gpu_options.h"
43-
44- ABSL_FLAG (std::string, graph, " " , " Model filename to use for testing." );
44+ #include " litert/core/filesystem.h"
45+
46+ ABSL_FLAG (bool , print_diff_stats, false ,
47+ " Whether to print the diff stats CSV." );
48+ ABSL_FLAG (std::string, model_dir, " " ,
49+ " Optional base directory to prepend to models provide in --graph." );
50+ ABSL_FLAG (std::vector<std::string>, graph, {},
51+ " Model file(s) to use for testing." );
4552ABSL_FLAG (size_t , signature_index, 0 , " Index of the signature to run." );
4653ABSL_FLAG (float , epsilon, 1e-4f ,
4754 " Threshold value for gpu / cpu inference comparison" );
@@ -56,8 +63,33 @@ ABSL_FLAG(bool, enable_constant_tensors_sharing, false,
5663 " Whether to enable constant tensors sharing." );
5764
5865namespace litert {
66+
5967namespace {
6068
69+ struct BufferDiffStats {
70+ // Index of output buffer.
71+ size_t buffer_idx;
72+ // Total number of elements in the buffer.
73+ size_t total_elements;
74+ // Number of elements with absolute difference greater than epsilon.
75+ size_t diff_elements;
76+ // Epsilon value used for comparison.
77+ double epsilon;
78+ // Maximum absolute difference between CPU and GPU values.
79+ double max_diff;
80+ // Minimum absolute difference between CPU and GPU values.
81+ double min_diff;
82+ // Mean absolute difference between CPU and GPU values.
83+ double mean_diff;
84+ // Mean squared error between CPU and GPU values.
85+ double mse;
86+ };
87+
88+ struct ModelRunResult {
89+ std::string model_name;
90+ std::vector<BufferDiffStats> diff_stats;
91+ };
92+
6193Expected<Environment> GetEnvironment () {
6294 std::vector<litert::Environment::Option> environment_options = {};
6395
@@ -154,13 +186,14 @@ Expected<std::vector<TensorBuffer>> CreateOutputBuffers(
154186}
155187
156188// Compares a single pair of output buffers and prints the results.
157- Expected<void > CompareSingleOutputBuffer (TensorBuffer& cpu_buffer,
158- TensorBuffer& gpu_buffer,
159- size_t buffer_index, float epsilon) {
189+ Expected<BufferDiffStats> CompareSingleOutputBuffer (TensorBuffer& cpu_buffer,
190+ TensorBuffer& gpu_buffer,
191+ size_t buffer_index,
192+ float epsilon) {
160193 std::vector<std::pair<float , int >> all_diffs;
161194 const int kMaxPrint = 20 ;
162195 int printed = 0 ;
163- int total_different = 0 ;
196+ size_t total_different = 0 ;
164197 double mean_squared_error = 0 ;
165198 float mean_diff = 0 ;
166199
@@ -299,10 +332,19 @@ Expected<void> CompareSingleOutputBuffer(TensorBuffer& cpu_buffer,
299332 std::cout << " Total " << total_different << " out of " << total_elements
300333 << " are different elements, for output #" << buffer_index
301334 << " , threshold - " << epsilon << std::endl;
302- return {};
335+ return BufferDiffStats{
336+ .buffer_idx = buffer_index,
337+ .total_elements = total_elements,
338+ .diff_elements = total_different,
339+ .epsilon = epsilon,
340+ .max_diff = all_diffs.back ().first ,
341+ .min_diff = all_diffs.front ().first ,
342+ .mean_diff = mean_diff / all_diffs.size (),
343+ .mse = mean_squared_error / total_elements,
344+ };
303345}
304346
305- Expected<void > CompareOutputBuffers (
347+ Expected<std::vector<BufferDiffStats> > CompareOutputBuffers (
306348 std::vector<TensorBuffer>& cpu_output_buffers,
307349 std::vector<TensorBuffer>& gpu_output_buffers) {
308350 if (cpu_output_buffers.size () != gpu_output_buffers.size ()) {
@@ -312,26 +354,24 @@ Expected<void> CompareOutputBuffers(
312354
313355 float epsilon = absl::GetFlag (FLAGS_epsilon);
314356 size_t num_output_buffers = cpu_output_buffers.size ();
357+ std::vector<BufferDiffStats> diff_stats;
315358 for (size_t i = 0 ; i < num_output_buffers; ++i) {
316359 auto & cpu_buffer = cpu_output_buffers[i];
317360 auto & gpu_buffer = gpu_output_buffers[i];
318- LITERT_RETURN_IF_ERROR (
361+ LITERT_ASSIGN_OR_RETURN (
362+ auto diff_stat,
319363 CompareSingleOutputBuffer (cpu_buffer, gpu_buffer, i, epsilon));
364+ diff_stats.push_back (std::move (diff_stat));
320365 }
321- return {} ;
366+ return diff_stats ;
322367}
323368
324- Expected<void > RunModel () {
325- if (absl::GetFlag (FLAGS_graph).empty ()) {
326- return Error (kLiteRtStatusErrorInvalidArgument ,
327- " model filename is empty. Use --graph to provide it." );
328- }
329-
330- ABSL_LOG (INFO) << " Model: " << absl::GetFlag (FLAGS_graph);
369+ Expected<std::vector<BufferDiffStats>> RunModel (absl::string_view model_path) {
370+ ABSL_LOG (INFO) << " Model: " << model_path;
331371 LITERT_ASSIGN_OR_RETURN (auto cpu_model,
332- Model::CreateFromFile (absl::GetFlag (FLAGS_graph )));
372+ Model::CreateFromFile (std::string (model_path )));
333373 LITERT_ASSIGN_OR_RETURN (auto gpu_model,
334- Model::CreateFromFile (absl::GetFlag (FLAGS_graph )));
374+ Model::CreateFromFile (std::string (model_path )));
335375
336376 LITERT_ASSIGN_OR_RETURN (auto env, GetEnvironment ());
337377
@@ -373,10 +413,53 @@ Expected<void> RunModel() {
373413 signature_index, gpu_input_buffers, gpu_output_buffers));
374414
375415 // Compare output buffers
376- LITERT_RETURN_IF_ERROR (
416+ LITERT_ASSIGN_OR_RETURN (
417+ auto diff_stats,
377418 CompareOutputBuffers (cpu_output_buffers, gpu_output_buffers));
419+ return diff_stats;
420+ }
421+
422+ Expected<std::vector<ModelRunResult>> RunModels () {
423+ std::vector<std::string> relative_model_paths = absl::GetFlag (FLAGS_graph);
424+ if (relative_model_paths.empty ()) {
425+ return Error (kLiteRtStatusErrorInvalidArgument ,
426+ " No model provided. Use --graph to provide it." );
427+ }
378428
379- return {};
429+ std::string model_dir = absl::GetFlag (FLAGS_model_dir);
430+ std::vector<std::string> full_model_paths;
431+ full_model_paths.reserve (relative_model_paths.size ());
432+ for (auto & model_path : relative_model_paths) {
433+ full_model_paths.push_back (internal::Join ({model_dir, model_path}));
434+ }
435+
436+ std::vector<ModelRunResult> results;
437+ for (const auto & model_path : full_model_paths) {
438+ LITERT_ASSIGN_OR_RETURN (std::vector<BufferDiffStats> diff_stats,
439+ RunModel (model_path));
440+ results.push_back (ModelRunResult{
441+ .model_name = internal::Stem (model_path),
442+ .diff_stats = std::move (diff_stats),
443+ });
444+ }
445+
446+ return results;
447+ }
448+
449+ void PrintDiffStats (const std::vector<litert::ModelRunResult>& results) {
450+ // Print CSV header
451+ std::cout << " model_name, buffer_idx, total_elements, diff_elements, "
452+ " epsilon, max_diff, min_diff, mean_diff, mse"
453+ << std::endl;
454+ for (const auto & result : results) {
455+ for (const auto & diff_stat : result.diff_stats ) {
456+ std::cout << result.model_name << " , " << diff_stat.buffer_idx << " , "
457+ << diff_stat.total_elements << " , " << diff_stat.diff_elements
458+ << " , " << diff_stat.epsilon << " , " << diff_stat.max_diff
459+ << " , " << diff_stat.min_diff << " , " << diff_stat.mean_diff
460+ << " , " << diff_stat.mse << std::endl;
461+ }
462+ }
380463}
381464
382465} // namespace
@@ -385,10 +468,15 @@ Expected<void> RunModel() {
385468int main (int argc, char ** argv) {
386469 absl::ParseCommandLine (argc, argv);
387470
388- auto res = litert::RunModel ();
471+ auto res = litert::RunModels ();
389472 if (!res) {
390473 ABSL_LOG (ERROR) << res.Error ().Message ();
391474 return EXIT_FAILURE;
392475 }
476+
477+ if (absl::GetFlag (FLAGS_print_diff_stats)) {
478+ litert::PrintDiffStats (*res);
479+ }
480+
393481 return EXIT_SUCCESS;
394482}
0 commit comments