digitalWrite(somePin, digitalRead(somePin) == LOW ? HIGH : LOW);
with somePin having been placed in OUTPUT pin mode.
Measure the frequency of that pin, it will be half the loop frequency.
With a bit more code, you can get the loop frequency, and by measuring the duty cycle on somePin see how busy your loop is, and how much time is left over you could be doing something more.
For example, I have a neopixel animation that runs at 100 Hz, with a busy period of 80 percent of the 10 milliseconds each frame gets.
So I might not add too much without needing to lower the frame rate.
The clock frequency does directly affect how long it takes the processor to execute an instruction, that is determined by the hardware of the processor itself. As has been pointed out, there is code outside of loop that runs in main(), but there are also other things that will affect timing, such as the interrupt routine for the millis timer, which executes approximately once per millisecond regardless of the clock frequency, as well as background processing if you are sending/receiving data on the serial port (the serial port itself is implemented in hardware, but the code transferring data between the serial hardware and the rx/tx buffers is an interrupt routine). loop() timing will also be greatly affected by overflowing the serial transmit buffer.
Definitly. Arduino gives you at best 1/4 of the performance your uC can do barebones, but you get easier portability. That's why I said "who cares", 'cause when your Arduino is to slow, just get one more beefy.
Once you have loaded up address (of IO port) and value (of bits) registers, you can manipulate the pins in a single instruction ("store value to address", essentially), so you can potentially do things like pin-toggling in a loop very quickly, but there is always that setup of the registers - the minimum code to write something (in isolation) is three instructions (and one of them is probably a memory access that takes more than one cycle.) On top of that, the IO ports are frequently connected to a relatively low-priority bus, and take more than one clock cycle to write, and flash memory is usually slower than the CPU speed so that it may introduce "wait states" for program and data access. (writing cycle-accurate deterministic code for most ARM processors is ... annoying.)
On the bright side, this means that the "penalty" for going through the Arduino functions like digitalWrite() is less than it would be, compared to the difference between an SBI avr instruction and the AVR digitalWrite() - the ARM has no special instructions for writing the IO ports, so it isn't slowed down by the fact that the bits and ports are variables.
A bit of a tangent, but there was a thread on I/O benchmarks (including the abstraction layer) that's a bit hard to find with the new forum software. It includes benchmarks for several ARM MCUs.
The take away, as "westfw" notes above is that ARM I/O can be plenty fast, but implementations with the abstraction layer are all over the place.
"Bit Banding" has mostly gone away, apparently (I hear it didn't interact very well with memory caches and buses, at the hardware level.)
However, GPIO "peripherals" with separate "Set", "Clear", "Toggle" registers that can operate on single bits (or multiple single bits) are very common, and allow for the same desirable Speed and Atomicity behavior.
Even with bit-banding, you had to load the (bit-banded) address, load a 0 or 1 into a data register, and do a store via the address register. The handy "store a constant to a memory address" or even "store a register to a fully specified 32bit address" instructions don't exist in ARM (nor in Cortex-M, anyway.)
Much slower than PIND = bit(2); (that was up around 1.59MHz), but I wanted to see how the R3 & R4 did on more or less the same code and I didn't know off the top of my head how to quickly do a port bit toggle on the R4.
Now this was a little interesting, and I'm not sure I believe it. (Mind you, it's probably got about as much relevance as sport statistics, and we seem to eat them up too!)
R3: 2.65MHz
void setup() {
pinMode(2, OUTPUT);
}
void loop() {
PIND = bit(2);
PIND = bit(2);
}
It doesn't. The compiler is smart enough to optimize the single-bit read/write to the single SBI/CBI instructions. Sure, they take two cycles (on the Uno CPU, anyway), but it's not the same as having separate read/modify/write steps in the program.
Fwiw, there’s a pretty extensive write up on the avr pin toggling methods here: Maximum pin toggle speed - #6 by westfw
Sheesh. 2008. I feel old. (I am old!)
Just on the overhead on the call to loop() each time, I'm betting it was off a lot longer than it was on.
Having slept on it, I guess I don't find the R3 results all that surprising; the frequency when toggling the pin twice in loop() is is in the neighbourhood of double what I saw when toggling the pin once in loop() (and it's in line with what westfw reported back in 2008). But the R4 results are quite a bit slower than I would have expected. I could dig into the assembler code to see what's going on, but to be honest, it really doesn't matter and my interest has passed.