Faster webpage transmission when using F() flash strings

Hi

In another thread in this topic someone kindly pointed out that the web pages on my home automation site at http://www.2wg.co.nz/ are quite slow to download.

The problem seems to relate to the use of ethernetclient.print() and println() procedures, specifically procedure calls that contain F() function flash memory strings. I use F() function flash memory strings extensively within my application to save precious SRAM.

Today I researched through the Arduino libraries to find the cause of the problem. I can confirm that the implementation of print() within the print.cpp library file is both extracting and writing flash memory strings one byte at a time without using any buffer to accumulate flash string bytes before writing the string out to its destination in bulk (using a buffer).

My research indicates that the ethernetclient.write() procedure is what the print.print() procedure calls to write data to a TCP/IP socket on my W5100 ethernet chip. ethernetclient.write() in turn calls the socket.send() procedure - this latter procedure writes data into a socket before instructing the W5100 chip to transmit the data.

The net effect is that ethernetclient.print() and println() procedures (when using F() function flash strings) do cause data transmission out of the W5100 chip one byte at a time with all of the multi-level procedure (i.e. stack) and TCP/IP overheads occurring for each and every byte of a F() function flash memory string.

This is the original print.print() procedure that is at the heart of this problem:

size_t Print::print(const __FlashStringHelper *ifsh)
{
  PGM_P p = reinterpret_cast<PGM_P>(ifsh);
  size_t n = 0;
  while (1) {
 unsigned char c = pgm_read_byte(p++);
 if (c == 0) break;
 n += write(c);
  }
  return n;
}

and this is how I have reimplemented it:

size_t Print::print(const __FlashStringHelper *ifsh)
{
  PGM_P p = reinterpret_cast<PGM_P>(ifsh);
  size_t n = 0;
  const size_t c_file_buffer_size = 128;
  char l_buffer[c_file_buffer_size];
  size_t l_index = 0;
  while (1) {
	l_buffer[l_index] = pgm_read_byte(p++);
	if (l_buffer[l_index] == 0)
	  break;
	//
	l_index++;
	if (l_index == c_file_buffer_size) {
	  n += write(&l_buffer[0], c_file_buffer_size);
	  l_index = 0;
	}
  }
  if (l_index != 0)
	n += write(&l_buffer[0], l_index);
  //
  return n;
}

My website is now returning web pages much faster however there are still some pages that generate dynamic content that have minor delays while the system is communicating with various sensors.

I have also converted my web page text file listing code to also use buffers instead of using ethernetclient.print() character by character. This has significantly increased the response time for these web pages.

I hope this is helpful for anyone wanting to make their web pages faster and not knowing the performance impact of using the F() flash function or of sending web page data one byte at a time.

Cheers

Catweazle NZ

Nice detective work, and thanks for figuring out a workaround!

But for those of us who are less experienced, can you explain the correct way to apply this patch?

Isn't there a risk that changing the function code in the Print.cpp file supplied with the Arduino IDE will have unintended consequences in other contexts?

Is there a way to simply provide your buffer-enabled print.print() procedure in the user program or in the EthernetClient library file so that other programs/libraries still use the original print.print() code?

Hi

To apply the patch you do have to find print.cpp on you system and make the code change that I provided.

You will lose the change if the Arduino system updates the print.cpp file at any time in the future. So keep a copy of this change so you can reapply it.

You need to make the change on every PC that you use for Arduino development.

The beauty of functional programming is that you can change/improve individual shared procedures or functions without impacting any software application provided the procedure inputs and outputs remain the same.

This particular change might impact any Arduino sketch that has less than 128 bytes of SRAM free before any call to print(). But changing the buffer size to 16 bytes should avoid that and still give good performance improvements when printing flash F() strings.

You cannot implement thus change solely within your Arduino sketches if you want to use print() with flash F() strings. The change I have provided addresses a poor implementation of an Arduino print() function that loads F() strings from flash memory for printing.

Cheers

Catweazle NZ

If you don't want to edit print.cpp then just convert the __FlashStringHelper to a string in the sketch or library and then write the string.

I've been using this. Do you see anything wrong here?

char tBuf[64];
strcpy_P(tBuf,PSTR("your program memory text here"));