EEPROM.put() method

Hi,

My question concerns the EEPROM.put method.
If I understand correctly, when I use EEPROM.put to save a data structure to EEPROM, and only a single byte of that structure has been modified since the last time EEPROM.put was used, only that byte will be written. Is that correct?

As I understand it, that is correct

The put() function uses the update() function to do what you describe

We need an experiment to verify the apparent fact. Here it is:

#include <EEPROM.h>

struct MyData 
{
  byte a;
  byte b;
  byte c;
  byte d;
};

MyData data1 = {10, 20, 30, 40};  //the values are in RAM
MyData data2;

unsigned long t1, t2;

void setup() 
{
  Serial.begin(9600);
  while (!Serial);

  Serial.println("EEPROM.put() Timing Test");

  // -------- First Write (All bytes written) --------
  t1 = micros();
  EEPROM.put(0, data1);  //10, 20, 30, 40 has entered into EEPROM
  t2 = micros();
  Serial.print("First write time (us): ");
  Serial.println(t2 - t1);

  // -------- Second Write (Same data, should skip writes) --------
  t1 = micros();
  EEPROM.put(0, data1);
  t2 = micros();
  Serial.print("Second write (no change) time (us): ");
  Serial.println(t2 - t1);

  // -------- Modify ONE byte --------
  data1.c = 99;

  // -------- Third Write (Only 1 byte should be written) --------
  t1 = micros();
  EEPROM.put(0, data1);
  t2 = micros();
  Serial.print("Third write (1 byte changed) time (us): ");
  Serial.println(t2 - t1);

  // -------- Read back for verification --------
  EEPROM.get(0, data2);

  Serial.print("Read back: ");
  Serial.print(data2.a); Serial.print(", ");
  Serial.print(data2.b); Serial.print(", ");
  Serial.print(data2.c); Serial.print(", ");
  Serial.println(data2.d);
}

void loop() { }

Output:

EEPROM.put() Timing Test
First write time (us): 3432
Second write (no change) time (us): 12
Third write (1 byte changed) time (us): 3436
Read back: 10, 20, 99, 40

Please explain what you infer from the results

What values were in the EEPROM before running the test ?

I am expecting to hear what OP says based on his understanding the difference between put() and update() methods.

Unknown.

To my mind that invalidates the first use of put() in the test

The first EEPROM.put(0, data1) writes 10, 20, 30, 40 into four consecutive memory locations (0, 1, 2, 3) of the EEPROM. This is to start the experiment with known values in the EEPROM.

You are right. Please accept my apologies for doubting your code

I have received your interactions very positively, as they reflect a genuine effort to explore things with good intentions.

The answer is obvious simply looking at the source code in "EEPROM.h".

Pardon me for saying this, but it is far from obvious.

put() calls update() but it is not obvious how it avoids updating bytes that match, at least to me

Can you explain it ?

If the put() method employs the update() method while writing data, then I would expect that writing 10, 20, 99, 40 (3436 us?) would take less time than the previous write of 10, 20, 30, 40 (3432 us), since only one element (99 in place of 30) differs in the new data.

I understand that, in the example from post #3, the put() operation is effectively broken into four update() calls. The 1st, 2nd, and 4th update() calls should be skipped because the old and new data are the same, and only the 3rd update() should actually perform a write. However, the measured execution time (3436 us) suggests that all four update() operations took place. What might be happening here? I believe that the answer can be understood clearly from the textual description, rather than from the cryptic code of the EEPROM.h library.

Unless you already have the serial monitor open when uploading the sketch, the entire sketch will have had time to run completely before you see any output. What you are seeing in this output is the first write only writing one byte, the third byte changing from 99 to 30. The second write writes no bytes, thus the 12uS time. The third write again writes a single byte, changing from 30 back to 99. If you add an additional step changing two bytes of the struct, the time is almost 7000uS.

Put calls update for each byte in the object being written:

    template< typename T > const T &put( int idx, const T &t ){
        EEPtr e = idx;
        const uint8_t *ptr = (const uint8_t*) &t;
        for( int count = sizeof(T) ; count ; --count, ++e )  (*e).update( *ptr++ );
        return t;
    }

update() first checks to see if the byte being written differs from the byte already stored in EEPROM. If so it writes the new value:

EERef &update( uint8_t in )          { return  in != *this ? *this = in : *this; }

See the definition of the EERef and EEPtr types near the beginning of "EEPROM.h" to see how the operator overloading works.

For such tests, I would write first some data, then timed writing totally differen data, then timed no change, timed writing 1 byte change, then timed writing 2 byte change.

Because if I will repeat the test (improving output format, add more subtests, just reseted the board before reading results, …) the first write will be against the resulting data, so if result differs just in one byte, it may be 1 byte update.

Writind first some data and timing totatly different data ensures, that the result will be 4 byte change every time the test is run.

timing 0, 1 and 2 bytes change give insight into how much time is spend by checking what NOT to update and how much is spend by the updates.

I am not fully convinced with your reasoning as I understand that the put() method essenstially does the following:

for (...) 
{
  if (old != new)   //read and compare
  {
    write();     
  }
}

I'm not disputing how put() works. What I am saying is that the initial contents of the EEPROM at the start of the code will be {10, 20, 99, 40}, because the processor has time to run the entire code while the USB connection is being reset between the upload and the re-connection to the serial monitor. This means that the initial 3432uS is the time required to read back all four bytes from EEPROM, compare the values, and write the single byte to change 99 back to 30. The second test only takes 12uS because no write to EEPROM is needed, and the third test take 3436uS because you are again writing a single byte to EEPROM. I added a 4th test, changing two bytes of the struct, and got slightly below 7000uS for the two bytes written to EEPROM.

This source is based on GolamMostafa source. It shows that put method and multiple update methods have the same timing.

#include <EEPROM.h>

struct MyData 
{
  byte a;
  byte b;
  byte c;
  byte d;
};

MyData data1;
MyData data2;

unsigned long t1, t2;


void EepromPut(int EEPROM_address, unsigned char *data, uint8_t dataLength) 
{
	int _address=EEPROM_address;
	for (int i = 0; i < dataLength; i++)
          {
            EEPROM.update(_address, data[i]);
	          _address+=1;
          }
}


void setup() 
{
  Serial.begin(9600);
  while (!Serial);




  Serial.println("EEPROM.put() Timing Test");

  data1 = {10, 20, 30, 40};  //the values are in RAM

  //-------- First Write (All bytes written) --------
  t1 = micros();
  EEPROM.put(0, data1);  //10, 20, 30, 40 has entered into EEPROM
  t2 = micros();
  Serial.print("First write time (us) ");
  Serial.println(t2 - t1);

  //-------- Second Write (Same data, should skip writes) --------
  t1 = micros();
  EEPROM.put(0, data1);
  t2 = micros();
  Serial.print("Second write (no change) time (us) ");
  Serial.println(t2 - t1);

  
  

  //-------- Third Write (Only 1 byte should be written) --------
  data1.c = 99; //-------- Modify ONE byte --------
  t1 = micros();
  EEPROM.put(0, data1);
  t2 = micros();
  Serial.print("Third write (1 byte changed) time (us) ");
  Serial.println(t2 - t1);

  //-------- Read back for verification --------
  EEPROM.get(0, data2);

  Serial.print("Read back ");
  Serial.print(data2.a); Serial.print(", ");
  Serial.print(data2.b); Serial.print(", ");
  Serial.print(data2.c); Serial.print(", ");
  Serial.println(data2.d);

   //-------- Fourth Write (Only 2 bytes should be written) --------
  data1.a=100; data1.b=200;
  t1 = micros();
  EepromPut(0, (unsigned char*)&data1, sizeof(data1));  //EEPROM.put(0, data1);
  t2 = micros();
  Serial.print("Fourth write (2 bytes changed) time (us) ");
  Serial.println(t2 - t1);

  //-------- Read back for verification --------
  EEPROM.get(0, data2);

  Serial.print("Read back ");
  Serial.print(data2.a); Serial.print(", ");
  Serial.print(data2.b); Serial.print(", ");
  Serial.print(data2.c); Serial.print(", ");
  Serial.println(data2.d);



  Serial.println("");
  Serial.println("********************************************************");
  Serial.println("");




  Serial.println("Multiple update() Timing Test");
  data1 = {10, 20, 30, 40};  //the values are in RAM

  //-------- First Write (All bytes written) --------
  t1 = micros();
  EepromPut(0, (unsigned char*)&data1, sizeof(data1));  //EEPROM.put(0, data1);  10, 20, 30, 40 has entered into EEPROM
  t2 = micros();
  Serial.print("First write time (us) ");
  Serial.println(t2 - t1);

  //-------- Second Write (Same data, should skip writes) --------
  t1 = micros();
  EepromPut(0, (unsigned char*)&data1, sizeof(data1));  //EEPROM.put(0, data1);
  t2 = micros();
  Serial.print("Second write (no change) time (us) ");
  Serial.println(t2 - t1);

  
  

  //-------- Third Write (Only 1 byte should be written) --------
  data1.c = 99; //-------- Modify ONE byte --------
  t1 = micros();
  EepromPut(0, (unsigned char*)&data1, sizeof(data1));  //EEPROM.put(0, data1);
  t2 = micros();
  Serial.print("Third write (1 byte changed) time (us) ");
  Serial.println(t2 - t1);

  //-------- Read back for verification --------
  EEPROM.get(0, data2);

  Serial.print("Read back ");
  Serial.print(data2.a); Serial.print(", ");
  Serial.print(data2.b); Serial.print(", ");
  Serial.print(data2.c); Serial.print(", ");
  Serial.println(data2.d);

   //-------- Fourth Write (Only 2 bytes should be written) --------
  data1.a=100; data1.b=200;
  t1 = micros();
  EepromPut(0, (unsigned char*)&data1, sizeof(data1));  //EEPROM.put(0, data1);
  t2 = micros();
  Serial.print("Fourth write (2 bytes changed) time (us) ");
  Serial.println(t2 - t1);

  //-------- Read back for verification --------
  EEPROM.get(0, data2);

  Serial.print("Read back ");
  Serial.print(data2.a); Serial.print(", ");
  Serial.print(data2.b); Serial.print(", ");
  Serial.print(data2.c); Serial.print(", ");
  Serial.println(data2.d);
}

void loop() { }









First write time (us) 10376
Second write (no change) time (us) 16
Third write (1 byte changed) time (us) 3468
Read back 10, 20, 99, 40
Fourth write (2 bytes changed) time (us) 6920
Read back 100, 200, 99, 40

********************************************************

Multiple update() Timing Test
First write time (us) 10372
Second write (no change) time (us) 16
Third write (1 byte changed) time (us) 3468
Read back 10, 20, 99, 40
Fourth write (2 bytes changed) time (us) 6924
Read back 100, 200, 99, 40