Skip to content

zoopyserg/rails-performance-optimization-example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 

Repository files navigation

Rails 7 Performance improvement using C

Over the past decade, my journey through the realm of large-scale web development has exposed me to a recurring theme: performance bottlenecks that neither Ruby on Rails nor Python can efficiently overcome. These challenges often manifest in projects demanding high-level data processing and management, where the conventional approach of multiplying workers and implementing extensive caching strategies falls short. Faced with this reality, I ventured beyond traditional frameworks, embracing the power of C to address these critical performance issues. My experience has shown that integrating C with Rails can dramatically enhance computational efficiency, transforming how we handle data-intensive operations.

In sharing my approach, my goal is not merely to offer solutions but to demonstrate a pathway to significant performance optimization for large websites. If you find your platform at a crossroads, requiring more than just incremental improvements to meet your growing demands, you're in the right place. I'm here to guide you through leveraging C within your Rails applications, not just as a technique, but as a strategic advantage for those ready to invest in top-tier performance enhancements.

Imagine this code:

class Event < ApplicationRecord
  has_many :attendees
  has_many :activities

  # A heavy operation simulated in Ruby (but better suited for C in this scenario)
  def calculate_carbon_footprint
    footprint = 0
    attendees.each do |attendee|
      footprint += attendee.carbon_footprint
    end
    activities.each do |activity|
      footprint += activity.carbon_footprint
    end
    update(carbon_footprint: footprint)
  end
end

class Attendee < ApplicationRecord
  belongs_to :event

  def carbon_footprint
    # Simplified calculation based on travel mode and distance
    case travel_mode
    when 'car' then distance * 0.24
    when 'plane' then distance * 0.18
    when 'train' then distance * 0.14
    else 0
    end
  end
end

class Activity < ApplicationRecord
  belongs_to :event

  def carbon_footprint
    # Simplified footprint based on activity type
    virtual? ? 10 : 50 # Virtual activities consume less energy
  end
end

You can see that it easily become a performance bottleneck for Rails. For example, if carbon_footprint methods start requiring heavy calculations based on data from many different tables.

That's where C comes in:

// gcc program.c -o program -I/usr/include/postgresql -L/usr/lib -lpq
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libpq-fe.h>

typedef struct {
    int id;
    char travel_mode[10];
    double distance;
} Attendee;

typedef struct {
    int id;
    char activity_type[10];
} Activity;

double calculate_attendee_footprint(Attendee attendee) {
    if (strcmp(attendee.travel_mode, "car") == 0) {
        return attendee.distance * 0.24;
    } else if (strcmp(attendee.travel_mode, "plane") == 0) {
        return attendee.distance * 0.18;
    } else if (strcmp(attendee.travel_mode, "train") == 0) {
        return attendee.distance * 0.14;
    }
    return 0.0;
}

double calculate_activity_footprint(Activity activity) {
    return strcmp(activity.activity_type, "virtual") == 0 ? 10 : 50;
}

void calculate_and_update_event_footprint(PGconn *conn, int event_id) {
    double total_footprint = 0.0;
    PGresult *res;
    int i, nrows;

    // Calculate footprint for attendees
    char query[1024];
    snprintf(query, sizeof(query), "SELECT id, travel_mode, distance FROM attendees WHERE event_id = %d", event_id);
    res = PQexec(conn, query);
    if (PQresultStatus(res) != PGRES_TUPLES_OK) {
        fprintf(stderr, "SELECT attendees failed: %s\n", PQerrorMessage(conn));
        PQclear(res);
        return;
    }

    nrows = PQntuples(res);
    for (i = 0; i < nrows; i++) {
        Attendee attendee = {
            .id = atoi(PQgetvalue(res, i, 0)),
            .distance = atof(PQgetvalue(res, i, 2))
        };
        strncpy(attendee.travel_mode, PQgetvalue(res, i, 1), sizeof(attendee.travel_mode) - 1);
        total_footprint += calculate_attendee_footprint(attendee);
    }
    PQclear(res);

    // Calculate footprint for activities
    snprintf(query, sizeof(query), "SELECT id, activity_type FROM activities WHERE event_id = %d", event_id);
    res = PQexec(conn, query);
    if (PQresultStatus(res) != PGRES_TUPLES_OK) {
        fprintf(stderr, "SELECT activities failed: %s\n", PQerrorMessage(conn));
        PQclear(res);
        return;
    }

    nrows = PQntuples(res);
    for (i = 0; i < nrows; i++) {
        Activity activity = {
            .id = atoi(PQgetvalue(res, i, 0)),
        };
        strncpy(activity.activity_type, PQgetvalue(res, i, 1), sizeof(activity.activity_type) - 1);
        total_footprint += calculate_activity_footprint(activity);
    }
    PQclear(res);

    // Update event with the total carbon footprint
    snprintf(query, sizeof(query), "UPDATE events SET carbon_footprint = %f WHERE id = %d", total_footprint, event_id);
    res = PQexec(conn, query);
    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
        fprintf(stderr, "UPDATE event failed: %s\n", PQerrorMessage(conn));
    }
    PQclear(res);
}

int main(int argc, char *argv[]) {
    // Connect to the database
    const char *conninfo = "dbname=your_database user=your_user password=your_password host=localhost";
    PGconn *conn = PQconnectdb(conninfo);

    if (PQstatus(conn) != CONNECTION_OK) {
        fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(conn));
        PQfinish(conn);
        return 1;
    }

    int event_id = 1; // This would be dynamically determined or passed as a parameter
    calculate_and_update_event_footprint(conn, event_id);

    PQfinish(conn);
    return 0;
}

This C program connects to a PostgreSQL database and calculates the carbon footprint for an event by fetching data from the attendees and activities tables, performing the calculations in C, and updating the event table with the total carbon footprint.

It can be easily called from Ruby like so:

class Event < ApplicationRecord
  has_many :attendees
  has_many :activities

  # Call the C program to calculate the carbon footprint for this event
  def calculate_carbon_footprint_with_c_program
    # Construct the command to run the C program with the event ID as an argument
    command = "./path/to/program #{id}"

    # Execute the command
    system(command)

    # Optionally, reload the event to get the updated carbon footprint from the database
    reload
  end
end

This performance improvement can be significant when dealing with large datasets and complex calculations. For example, when dealing with millions of attendees and hundreds of events on some country-wide event tracking website, and the need to update the total carbon footprint of all events to (for example) rank which categories of events (concerts? conferences? tours?) are the most environmentally friendly.

Ready to elevate your platform’s performance to unparalleled heights? If you’re navigating the complexities of scaling a large website and demand nothing less than exceptional optimization, I’m here to make that transformation a reality. Together, we can break through the barriers of conventional programming limits, unlocking speed, efficiency, and reliability like never before. Let’s discuss how my specialized approach, blending the robustness of Rails with the precision of C, can propel your project forward.

Reach out to embark on a journey towards unmatched web performance. Your website isn’t just another project to me—it’s the next benchmark in web excellence.

About

Making Rails 7 work faster using C.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages