(Also, no, `%5` isn't "horrifically" slow when it's a compile-time-constant modulus: you or the compiler can do it with a multiply and shift for the integer division, and then x%5 = x - (x/5 * 5). https://godbolt.org/g/3HwBrF. It still sucks though.)
I wrote an x86 assembly FizzBuzz for fun a while ago, intended to be an example of how to optimize (https://stackoverflow.com/a/37494090/224132). Some parts of it kind of suck, though. I made some parts efficient (like appending "buzz\n" to a buffer with a mov [rdx], r13 / add rdx, 5), but left in too many conditional branches and a function call instead of inlining everything when unrolling.
I'm glad so many people like my post that the OP linked. It was fun to write. :)
Turns out you can compute x%3 == 0 with even fewer steps, it's (x*0xaaaaaaaab) < 0x55555556 (assuming wrapping unsigned 32 bit arithmetic).
#include <stdio.h>
#include <stdint.h>
#define FIZZ(x) (x & 0x0924)
#define BUZZ(x) (x & 0x0210)
#define FIZZBUZZ(x) (x & 0x4000)
#define NONE(x) (x & 0x34cb)
void fizzbuzz(size_t n_max)
{
uint32_t x = 1;
for (size_t i = 1; i <= n_max; i++)
{
if (FIZZ(x))
printf("fizz\n");
if (BUZZ(x))
printf("buzz\n");
if (FIZZBUZZ(x))
printf("fizzbuzz\n");
if (NONE(x))
printf("%d\n", i);
x <<= 1;
x |= (x >> 15);
}
}
int main(int argc, char **argv)
{
fizzbuzz(100);
}
It's less costly than integer division, and I'm not sure it's what you had in mind. The output here still duplicates the strings.Congrats!
I decided to reject your conditions and replace them with my own, for no apparent reason. Mine has some of the redundancy you wish to eliminate, but the loop body is completely branchless, aside from the call to printf, of course, which we're apparently ignoring.
#include <stdio.h>
#define NUM "\x06" "%zu\n\0"
#define FIZZ "\x07" "Fizz\n\0"
#define BUZZ "\x07" "Buzz\n\0"
const char *x =
NUM
NUM
FIZZ
NUM
BUZZ
FIZZ
NUM
NUM
FIZZ
BUZZ
NUM
FIZZ
NUM
NUM
"\xa6" "FizzBuzz\n";
void fizzbuzz(size_t upTo) {
for(size_t i = 1; i <= upTo; i++) {
printf(x + 1, i);
x += *x;
}
}
int main(int argc, char **argv) {
fizzbuzz(100);
}You did remove a level of indirection for the format-strings, though. You could have done that with
struct state {
int next;
char fmt[6];
};
Anyway, this has probably a 5 cycle loop-carried dependency chain on Skylake, from x += *x; compiling into a 4-cycle latency movsx rax, byte [rdi], then a 1-cycle add rdi, rax. (Or whatever registers the compiler picks).If you'd stored pointers, you could have got it down to 4 cycles on Skylake for the load-use latency of a simple addressing mode ([reg + disp] where displacement is 0..2047).
[0]: http://blog.cdleary.com/2012/11/arm-chars-are-unsigned-by-de...
#include <stdio.h>
#include <stdint.h>
#define F(n) (1 << (2*(n)-2))
#define B(n) (1 << (2*(n)-1))
int main() {
uint32_t m = F(3) | F(6) | F(9) | F(12) | F(15) | B(5) | B(10) | B(15);
int i;
static char t[4][16]= { "%d\n", "Fizz\n", "Buzz\n", "FizzBuzz\n" };
for (i = 1; i <= 100; i++) {
printf(t[m&3], i);
m = (m >> 2) | (m << 28);
}
return 0;
}
Transforming that into an if-based version is left as an exercise for the reader. :) #define FIZZ 1
#define BUZZ 2
static const uint8_t divs[] =
{FIZZ+BUZZ,0,0,FIZZ,0,BUZZ,FIZZ,0,0,FIZZ,BUZZ,0,FIZZ,0,0};
static const char* strs[] = {
[0] = "%u\n",
[FIZZ] = "fizz\n",
[BUZZ] = "buzz\n",
[FIZZ+BUZZ] = "fizzbuzz\n"
};
void fizzbuzz(uint32_t upTo)
{
uint32_t i, state;
for(i = state = 0; i < upTo; i++) {
printf(strs[divs[state]], i);
if(++state == sizeof(divs) / sizeof(*divs))
state = 0;
}
No divisibility tests at allOne level of arrays may be skipped at cost of extra .rodata size (store string pointers directly in divs). But in a modern cpu cost of the extra load is small
String duplication helps speed here.
Branch misprediction will happen once every fifteen times worst case.
Plus, if shaving nanoseconds, printing is a bad idea
#define FIZZ 1
#define BUZZ 2
struct {
uint8_t flags;
uint8_t next;
} static const nfo[] = {
{FIZZ+BUZZ,1},
{0,2},
{0,3},
{FIZZ,4},
{0,5},
{BUZZ,6},
{FIZZ,7},
{0,8},
{0,9},
{FIZZ,10},
{BUZZ,11},
{0,12},
{FIZZ,13},
{0,14},
{0,0}
} __attribute__((align(YOUR_L1D_LINE_SIZE)));
static const char* strs[] = {
[0] = "%u\n",
[FIZZ] = "fizz\n",
[BUZZ] = "buzz\n",
[FIZZ+BUZZ] = "fizzbuzz\n"
};
void fizzbuzz(uint32_t upTo)
{
uint32_t i, state;
for(i = state = 0; i < upTo; i++) {
printf(strs[nfo[state].flags], i);
state = nfo[state].next;
}
}
No divisibility tests at all. No branches besides loop and printf call. Space can be saved by using a bitfield, but masking it will add speed costs. .data: 0
.rodata: 30 + 4 * sizeof(void*) + strings
.text: depending on arch, but not much
Assuming call to printf has no cost and the caches are hot, a modern x86 cpu could execute one iteration of this loop in 1 cycle (issuing 2 loads, one add, one cmp, one branch)I have no compiler and am typing this on a phone so please forgive typos, if any
Also, no, a typical x86 CPU would take about 4 or 5 cycles per iteration even if printf (and the cost of moving its arguments into the right register) was free.
state = nfo[state].next is a pointer-chasing loop-carried dependency chain, so you will bottleneck on L1D load-use latency. (For Skylake, 5 cycles for a complex addressing mode: http://www.7-cpu.com/cpu/Skylake.html).
If out-of-order execution could overlap many of these loops then the throughput could be close to 1 iter per clock.
The classic fizzbuzz will use %3 and %5 operations to test divisibility. As we know from the same source as OP, these are horrifically slow. In addition, the usual approach to fizzbuzz has an annoying duplication, either of the strings or of the predicates.
So, the challenge is, write an optimized fizzbuzz with the following properties: the state for the divisibility testing is a function with a period of 15, which can be calculated in 2 C operations. There are 3 tests for printing, each of the form 'if (...) printf("...");' where each if test is one C operation.
Good luck and have fun!