Last update: 24-Mar-2019
Author: R. Koucha

How to use printf() efficiently




Introduction

In lots of software, I can see the following in red color (one example among numerous ones !):

BRCode_t ethsw_run_print_bist(int switch_ID)
{
  ethsw_bist_t bist_result;
  BRCode_t rt;
  char * sw_name;
  if (!KETHSW_VALID_SWITCH_ID(switch_ID))
  {
    printf("Invalid switch ID (%d)\n", switch_ID);
    return BRC_ERR;
  }

  rt = ethsw_bist_run(switch_ID, &bist_result);
 
  printf("BIST result:\n");

  switch (bist_result.device_ID)
      {
      case ZL_ZL33032_ID:
          sw_name =  ETHSW_NAME1;
          break;
      case ZL_ZL33036_ID:
          sw_name =  ETHSW_NAME3;
          break;
      case ZL_ZL33050_ID:
          sw_name =  ETHSW_NAME2;
          break;
      default:
          sw_name = "Unknown switch !";
          break;
      }

  printf("    Device ID      : %s (0x%x)\n", sw_name,bist_result.device_ID);
  printf("    Device revision: %d\n",  bist_result.device_revision);
  printf("    RAM status     : %s\n", (bist_result.RAM_OK     )?"OK" :"KO");
  printf("    BIST result    : %s\n", (rt == BRC_OK           )?"OK" :"KO");
  printf("\n!! Warning: Switch has been stopped and restarted to run BIST !!\n");
 
  return rt;
}


This is bad for the following reasons :
1. This is not efficient
Why are we calling printf() several times since only one call would be sufficient. We would save some CPU time.
2. The displays may be mixed
As the data are displayed in multiple shots (one shot per printf() call), the calling task may be preempted between the calls by another task which may call printf() as well. So, this would trigger the mix of displays coming from multiple tasks. This does not facilitate the reading of the traces while debugging !
3. The memory footprint is more important
The multiple calls to printf() triggers the generation of multiple identical sets of instructions: one set per printf() call. This makes the generated code fat.

Proposed better code

Here is the function fixed to avoid trace mixing, save memory space (the assembly code shows that we save 92 bytes of instructions on a PowerPC architecture) and save some CPU time...


BRCode_t ethsw_run_print_bist(int switch_ID)
{
  ethsw_bist_t bist_result;
  BRCode_t rt;
  char * sw_name;
  if (!KETHSW_VALID_SWITCH_ID(switch_ID))
  {
    printf("Invalid switch ID (%d)\n", switch_ID);
    return BRC_ERR;
  }

  rt = ethsw_bist_run(switch_ID, &bist_result);
 
  switch (bist_result.device_ID)
      {
      case ZL_ZL33032_ID:
          sw_name =  ETHSW_NAME1;
          break;
      case ZL_ZL33036_ID:
          sw_name =  ETHSW_NAME3;
          break;
      case ZL_ZL33050_ID:
          sw_name =  ETHSW_NAME2;
          break;
      default:
          sw_name = "Unknown switch !";
          break;
      }


  printf("BIST result:\n"
         "    Device ID      : %s (0x%x)\n"
         "    Device revision: %d\n"
         "    RAM status     : %s\n"
         "    BIST result    : %s\n"
         "\n!! Warning: Switch has been stopped and restarted to run BIST !!\n"
         ,
         sw_name,bist_result.device_ID,
         bist_result.device_revision,
         (bist_result.RAM_OK)?"OK" :"KO",
         (rt == BRC_OK)?"OK" :"KO"
        );

 
  return rt;
}

Conclusion

The preceding applies to all the printf() family routines... Note that some functions may be limited to 4KB of displayed data in one shot.

Of course, this is not a revolution. We will not definitely solve the memory space and performance problems that we may face but it is so simple to do that it is worth it !


Some interesting links


About the author

The author is an engineer in computer sciences located in France. He can be contacted here.