Closed vovagorodok closed 1 year ago
Thanks for these explanations, I'll try to review this week.
Changing default behavior is probably the cause of the fail of the unit test Please be aware that existing projects depend on earlier defaults.
Fixed CRC64 crc();
was interpreted as function
Changing default behavior is probably the cause of the fail of the unit test Please be aware that existing projects depend on earlier defaults.
Defaults are not the same only for CRC32, for other CRC types defaults were correct
Ahh CRC64_test.ino was not compiled by me because printHelpers.h. Fixed
How you run all CI jobs locally?
I don't => on the github of Arduino-CI there is an installation guide, so it is very well possible you need to install ruby and more (and keep that in sync)
I don't => on the github of Arduino-CI there is an installation guide, so it is very well possible you need to install ruby and more (and keep that in sync)
I don't => on the github of Arduino-CI there is an installation guide, so it is very well possible you need to install ruby and more (and keep that in sync)
I don't => on the github of Arduino-CI there is an installation guide, so it is very well possible you need to install ruby and more (and keep that in sync)
GitHub is a bit laggy today. Works locally. Forgot about mask 0x0FFF
in CRC12::getCRC()
.
Tests passed. Commit is pushed but not processed it github yet
Opened questions:
getCRC()
methods to calc()
or calculate()
crc..()
functions to calcCRC..()
reverse8
to reverse8bits
. Or move names back? Or other names?yield..
functionality. Or maybe rename yieldAdd(array, length, yieldPeriod)
to add(array, length, yieldPeriod)
and remove add(array, length)
?Rename getCRC() methods to calc() or calculate()
I prefer calc()
over calculate()
Rename crc..() functions to calcCRC..()
I like the short CRC..() function name but you want it to be more distinct from the class name? am I correct? No real problem with it.
Rename reverse8 to reverse8bits. Or move names back? Or other names?
OK, reverseXXbits is more descriptive The link to the bitHelper library gets lost a bit.
Note: in my libraries thus far I introduced new function names side by side to the old as depreciated interface. So people get time to get used to the new ones. Too much work here.
(other answers wil come asap)
Separate yield.. functionality. Or maybe rename yieldAdd(array, length, yieldPeriod) to add(array, length, yieldPeriod) and remove add(array, length)?
Can you explain why you want the yield() function configurable per loop iteration ? The name yieldPeriod sounds like a duration of time, while one configures # loops. This is only to be understood by expert users.
Code wise:
void CRC12::yieldAdd(const uint8_t *array, size_t length, size_t yieldPeriod)
{
while (length--)
{
add(*array++);
if ((_count % yieldPeriod) == 0) yield();
}
}
The modulo is expensive and executed for every byte. I would propose a simple (countdown) counter.
void CRC12::yieldAdd(const uint8_t *array, size_t length, size_t yieldPeriod)
{
size_t _period = yieldPeriod;
while (length--)
{
add(*array++);
if (--period == 0) // subtraction to remove expensive modulo
{
yield();
period = yieldPeriod
}
}
}
Another thought:
Breaking the add() function in two (one with one without yield) makes code more confusing imho. Could be solved by making yieldPeriod default zero if one does not need yield()?
// give yieldPeriod a default value of 0 for no yield
// should be done in the CRC12.h file of course but to get the idea
void CRC12::add(const uint8_t *array, size_t length, size_t yieldPeriod = 0)
{
size_t period = yieldPeriod;
while (length--)
{
add(*array++);
if (yieldPeriod == 0) continue; // skip yield code if period = 0;, compare to zero is fast.
if (--period == 0)
{
yield();
period = yieldPeriod
}
}
}
Recall in my original code I have the variable _canYield and when this is never set to true, the compiler optimizes the yield block away.
This topic: https://github.com/RobTillaart/CRC/pull/32#discussion_r1188670099
CRC16-CCITT 0xFFFF or 0x0000 ?
To support both create a CRC16 _CCITT and CRC16_CCITT_FALSE in the CRCparameters Think that is the best solution.
And yes there is discussion about it on the net, users are free to choose their parameters in the end,
you could change the authors part in library.json as you are spending serious time on this lib.
"authors":
[
{
"name": "Rob Tillaart",
"email": "Rob.Tillaart@gmail.com",
"maintainer": true
}
]
==>
"authors":
[
{
"name": "Rob Tillaart",
"email": "Rob.Tillaart@gmail.com",
"maintainer": true
},
{
"name": "vovagorodok",
}
]
(working on some other projects this week, so time is limited)
Note: in my libraries thus far I introduced new function names side by side to the old as depreciated interface. So people get time to get used to the new ones. Too much work here.
Added deprecated functions/mathods
Can you explain why you want the yield() function configurable per loop iteration ? The name yieldPeriod sounds like a duration of time, while one configures # loops. This is only to be understood by expert users.
No need to add yield logic for void add(uint8_t value);
because it can be easly solved externally when needed.
Period doesn't sounds for me like a duration of time. Can rename to something else if you prefer.
The modulo is expensive and executed for every byte. I would propose a simple (countdown) counter.
Good point. Done.
Breaking the add() function in two (one with one without yield) makes code more confusing imho. Could be solved by making yieldPeriod default zero if one does not need yield()?
Decided to just rename yieldAdd
to add
instead default yieldPeriod in order to avoid additional logic in add
method
Looks more clear now. Please take a look.
Recall in my original code I have the variable _canYield and when this is never set to true, the compiler optimizes the yield block away.
Not sure. enable/disableYield()
are public methods and can be changed from another TU (Translation Unit). That why compliler does not optimize it.
Dont know if such optimizations can be done by linker with LTO (Link time optimization).
To support both create a CRC16 _CCITT and CRC16_CCITT_FALSE in the CRCparameters Think that is the best solution.
Done
you could change the authors part in library.json as you are spending serious time on this lib.
Done. Thanks!
What about version? 0.4.0?
What about version? 0.4.0?
0.4.0 is OK, Given the amount of changes 1.0.0 could be better.
ok, I'll change it
Version number should also be updated in
Done. I have a question. Why we need both library. files? Think about this in my libraries, should I have only .json or both there
I intend to add CMakeLists.txt
files too in the future (low prio)
@RobTillaart any updates?
Sorry too busy with other tasks that have priority.
Found a few hours to do a comparison test with ESP32 and UNO using the CRC_performance.ino example. (IDE 1.8.19)
For ESP32 things look just fine, UNO (AVR) pays a serious penalty. Opinion?
Version | Size / vars |
---|---|
0.3.3 | 5184 / 512 |
1.0.0 | 6018 / 512 |
Diff | 830 / 0 |
The 1.0.0 version has a larger footprint (for UNO this is substantial). As the sketch uses 4 CRC's it is not representative, but even 1/4 == 200+ bytes is quite a lot for an UNO.
algo | output 0.3.3 | output 1.0.0 | time 0.3.3 | time 1.0.0 | delta % |
---|---|---|---|---|---|
CRC8 | 2 | 2 | 412 | 800 | +94% |
CRC8 | 3E | 3E | 580 | 964 | +66% |
CRC16 | 6E99 | 6E99 | 1016 | 1120 | +10% |
CRC16 | 7480 | 7480 | 1208 | 1288 | +07% |
CRC32 | 50FD78E4 | 50FD78E4 | 760 | 1824 | +140% |
CRC32 | 1D73421 | 1D73421 | 992 | 2004 | +102% |
CRC64 | BFF3AA1C182ABDB1 | BFF3AA1C182ABDB1 | 5084 | 7380 | +45% |
CRC64 | 451DF73FA9BFFA5A | 451DF73FA9BFFA5A | 5176 | 7608 | +47% |
algo | output 0.3.3 | output 1.0.0 | time 0.3.3 | time 1.0.0 | delta % |
---|---|---|---|---|---|
CRC8 | 2 | 2 | 66 | 65 | -01% |
CRC8 | 3E | 3E | 61 | 61 | +00% |
CRC16 | 6E99 | 6E99 | 61 | 61 | +00% |
CRC16 | 7480 | 7480 | 66 | 66 | +00% |
CRC32 | 50FD78E4 | 50FD78E4 | 63 | 63 | +00% |
CRC32 | 1D73421 | 1D73421 | 67 | 66 | -01% |
CRC64 | BFF3AA1C182ABDB1 | BFF3AA1C182ABDB1 | 81 | 80 | -01% |
CRC64 | 451DF73FA9BFFA5A | 451DF73FA9BFFA5A | 91 | 91 | +00% |
Hmm, only one serious difference I see. Perhaps it's an issue. In 1.0.0 we use common type for length/size size_t
(perhaps uint32_t
everywhere). Before was uint16_t
. Perhaps for UNO (8bit architecture) is hard to calculate 32bit values.
What if use custom type for size:
#if defined(CRC_CUSTOM_SIZE)
using crc_size_t = CRC_CUSTOM_SIZE;
#elif defined(__AVR__)
using crc_size_t = uint16_t;
#else
using crc_size_t = size_t;
#endif
?
For binary size of UNO perhaps compiler/linker for avr doesn't optimize code as gcc for esp32. We can remove FastCRC32 and check if binary size become less
For binary size of UNO perhaps compiler/linker for avr doesn't optimize code as gcc for esp32. We can remove FastCRC32 and check if binary size become less
Manually removed the FastCRC32 gave the same binary size. So that works well.
No time today to do the size_t test, maybe later this week. Yes AVR is 16 bit intern, so yes it can be the cause.
Let's check it. I'll prepare commit with crc_size_t today
Manually removed the FastCRC32 gave the same binary size. So that works well.
I see. We left a lot of deprecated functions and methods:
[[deprecated("Use reverse8bits() instead")]] uint8_t reverse8(uint8_t in);
[[deprecated("Use reverse12bits() instead")]] uint16_t reverse16(uint16_t in);
[[deprecated("Use reverse16bits() instead")]] uint16_t reverse12(uint16_t in);
[[deprecated("Use reverse32bits() instead")]] uint32_t reverse32(uint32_t in);
[[deprecated("Use reverse64bits() instead")]] uint64_t reverse64(uint64_t in);
[[deprecated("Use calcCRC8() instead")]]
uint8_t crc8(...)
and for 12, 16..
[[deprecated("Use calc() instead")]]
uint64_t getCRC() const;
[[deprecated("Use setInitial() instead")]]
void setStartXOR(uint64_t initial) { _initial = initial; }
[[deprecated("Use setXorOut() instead")]]
void setEndXOR(uint64_t xorOut) { _xorOut = xorOut; }
[[deprecated("Use getInitial() instead")]]
uint64_t getStartXOR() const { return _initial; }
[[deprecated("Use getXorOut() instead")]]
uint64_t getEndXOR() const { return _xorOut; }
[[deprecated("Use add() with yieldPeriod instead")]]
void enableYield() const {}
[[deprecated("Use add() without yieldPeriod instead")]]
void disableYield() const {}
and for 12, 16..
That why binary size is higher. After removing them I think binary size should be less even than 0.3.3, because we reuse a lot of code in 1.0.0
In stddef.h of UNO:
#ifndef __SIZE_TYPE__
#define __SIZE_TYPE__ long unsigned int
#endif
#if !(defined (__GNUG__) && defined (size_t))
typedef __SIZE_TYPE__ size_t;
Quick test in my coffee break, disabled the deprecated functions and patched the test
6018 ==> 5894 versus 5184 (0.3.3)
I think that for CRC.h
will be a bit higher because we use algorithms from classes inside functions and class is linked when we use a function.
Binary size will be lower than 0.3.3 when we use functions and classes.
For this moment cant find solution to not duplicate code and not reuse classes. Have an idea?
Not sure if we should consider CRC_reference_test result for that, because it uses all possible CRC functions in one test (binary). For CRC32_test binary size is 2450 (library overhaed ~2000 bytes) for ver 1.0.0. CRC32_test for ver 0.3.3 has binary size 2708
Quick check latest version => CRC16 has improved a lot, others are (nearly)identical to previous 1.0.0
Good step forward
CRC_performance.ino
Calculating CRC of 100 bytes message
CRC8: 2
TIME: 800
CRC8: 3E
TIME: 964
=============================
CRC16: 6E99
TIME: 552 (was 1120)
CRC16: 7480
TIME: 724 (was 1288)
=============================
CRC32: 50FD78E4
TIME: 1820
CRC32: 1D73421
TIME: 2004
=============================
CRC64: BFF3AA1C182ABDB1
TIME: 7384
CRC64: 451DF73FA9BFFA5A
TIME: 7616
=============================
Next possible issue that I see:
void CRC32::add(const uint8_t *array, crc_size_t length)
{
while (length--)
{
add(*array++);
}
}
We call add() each time instead of direct calculations in 0.3.3. Method calling can cost
Maybe change:
void CRC32::add(uint8_t value)
{
_count++;
if (_reverseIn) value = reverse8bits(value);
_crc ^= ((uint32_t)value) << 24;
for (uint8_t i = 8; i; i--)
{
if (_crc & (1UL << 31))
{
_crc <<= 1;
_crc ^= _polynome;
}
else
{
_crc <<= 1;
}
}
}
as inline function?
Lets prepare small check before. Changed reverse8bits
as inline
I bought a board in order to check. Changing functions to inline doesn't help.
But in function uint32_t calcCRC32(...)
when was changed from:
CRC32 crc(polynome, initial, xorOut, reverseIn, reverseOut);
crc.add(array, length);
return crc.calc();
to:
CRC32 crc(polynome, initial, xorOut, reverseIn, reverseOut);
while (length--)
{
crc.add(*array++);
}
return crc.calc();
calculation time was decreased from:
=============================
CRC32: 50FD78E4
TIME: 1820
CRC32: 1D73421
TIME: 2004
to:
=============================
CRC32: 50FD78E4
TIME: 776
CRC32: 1D73421
TIME: 948
Hmm, crc.add(array, length);
does the same job (only while with length decrementation). Don't know what happens for this moment. AVR compiler issue?
Could be an AVR issue, I expect to (squash and) merge your work coming weeks, there might be some loose ends but those can be handled separately. Merging will get more feedback from other users, which is important.
Lots of work piles up, so sorry this one does not get attention. I might do an update to move the sources to a src folder. That allows an easier compare of the sum of all your commits against the last version.
What was changed:
CrcParameters.h
where all CRC parameters will be stored instead only polynomes. Reused this param constants everywhere.CRC32
class andcrc32
func now use default CRC32 paramsyieldAdd()
methods andyieldCrc..()
functions instead yeald by defaultcrc..()
functions now use the same codebase as classesFastCRC32
based on lookup table for default CRC32Proposed to change:
getCRC()
methods tocalc()
orcalculate()
crc..()
functions tocalcCRC..()