smoothening image boundaries for NxN kernel size - c

In my open source project ( https://github.com/mmj-the-fighter/GraphicsLabFramework ) i am trying to add a image smoothening box filter for NxN kernel size. I have already implemented the algorithm for 3x3 kernel size. As you can see from the source code below I am not processing the image for edges. Using this logic, for a 5x5 kernel size I have to skip two rows or columns from top, right, bottom and left of the image. So the edges will not be blurred. Is there any other solution.
Here is the code:
/*applying a box filter of size 3x3*/
void blur_image(unsigned char *img, int width, int height)
{
int n = width * height;
int i, j;
int r, g, b;
int x, y;
float v = 1.0 / 9.0;
float kernel[3][3] =
{
{ v, v, v },
{ v, v, v },
{ v, v, v }
};
unsigned char* resimage = (unsigned char *)malloc(width * height * 4 * sizeof(unsigned char));
memcpy(resimage, img, width*height * 4);
for (x = 1; x < width - 1; ++x) {
for (y = 1; y < height - 1; ++y) {
float bs = 0.0;
float gs = 0.0;
float rs = 0.0;
for (i = -1; i <= 1; ++i) {
for (j = -1; j <= 1; ++j){
float weight = (float)kernel[i + 1][j + 1];
unsigned char* buffer = img + width * 4 * (y + j) + (x + i) * 4;
bs += weight * *buffer;
gs += weight * *(buffer + 1);
rs += weight * *(buffer + 2);
}
}
unsigned char* outbuffer = resimage + width * 4 * y + x * 4;
*outbuffer = bs;
*(outbuffer + 1) = gs;
*(outbuffer + 2) = rs;
*(outbuffer + 3) = 255;
}
}
memcpy(img, resimage, width*height * 4);
free(resimage);
}

Related

Super-sampling anti-aliasing for a curve via libpng

I tried to smoothen a line via the super-sampling anti-aliasing technique by adding transparency to neighboring pixels. Here is the code in C
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <png.h>
#define WIDTH 512
#define HEIGHT 512
int main()
{
// Image buffer
unsigned char image[HEIGHT][WIDTH][4];
for (int i = 0; i < HEIGHT; i++)
{
for (int j = 0; j < WIDTH; j++)
{
image[i][j][0] = 255;
image[i][j][1] = 255;
image[i][j][2] = 255;
image[i][j][3] = 0;
}
}
// A sample curve
for (double x = -M_PI; x <= M_PI; x += M_PI / WIDTH)
{
int y = (int)(HEIGHT / 2 - sin(x) * cos(x) * HEIGHT / 2);
int i = (int)(x * WIDTH / (2 * M_PI) + WIDTH / 2);
// The anti-aliasing part
int sample = 2; // how far we are sampling
double max_distance = sqrt(sample * sample + sample * sample);
for (int ii = -sample; ii <= sample; ii++)
{
for (int jj = -sample; jj <= sample; jj++)
{
int iii = i + ii;
int jjj = y + jj;
if (iii >= 0 && iii < WIDTH && jjj >= 0 && jjj < HEIGHT)
{
// Here is my question
int alpha = 255 - (int)(100.0 * sqrt(ii * ii + jj * jj) / max_distance);
image[jjj][iii][0] = 0;
image[jjj][iii][1] = 0;
image[jjj][iii][2] = 0;
image[jjj][iii][3] = alpha > image[jjj][iii][3] ? alpha : image[jjj][iii][3];
}
}
}
}
FILE *fp = fopen("curve.png", "wb");
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info = png_create_info_struct(png);
png_init_io(png, fp);
png_set_IHDR(png, info, WIDTH, HEIGHT, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_write_info(png, info);
for (int i = 0; i < HEIGHT; i++)
{
png_write_row(png, (png_bytep)image[i]);
}
png_write_end(png, NULL);
fclose(fp);
return 0;
}
Although it does some smoothing, but the result is far from the available programs. Where did I do wrong?
I tried to calculate the transparency based on the distance of each neighboring pixel from the center of the line:
int alpha = 255 - (int)(100.0 * sqrt(ii * ii + jj * jj) / max_distance);
I used the factor of 100 instead of 255 since we do not need to go deep into full transparency.
The problem is how to set the alpha value for each pixel based on neighboring pixels when the transparency of each neighbor is subject to change according to its neighbors?

what is executed first in c?

I am trying to write a function that applies a sepia filter to the images,
void sepia(int height, int width, RGBTRIPLE image[height][width])
{
for(int i=0; i<height; i++)
{
for(int j=0; j<width; j++)
{
int r = image[i][j].rgbtRed;
int g = image[i][j].rgbtGreen;
int b = image[i][j].rgbtBlue;
image[i][j].rgbtRed = round((0.393 * r) + (0.769 * g) + (0.189 * b));
image[i][j].rgbtGreen = round((0.349 * r) + (0.686 * g) + (0.168 * b));
image[i][j].rgbtBlue = round((0.272 * r) + (0.534 * g) + (0.131 * b));
if(image[i][j].rgbtRed > 255)
{
image[i][j].rgbtRed = 255;
}
if(image[i][j].rgbtGreen > 255)
{
image[i][j].rgbtGreen = 255;
}
if(image[i][j].rgbtBlue > 255)
{
image[i][j].rgbtBlue = 255;
}
}
}
}
when I used this code, it does not raise any error, but the if conditions do not work at all, and the resulting image has many creepy pixels.
but when I used intermediate variables like this code below:
void sepia(int height, int width, RGBTRIPLE image[height][width])
{
for(int i=0; i<height; i++)
{
for(int j=0; j<width; j++)
{
int r = image[i][j].rgbtRed;
int g = image[i][j].rgbtGreen;
int b = image[i][j].rgbtBlue;
int sr = round((0.393 * r) + (0.769 * g) + (0.189 * b));
int sg = round((0.349 * r) + (0.686 * g) + (0.168 * b));
int sb = round((0.272 * r) + (0.534 * g) + (0.131 * b));
if(sr > 255)
{
sr = 255;
}
if(sg > 255)
{
sg = 255;
}
if(sb > 255)
{
sb = 255;
}
image[i][j].rgbtRed = sr;
image[i][j].rgbtGreen = sg;
image[i][j].rgbtBlue = sb;
}
}
}
the if statements worked perfectly.
my question is what is wrong in the first code that makes the if statements don't work?
image[i][j].rgbtRed > 255 is never true as .rgbtRed is 8-bit. Use an int to save the sum.
The structure members are eight-bit unsigned integers. When you assign to them, values over 255 are lost. (They are wrapped modulo 256.)
When you assign to the intermediate int variables, they retain the full values.

Fisheye Raycasting

I'm trying to build simple raycasting game. I'm rendering wall based on light length. Wall size is calculated based on natural log formula.
Problem is fisheye! I tries cos formula but it's not applicable to my architecture.
Here's the code:
void draw_3d(t_cub3d *this, double len, int num_of_ray, double start)
{
double height;
double width;
double x;
double y;
height = 720 / log10(len + 1);
if (height > 720)
height = 720;
width = 1280.0 / 640;
y = (720 - height) / 2;
while (y < height)
{
x = 2 * num_of_ray;
while(x < 2 + width * num_of_ray)
{
my_mlx_pixel_put(this, (int)x, (int)y, 0xfffff);
x++;
}
y++;
}
}
int raycast(t_cub3d *this)
{
double start = this->raycast->pa - this->raycast->fov;
double end = this->raycast->pa + this->raycast->fov;
double ray_p_x;
double ray_p_y;
double len;
int num_of_ray;
num_of_ray = 0;
while (start <= end)
{
ray_p_x = this->raycast->px;
ray_p_y = this->raycast->py;
while (this->gameinfo->map[(int) ray_p_y / 30][(int) ray_p_x / 30] != '1' && this->gameinfo->map[(int) ray_p_y / 30][(int) ray_p_x / 30] != ' ')
{
ray_p_x += cos(start);
ray_p_y += sin(start);
}
len = fabs(this->raycast->px - ray_p_x) / fabs(cos(start));
len *= cos(this->raycast->pa - start);
start += this->raycast->fov / 320;
draw_3d(this, len, num_of_ray, start);
num_of_ray++;
}
mlx_put_image_to_window(this->mlx_info->mlx, this->mlx_info->mlx_win, this->img->img, 0, 0);
return (0);
}
How to fix fisheye distortion if cos formula doesn't apply to my case.
How it looks like
How it looks like

Apply gaussian blur filter on 24/32 bits BMP images in C

I use the following structures to load in memory a bmp image:
// header
typedef struct {
uint16_t type; // Magic identifier
uint32_t size; // File size in bytes
uint16_t reserved1; // Not used
uint16_t reserved2; // Not used
uint32_t offset;
uint32_t header_size; // Header size in bytes
uint32_t width; // Width of the image
uint32_t height; // Height of image
uint16_t planes; // Number of color planes
uint16_t bits; // Bits per pixel
uint32_t compression;// Compression type
uint32_t imagesize; // image size in bytes
uint32_t xresol; // pixels per meter
uint32_t yresol; // pixels per meter
uint32_t ncolours; // nr of colours
uint32_t importantcolours; // important colours
} BMP_Header;
// image details
typedef struct {
BMP_Header header;
uint64_t data_size;
uint64_t width;
uint64_t height;
uint8_t *data;// allocate memory based on width and height
} BMP_Image;
Then I want to apply a gaussian filter over it.
The functions associated is the following:
double gaussianModel(double x, double y, double variance) {
return 1 / (2 * 3.14159 * pow(variance, 2)) * exp(-(x * x + y * y) / (2 * variance * variance));
}
double *generate_weights(int radius, double variance, int bits_nr) {
double *weights = (double*)malloc(sizeof(double) * radius * radius * bits_nr);
double sum = 0;
for (int i = 0; i < radius; i++) {
for (int j = 0; j < radius * bits_nr; j++) {
weights[i * radius * bits_nr + j] = gaussianModel(i - radius / 2, j - radius / 2, variance);
sum += weights[i * radius + j];
}
}
// normalize
for (int i = 0; i < radius * radius; i++)
weights[i] /= sum;
return weights;
}
double getWeightedColorValue(double *w, int len, unsigned int index, unsigned int bits_nr) {
double sum = 0;
for (int i = index; i < len; i+= bits_nr)
sum += w[i];
return sum;
}
BMP_Image* BMP_blur_collapsed(BMP_Image *img, unsigned int bits_nr, unsigned int radius, unsigned int th_number, FILE* f) {
BMP_Image *bluredImg = malloc(sizeof(BMP_Image));
bluredImg->header = img->header;
bluredImg->data_size = (img->header.size - sizeof(BMP_Header)) * bits_nr;
bluredImg->width = img->header.width;
bluredImg->height = img->header.height;
bluredImg->data = malloc(sizeof(uint8_t) * bluredImg->data_size);
for(uint64_t i = 0; i < bluredImg->data_size; ++i)
bluredImg->data[i] = img->data[i];
double variance = 1.94;
double* weights = generate_weights(radius, variance, bits_nr);
uint64_t i, j;
double start, end;
start = omp_get_wtime();
#pragma omp parallel for private(i, j) collapse(2) schedule(static) num_threads(th_number)
for (i = 0; i < img->height - radius; i++) {
for (j = 0; j < img->width * bits_nr - radius - bits_nr * 2; j+= bits_nr ) {
uint64_t ofs = i * img->width * bits_nr + j;
double *distributedColorRed = (double*)malloc(sizeof(double) * radius * radius * bits_nr);
double *distributedColorGreen = (double*)malloc(sizeof(double) * radius * radius * bits_nr);
double *distributedColorBlue = (double*)malloc(sizeof(double) * radius * radius * bits_nr);
for (int wx = 0; wx < radius; wx++) {
for (int wy = 0; wy < radius * bits_nr; wy += bits_nr) {
uint64_t wofs = wx * img->width * bits_nr + wy;
// double currWeight = weights[wx * radius + wy];
distributedColorRed[wofs] = weights[wx * radius + wy] * img->data[ofs];
distributedColorGreen[wofs + 1] = weights[wx * radius + wy + 1] * img->data[ofs + 1];
distributedColorBlue[wofs + 2] = weights[wx * radius + wy + 2] * img->data[ofs + 2];
}
}
bluredImg->data[ofs] = getWeightedColorValue(distributedColorRed, radius * radius * bits_nr, 0, bits_nr);
bluredImg->data[ofs + 1] = getWeightedColorValue(distributedColorGreen, radius * radius * bits_nr, 1, bits_nr);
bluredImg->data[ofs + 2] = getWeightedColorValue(distributedColorBlue, radius * radius * bits_nr, 2, bits_nr);
free(distributedColorRed);
free(distributedColorBlue);
free(distributedColorGreen);
}
}
end = omp_get_wtime();
fprintf(f, "blur collapsed %f \n", end - start);
free(weights);
return bluredImg;
}
Assuming that img is a BMP_Image object, then img->data has stored all the pixels inside it:
img->data[i] is Red
img->data[i + 1] is Green
img->data[i + 2] is Blue
But the output is not the expected one after I use this function.
Your image convolution with filtering coefficients looks weird.
One is because you use the variable bits_nr
in the calculation which makes the indexing complicated.
It will be more comprehensible to handle the color components r, g, and
b separately.
The variable name variance is not appropriate because variance = sigma ** 2.
You don't need to apply the multiplication with 1/sqrt(2*PI)/sigma
in gaussianModel() because it is cancelled in the normalization.
Here is an example of fully compilable code:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#define SIGMA 1.94
#define RADIUS 5
#define INFILE "0Jayx.bmp"
#define OUTFILE "blurred.bmp"
// header
typedef struct __attribute__((__packed__)) {
uint16_t type; // Magic identifier
uint32_t size; // File size in bytes
uint16_t reserved1; // Not used
uint16_t reserved2; // Not used
uint32_t offset;
uint32_t header_size; // Header size in bytes
uint32_t width; // Width of the image
uint32_t height; // Height of image
uint16_t planes; // Number of color planes
uint16_t bits; // Bits per pixel
uint32_t compression;// Compression type
uint32_t imagesize; // image size in bytes
uint32_t xresol; // pixels per meter
uint32_t yresol; // pixels per meter
uint32_t ncolours; // nr of colours
uint32_t importantcolours; // important colours
} BMP_Header;
// image details
typedef struct {
BMP_Header header;
uint64_t width;
uint64_t height;
uint8_t *b;
uint8_t *g;
uint8_t *r;
} BMP_Image;
double gaussianModel(double x, double y, double sigma) {
return 1. / exp(-(x * x + y * y) / (2 * sigma * sigma));
}
double *generate_coeff(int radius, double sigma) {
double *coeff = malloc(sizeof(double) * radius * radius);
double sum = 0;
for (int i = 0; i < radius; i++) {
for (int j = 0; j < radius; j++) {
coeff[i * radius + j] = gaussianModel(i - radius / 2, j - radius / 2, sigma);
sum += coeff[i * radius + j];
}
}
// normalize
for (int i = 0; i < radius * radius; i++)
coeff[i] /= sum;
return coeff;
}
BMP_Image *BMP_blur_collapsed(BMP_Image *img, int radius, double sigma) {
BMP_Image *bimg = malloc(sizeof(BMP_Image));
bimg->header = img->header;
bimg->width = img->header.width;
bimg->height = img->header.height;
bimg->b = malloc(bimg->width * bimg->height);
bimg->g = malloc(bimg->width * bimg->height);
bimg->r = malloc(bimg->width * bimg->height);
int b, g, r;
double *coeff = generate_coeff(radius, sigma);
int i, j, m, n;
for (i = 0; i < img->height - radius; i++) {
for (j = 0; j < img->width - radius; j++) {
b = g = r = 0;
for (m = 0; m < radius; m++) {
for (n = 0; n < radius; n++) {
b += coeff[m * radius + n] * img->b[(i + m) * img->width + (j + n)];
g += coeff[m * radius + n] * img->g[(i + m) * img->width + (j + n)];
r += coeff[m * radius + n] * img->r[(i + m) * img->width + (j + n)];
}
}
bimg->b[i * bimg->width + j] = b;
bimg->g[i * bimg->width + j] = g;
bimg->r[i * bimg->width + j] = r;
}
}
free(coeff);
return bimg;
}
void free_img(BMP_Image *img)
{
free(img->b);
free(img->g);
free(img->r);
free(img);
}
BMP_Image *read_bmp_file(char *filename)
{
FILE *fp;
int i, j, bytesperline;
BMP_Image *img;
img = malloc(sizeof(BMP_Image));
if (NULL == (fp = fopen(filename, "r"))) {
perror(filename);
exit(1);
}
fread(&img->header, sizeof(char), sizeof(BMP_Header), fp);
img->width = img->header.width;
img->height = img->header.height;
bytesperline = ((img->width * 3 + 3) / 4) * 4; // word alignment
img->b = malloc(img->width * img->height);
img->g = malloc(img->width * img->height);
img->r = malloc(img->width * img->height);
for (i = 0; i < img->height; i++) {
for (j = 0; j < img->width; j++) {
img->b[i * img->width + j] = getc(fp);
img->g[i * img->width + j] = getc(fp);
img->r[i * img->width + j] = getc(fp);
}
for (j = img->width * 3; j < bytesperline; j++) {
getc(fp);
}
}
return img;
}
void write_bmp_file(BMP_Image *img, char *filename)
{
FILE *fp;
int i, j, bytesperline;
if (NULL == (fp = fopen(filename, "w"))) {
perror(filename);
exit(1);
}
bytesperline = ((img->width * 3 + 3) / 4) * 4; // word alignment
fwrite(&img->header, sizeof(char), sizeof(BMP_Header), fp);
for (i = 0; i < img->height; i++) {
for (j = 0; j < img->width; j++) {
putc(img->b[i * img->width + j], fp);
putc(img->g[i * img->width + j], fp);
putc(img->r[i * img->width + j], fp);
}
for (j = img->width * 3; j < bytesperline; j++) {
putc(0, fp);
}
}
}
int main()
{
BMP_Image *img = read_bmp_file(INFILE);
BMP_Image *bimg = BMP_blur_collapsed(img, RADIUS, SIGMA);
write_bmp_file(bimg, OUTFILE);
free_img(img);
free_img(bimg);
return 0;
}
The produced image with radius=5, sigma=1.94:
[Edit]
To modify the code to support 32bit bmp file:
remove the bytesperline relevant codes.
append uint8_t *a; to the struct BMP_Image.
find the b, g, r pixel processing codes and append the a processing
thereafter (just by changing the variable name).
If you are familiar with the patch command, below is the patch file
which can be applied with:
patch -u < gauss.diff
to convert my previous source code to the 32bit version.
(You will need to modify the filenames in the 1st and the 2nd line
according to the source filename you work with.)
gauss.diff:
-- gauss.c.O 2021-12-09 08:57:36.504685533 +0900
+++ gauss.c 2021-12-09 09:49:57.342895501 +0900
## -36,6 +36,7 ##
uint8_t *b;
uint8_t *g;
uint8_t *r;
+ uint8_t *a;
} BMP_Image;
double gaussianModel(double x, double y, double sigma) {
## -67,25 +68,28 ##
bimg->b = malloc(bimg->width * bimg->height);
bimg->g = malloc(bimg->width * bimg->height);
bimg->r = malloc(bimg->width * bimg->height);
+ bimg->a = malloc(bimg->width * bimg->height);
- int b, g, r;
+ int b, g, r, a;
double *coeff = generate_coeff(radius, sigma);
int i, j, m, n;
for (i = 0; i < img->height - radius; i++) {
for (j = 0; j < img->width - radius; j++) {
- b = g = r = 0;
+ b = g = r = a = 0;
for (m = 0; m < radius; m++) {
for (n = 0; n < radius; n++) {
b += coeff[m * radius + n] * img->b[(i + m) * img->width + (j + n)];
g += coeff[m * radius + n] * img->g[(i + m) * img->width + (j + n)];
r += coeff[m * radius + n] * img->r[(i + m) * img->width + (j + n)];
+ a += coeff[m * radius + n] * img->r[(i + m) * img->width + (j + n)];
}
}
bimg->b[i * bimg->width + j] = b;
bimg->g[i * bimg->width + j] = g;
bimg->r[i * bimg->width + j] = r;
+ bimg->a[i * bimg->width + j] = a;
}
}
free(coeff);
## -98,13 +102,15 ##
free(img->b);
free(img->g);
free(img->r);
+ free(img->a);
free(img);
}
BMP_Image *read_bmp_file(char *filename)
{
FILE *fp;
- int i, j, bytesperline;
+// int i, j, bytesperline;
+ int i, j;
BMP_Image *img;
img = malloc(sizeof(BMP_Image));
## -117,21 +123,25 ##
img->width = img->header.width;
img->height = img->header.height;
- bytesperline = ((img->width * 3 + 3) / 4) * 4; // word alignment
+// bytesperline = ((img->width * 3 + 3) / 4) * 4; // word alignment
img->b = malloc(img->width * img->height);
img->g = malloc(img->width * img->height);
img->r = malloc(img->width * img->height);
+ img->a = malloc(img->width * img->height);
for (i = 0; i < img->height; i++) {
for (j = 0; j < img->width; j++) {
img->b[i * img->width + j] = getc(fp);
img->g[i * img->width + j] = getc(fp);
img->r[i * img->width + j] = getc(fp);
+ img->a[i * img->width + j] = getc(fp);
}
+/*
for (j = img->width * 3; j < bytesperline; j++) {
getc(fp);
}
+*/
}
return img;
}
## -139,13 +149,14 ##
void write_bmp_file(BMP_Image *img, char *filename)
{
FILE *fp;
- int i, j, bytesperline;
+// int i, j, bytesperline;
+ int i, j;
if (NULL == (fp = fopen(filename, "w"))) {
perror(filename);
exit(1);
}
- bytesperline = ((img->width * 3 + 3) / 4) * 4; // word alignment
+// bytesperline = ((img->width * 3 + 3) / 4) * 4; // word alignment
fwrite(&img->header, sizeof(char), sizeof(BMP_Header), fp);
for (i = 0; i < img->height; i++) {
## -153,10 +164,13 ##
putc(img->b[i * img->width + j], fp);
putc(img->g[i * img->width + j], fp);
putc(img->r[i * img->width + j], fp);
+ putc(img->a[i * img->width + j], fp);
}
+/*
for (j = img->width * 3; j < bytesperline; j++) {
putc(0, fp);
}
+*/
}
}

How can I improve locality of reads and writes in the following code?

I'm working on the following image convolution code:
typedef struct fmatrix{
int rows;
int cols;
float** array;
} fmatrix;
typedef struct image{
unsigned char* data;
int w;
int h;
int c;
} image;
typedef struct kernel{
fmatrix* psf;
int divisor;
} kernel;
void convolve_sq(image* src, image* dst, kernel* psf, int pixel){
int size = psf->psf->rows * psf->psf->cols;
float tmp[size];
int n, m; //for psf
int x, y, x0, y0, cur; //for image
y0 = pixel / (src->w * src->c);
x0 = (pixel / src->c) % src->w;
for (n = 0; n < psf->psf->rows; ++n){
for (m = 0; m < psf->psf->cols; ++m){
y = n - (psf->psf->rows / 2);
x = m - (psf->psf->cols / 2);
if ((y + y0) < 0 || (y + y0) >= src->h || (x + x0) < 0 || (x + x0) >= src->w){
tmp[n*psf->psf->rows+m] = 255 * psf->psf->array[n][m];
}
else{
cur = (pixel + y * src->w * src->c + x * src->c);
tmp[n*psf->psf->rows+m] = src->data[cur] * psf->psf->array[n][m]; //misses on read
}
}
}
m = 0;
for (n = 0; n < size; ++n){
m += (int) tmp[n];
}
m /= psf->divisor;
if (m < 0) m = 0;
if (m > 255) m = 255;
dst->data[pixel] = m; //misses on write
}
void convolve_image(image* src, image* dst, kernel* psf){
int i, j, k;
for (i = 0; i < src->h; ++i){
for (j = 0; j < src->w; ++j){
for (k = 0; k < src->c; ++k){
convolve_sq(src, dst, psf, (i * src->w * src->c + j * src->c + k) );
}
}
}
}
Running cachegrind, I've determined two places where there are a substantial number of cache misses, which I've annotated in the code above. For the line marked "misses on read", there were 97,205 D1mr and 97,201 DLmr. For the line marked "misses on write", there were 97,201 D1mw and DLmw. These lines read and write directly to/from the image respectively.
How can I make this code more efficient, in terms of avoiding cache misses?

Resources