-
Notifications
You must be signed in to change notification settings - Fork 59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make sure conversion is complete; fix ads1015 scaling. #174
base: main
Are you sure you want to change the base?
Conversation
The extra overhead (if any) only shows up when doing tracing, which is NOT the usual use case. The optimizations that are there keep the usual no tracing case as quick as possible.
On a Raspberry Pi 0, the optimizations are not nanoseconds....
On Tuesday, July 4, 2023 at 03:28:45 PM EDT, E. A. (Ed) Graham, Jr. ***@***.***> wrote:
@EAGrahamJr commented on this pull request.
In diozero-core/src/main/java/com/diozero/devices/Ads1x15.java:
@@ -353,6 +356,12 @@ private Ads1x15(int controller, Model model, Address address, PgaConfig pgaConfi
boardPinInfo = new Ads1x15BoardPinInfo(model, pgaConfig.getVoltage());
device = I2CDevice.builder(address.getValue()).setController(controller).setByteOrder(ByteOrder.BIG_ENDIAN)
.build();
+
+ Logger.trace("{}", () -> {
If you don't mind, a few comments..
Your use of functional indirection for logging has a couple of things that I think are a little odd:
- the "calculated values" that get derived from the functional calls actually adds more overhead (creates a class for each, thus must maintain those classes, etc.)
- this indirection (to me) makes the code harder to understand - it takes a little bit to understand that all these values are function calls
- the amTracing flag should be simply amTraceing = Logger.isTracingEnable()
- some optimizations are probably not worth the extra effort that went into producing them - if the code is taking milliseconds to loop, chopping off a few nanoseconds isn't going to matter
In diozero-core/src/main/java/com/diozero/devices/Ads1x15.java:
@@ -464,20 +473,48 @@ private void setDataRate(int dataRate, byte dataRateMask) {
this.dataRateMask = dataRateMask;
}
+ public void waitUntilReady() {
+ long t0 = 0;
+
+ if (amTracing) { // only want to hit currentTimeMillis() if necessary
This is one of the optimizations that just doesn't need to be done - the call to get the current time in millis is negligible in the overall call pattern.
In diozero-core/src/main/java/com/diozero/devices/Ads1x15.java:
+ Logger.trace("waitUntilReady hit");
+ t0 = System.currentTimeMillis();
+ }
+
+ SleepUtil.sleepMillis(dataRateSleepMillis);
+ int incrementalSleepMillis = Math.max(dataRateSleepMillis / 20, 1);
+
+ int i = 0;
+ while (true) {
+ short data = device.readShort(ADDR_POINTER_CONFIG);
+ if ((data & 0x8000) != 0) break;
+ SleepUtil.sleepMillis(incrementalSleepMillis);
+ i++;
+ }
+
+ if (amTracing) {
Another example of nanosecond operations that should just be in-lined.
In diozero-core/src/main/java/com/diozero/devices/Ads1x15.java:
@@ -486,18 +523,16 @@ protected void setConfig(int adcNumber) {
byte config_lsb = (byte) (dataRateMask | comparatorMode.getMask() | comparatorPolarity.getMask()
| (latchingComparator ? CONFIG_LSB_COMP_LATCHING : 0) | comparatorQueue.getMask());
device.writeI2CBlockData(ADDR_POINTER_CONFIG, config_msb, config_lsb);
- Logger.trace("msb: 0x{}, lsb: 0x{}", Integer.toHexString(config_msb & 0xff),
- Integer.toHexString(config_lsb & 0xff));
+ Logger.trace("setConfig: 0x{} 0x{}", () -> Integer.toHexString(config_msb & 0xff),
Use of "setConfig" isn't necessary - the log format should output the class and method (see the tinylog.properties resource file).
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you authored the thread.Message ID: ***@***.***>
|
that being said, I'll do whatever needs to be done to get the fixes integrated....
On Thursday, July 6, 2023 at 09:05:16 PM EDT, Doug Wegscheid ***@***.***> wrote:
The extra overhead (if any) only shows up when doing tracing, which is NOT the usual use case. The optimizations that are there keep the usual no tracing case as quick as possible.
On a Raspberry Pi 0, the optimizations are not nanoseconds....
On Tuesday, July 4, 2023 at 03:28:45 PM EDT, E. A. (Ed) Graham, Jr. ***@***.***> wrote:
@EAGrahamJr commented on this pull request.
In diozero-core/src/main/java/com/diozero/devices/Ads1x15.java:
@@ -353,6 +356,12 @@ private Ads1x15(int controller, Model model, Address address, PgaConfig pgaConfi
boardPinInfo = new Ads1x15BoardPinInfo(model, pgaConfig.getVoltage());
device = I2CDevice.builder(address.getValue()).setController(controller).setByteOrder(ByteOrder.BIG_ENDIAN)
.build();
+
+ Logger.trace("{}", () -> {
If you don't mind, a few comments..
Your use of functional indirection for logging has a couple of things that I think are a little odd:
- the "calculated values" that get derived from the functional calls actually adds more overhead (creates a class for each, thus must maintain those classes, etc.)
- this indirection (to me) makes the code harder to understand - it takes a little bit to understand that all these values are function calls
- the amTracing flag should be simply amTraceing = Logger.isTracingEnable()
- some optimizations are probably not worth the extra effort that went into producing them - if the code is taking milliseconds to loop, chopping off a few nanoseconds isn't going to matter
In diozero-core/src/main/java/com/diozero/devices/Ads1x15.java:
@@ -464,20 +473,48 @@ private void setDataRate(int dataRate, byte dataRateMask) {
this.dataRateMask = dataRateMask;
}
+ public void waitUntilReady() {
+ long t0 = 0;
+
+ if (amTracing) { // only want to hit currentTimeMillis() if necessary
This is one of the optimizations that just doesn't need to be done - the call to get the current time in millis is negligible in the overall call pattern.
In diozero-core/src/main/java/com/diozero/devices/Ads1x15.java:
+ Logger.trace("waitUntilReady hit");
+ t0 = System.currentTimeMillis();
+ }
+
+ SleepUtil.sleepMillis(dataRateSleepMillis);
+ int incrementalSleepMillis = Math.max(dataRateSleepMillis / 20, 1);
+
+ int i = 0;
+ while (true) {
+ short data = device.readShort(ADDR_POINTER_CONFIG);
+ if ((data & 0x8000) != 0) break;
+ SleepUtil.sleepMillis(incrementalSleepMillis);
+ i++;
+ }
+
+ if (amTracing) {
Another example of nanosecond operations that should just be in-lined.
In diozero-core/src/main/java/com/diozero/devices/Ads1x15.java:
@@ -486,18 +523,16 @@ protected void setConfig(int adcNumber) {
byte config_lsb = (byte) (dataRateMask | comparatorMode.getMask() | comparatorPolarity.getMask()
| (latchingComparator ? CONFIG_LSB_COMP_LATCHING : 0) | comparatorQueue.getMask());
device.writeI2CBlockData(ADDR_POINTER_CONFIG, config_msb, config_lsb);
- Logger.trace("msb: 0x{}, lsb: 0x{}", Integer.toHexString(config_msb & 0xff),
- Integer.toHexString(config_lsb & 0xff));
+ Logger.trace("setConfig: 0x{} 0x{}", () -> Integer.toHexString(config_msb & 0xff),
Use of "setConfig" isn't necessary - the log format should output the class and method (see the tinylog.properties resource file).
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you authored the thread.Message ID: ***@***.***>
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the my main issue with the logging is that it does add overhead (each function is an anonymous class which then has a concrete implementation created, which may or may not ever be used).
It also doesn't look like any of the other classes/logging, so you've basically introduced a different style than everything around it.
But that's all up to Matt... 😀
Taking a look - the amTracing instance variable is a little unusual. |
That's kind of common practice to ensure that specific instructions are not run when a method is invoked. In this case, I believe the intent was to avoid any overhead when logging calls are made - e.g. when the Logger.trace("msb: 0x{}, lsb: 0x{}", Integer.toHexString(config_msb & 0xff),
Integer.toHexString(config_lsb & 0xff)); always calls both On the other hand, the PR changes also tries to get around calling "un-necessary" functions through the use of functional interfaces. Logger.trace("setConfig: 0x{} 0x{}", () -> Integer.toHexString(config_msb & 0xff),
() -> Integer.toHexString(config_lsb & 0xff)); But there's even more overhead because
Item 3 is the actual "gotcha" that a lot of people just really don't know about. |
yep, you are correct. Not sure this is a runtime issue.
yep, you are correct. But the class will only be loaded once from the classpath, which is expensive compared to running the constructor. If I had instantiated the Suppliers ONCE and then passed the instantiated objects to the logging calls, that would avoid the "multiple instantiations of the (already loaded) classes" issue that you have called out here. The calls I guarded/avoided with the lambdas are probably not expensive enough to be worth it. So, I removed the lambdas.
just so we all know, however, I'm pretty sure I'm on solid ground on this one. tinylog does not invoke the lazy parameter Supplier<> functions unless the logging is actually taking place. I confirmed this in the source code, and via experimentation (test case pasted below). Deferring the execution of the Supplier<> functions until they are needed is precisely why the tinylog (and log4j2) designers put those signatures in there, but given points 1 and 2 above, using the lambdas is probably appropriate for more expensive operations than the ones we are talking about here. TinylogTest.java:
log4j2.xml:
build.gradle:
Program output:
|
TIL - I didn't know TinyLog took a list of parameter suppliers - yes, you are correct. (I was expecting |
Fixing two ads1x15 issues:
just waiting for an amount of time is not always sufficient. Changed code to wait for the conversion complete bit.
range mapping for ads1015 was not correct.