EEPROM access: speed and logic

After reading through the forum and docs I made some test to access EEPROM and made up my mind how to structure data to be stored. One post stated that EEPROM access works on pages and a page is 32 bytes (I think it was an external EEPROM). I read in the ATmega328 specs that there are 256 pages each 4 bytes large. So I came up with a test for read speed using avr/eeprom.h and eeprom_read_byte/word/dword/block on a 32 byte struct (that's how I will organize my data, 28 bytes data, 4 bytes magic# and link to next block etc., including wear leveling). Somehow all the gathered information does not match the measured access times.

Here is the code used (Arduino 1.0):

void readTest(int nRepeat)
{
  uint8_t nBuf[32];
  uint8_t *pBuf;
  uint32_t nStartTime1, nStartTime2;

  eeprom_busy_wait();
  nStartTime1= micros();
  for(int nLoop= 0; nLoop < nRepeat; nLoop++)
  {
    eeprom_read_block(nBuf, (const void*)(0), 32);
    eeprom_busy_wait();
  }
  nStartTime1= micros() - nStartTime1;

  eeprom_busy_wait();
  nStartTime2= micros();
  for(int nLoop= 0; nLoop < nRepeat; nLoop++)
  {
    pBuf= nBuf;
    for(int nPos= 0; nPos < 32; nPos++)
      _EEGET(*pBuf++, nPos);
    eeprom_busy_wait();
  }
  nStartTime2= micros() - nStartTime2;

  Serial.println(nStartTime1);
  Serial.println(nStartTime2);
}

The eeprom_busy_wait() has no effect and was for testing purposes in the code. The results for 32 rounds are 2320 for eeprom_read_block() and 1436 for eeprom_read_byte(). Reading DWORDs was a bit slower than block read and WORDs got even slower access. Using the EEPROM class brings a bit of overhead (about 400µs). So in the end byte reads are the only way to go, aren't they? Anyone doing something different?

void writeTest()
{
  uint8_t nBuf[32];
  uint8_t *pBuf;
  uint32_t nStartTime1, nStartTime2;
  uint32_t nEndTime1, nEndTime2;

  memset(nBuf, 0, 32);
  eeprom_busy_wait();

  nStartTime1= micros();
  eeprom_write_block(nBuf, (void*)32, 32);
  nEndTime1= micros() - nStartTime1;
  eeprom_busy_wait();
  nStartTime1= micros() - nStartTime1;

  eeprom_busy_wait();
  nStartTime2= micros();
  pBuf= nBuf;
  for(int nPos= 0; nPos < 32; nPos++)
    _EEPUT(nPos, *pBuf++);
  nEndTime2= micros() - nStartTime2;
  eeprom_busy_wait();
  nStartTime2= micros() - nStartTime2;

  Serial.println(nEndTime1);
  Serial.println(nStartTime1);
  Serial.println(nEndTime2);
  Serial.println(nStartTime2);
}

Write results are 103464, 106796, 103516 and 106852. A single byte takes approx. 3.5ms - somebody posted 3.5 to 8ms as normal. This brings me to the point where I should only write data that changed instead of my 32 byte blocks and one byte after another using eeprom_is_ready() and preventing blocking calls to let other tasks work while writing. Is there something I missed? Anyone using eeprom_update_byte() instead of the write? Because I just started using the Arduino I'm looking for any experiences concerning EEPROM usage. Thank you for any input.

Edit: no eeprom_update_byte() in Arduino 1.0 (see avr-libc: <avr/eeprom.h>: EEPROM handling). Seems we are using an older version.

Marek080:
One post stated that EEPROM access works on pages and a page is 32 bytes (I think it was an external EEPROM).

That is not correct for the internal EEPROM. Access is byte-wise.

I read in the ATmega328 specs that there are 256 pages each 4 bytes large.

Wouldn't happen to have been in the section about external programming?

Somehow all the gathered information does not match the measured access times.

The access time for one byte is exactly what is described in the datasheet: load two registers (1 cycle each), fetch the data (1 cycle), 2 cycle pause for a grand total of 5 cycles (0.3125 us). The majority of what your sketch is measuring is the overhead of preparing the call, shuffling data to where it belongs, and looping.

Write results are 103464, 106796, 103516 and 106852. A single byte takes approx. 3.5ms - somebody posted 3.5 to 8ms as normal.

8ms is not normal and would very likely indicate a serious problem with the processor. 3.4ms is the erase + write time; nicely close to the value you measured.

This brings me to the point where I should only write data that changed

A very good idea.

and preventing blocking calls to let other tasks work while writing. Is there something I missed?

You could take a stab at making writes interrupt driven. If your application will be reading from sections that could have pending writes you will have to include a cache.

Edit: no eeprom_update_byte() in Arduino 1.0 (see avr-libc: <avr/eeprom.h>: EEPROM handling). Seems we are using an older version.

Yup. There are instructions for upgrading to a newer version in the forum or you could just steal eeprom_update_byte from a newer version of Libc.

Thank you for the clarifications. So I will update my avr_libc. Because I just started the project and do not know how much features will be necessary in the future I decided to go cooperative multitasking and not to use interrupts (power draw is not a concern that's why polling is ok). A small change to HardwareSerial seems inescapable for realization of non-blocking sends. Didn't want to modify the Arduino 1.0 in the first place...