How Green Is Your Code? 10 Simple Experiments

Two pieces of code can produce exactly the same answer while placing very different demands on a computer.

One version might finish almost instantly. Another might perform thousands of unnecessary calculations, allocate far more memory, transfer more data or keep the processor busy for several extra seconds. When software is used repeatedly or scaled across many devices, those differences begin to matter.

This is where energy-efficient programming becomes practical. Instead of simply memorising a list of energy-efficient coding principles, you can test different approaches and see the effects for yourself.

In this beginner-friendly coding lab, you’ll compare ten common programming choices. You don’t need specialised hardware or advanced knowledge. A computer, Python and a willingness to experiment are enough to get started.

Table of Contents

What Does It Mean for Code to Be Green?

Green code performs a useful task while avoiding unnecessary energy use, hardware demand and carbon emissions.

That does not mean the shortest piece of code is automatically the greenest. A compact line can still trigger a slow algorithm, make excessive network requests or load far more information than the user needs.

The Software Carbon Intensity specification developed by the Green Software Foundation describes three broad ways software can reduce emissions:

  • Energy efficiency: using less electricity to perform the same function
  • Hardware efficiency: making better use of existing physical resources
  • Carbon awareness: running flexible workloads at times or in locations where electricity has a lower carbon intensity

These ideas apply to complete software systems, not just individual lines of code. A program may depend on processors, memory, storage, networks, databases, cloud servers and the user’s device. All of those components can affect its environmental footprint.

If this is a new topic for you, start with the broader guide to green software engineering before attempting the experiments below.

Can You Measure the Energy Used by Code?

Yes, but accurate energy measurement is more complicated than checking how long a program takes to run.

Execution time is a useful starting point because a processor that remains active for longer will often consume more energy. However, runtime alone does not capture everything. Two programs can place different demands on the processor, memory, storage and network, even when they take a similar amount of time.

Professional researchers may use processor energy counters, external power meters, operating-system telemetry or dedicated laboratory equipment. Beginners can start with simpler measurements:

  • Execution time
  • Peak memory use
  • CPU utilisation
  • Number of database or network requests
  • Amount of data transferred
  • Battery use during longer tests

These measurements do not give you a complete carbon footprint, but they can reveal computational waste and help you compare two implementations of the same task.

Set Up a Simple Python Testing Lab

The experiments below use Python because it is approachable and widely used by beginners. You can still apply the underlying lessons to other programming languages.

First, confirm that Python 3 is installed. Then create a new file named green_code_lab.py.

You can measure execution time with Python’s built-in timeit module:

from timeit import timeit

runtime = timeit(
    "sum(range(1_000_000))",
    number=100
)

print(f"Runtime: {runtime:.6f} seconds")

The number value tells Python how many times to repeat the operation. Repetition is important because a tiny task may finish too quickly to measure reliably on its own.

For memory testing, install memory-profiler:

pip install memory-profiler

You can then add the @profile decorator above a function and run the file with:

python -m memory_profiler green_code_lab.py

Keep Your Tests Fair

Small differences in background activity can distort results. For more useful comparisons:

  • Run both versions on the same computer.
  • Close unnecessary applications and browser tabs.
  • Use the same input data for each version.
  • Repeat each test several times.
  • Alternate the order in which you test the versions.
  • Record the median result rather than trusting one run.
  • Confirm that both versions produce the same correct output.

A faster result is meaningless if the supposedly efficient version solves a different problem or produces the wrong answer.

Experiment 1: List Search Versus Set Lookup

Imagine that you have a large collection of usernames and need to check whether a particular name appears in it.

A list searches through its contents until it finds a match. A set uses a hash-based lookup that is generally much faster for repeated membership checks.

Version A: Search a List

usernames = [f"user_{i}" for i in range(1_000_000)]

def find_with_list():
    return "user_999999" in usernames

Version B: Search a Set

usernames = {f"user_{i}" for i in range(1_000_000)}

def find_with_set():
    return "user_999999" in usernames

Time both functions over many repeated lookups.

What to investigate: The set should make repeated membership checks substantially faster, but it may also require more memory. This demonstrates an important green coding principle: optimisation often involves trade-offs rather than one universally superior choice.

A list may still be appropriate when order matters, memory is limited or you only need to scan the data once.

Experiment 2: Recalculating Versus Caching

Programs often repeat the same expensive calculation even though the answer has not changed. Caching stores a previous result so it can be reused.

Version A: Repeat Every Calculation

def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

for _ in range(10):
    print(fibonacci(35))

Version B: Cache Previous Results

from functools import lru_cache

@lru_cache(maxsize=None)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

for _ in range(10):
    print(fibonacci(35))

What to investigate: Compare how long the first call takes with the time required for later calls. The cached function avoids repeating a large amount of work.

Caching can reduce processor use, server load and response times. However, cached information also occupies memory and can become outdated. It is most useful when a result is expensive to produce, requested repeatedly and safe to reuse.

Experiment 3: A Generator Versus a Full List

Suppose you want to process one million numbers but do not need to keep all of them in memory at once.

Version A: Build the Entire List

def squared_numbers_list(limit):
    return [number * number for number in range(limit)]

for value in squared_numbers_list(1_000_000):
    pass

Version B: Produce Values as Needed

def squared_numbers_generator(limit):
    for number in range(limit):
        yield number * number

for value in squared_numbers_generator(1_000_000):
    pass

What to investigate: Measure peak memory use. A generator produces one value at a time rather than creating the entire collection in advance.

This approach can be especially useful when reading large files, processing streams or working with datasets that would otherwise consume a substantial amount of memory.

The list may still be better when you need to reuse its values, access them by position or perform several operations on the complete collection.

Experiment 4: One-Pass Processing Versus Multiple Loops

Readable code matters, and splitting work into several steps can sometimes improve clarity. However, repeatedly scanning a very large collection may also create unnecessary work.

Version A: Scan the Data Three Times

numbers = range(10_000_000)

even_count = sum(1 for number in numbers if number % 2 == 0)
total = sum(numbers)
maximum = max(numbers)

Version B: Collect the Results in One Pass

numbers = range(10_000_000)

even_count = 0
total = 0
maximum = None

for number in numbers:
    total += number

    if number % 2 == 0:
        even_count += 1

    if maximum is None or number > maximum:
        maximum = number

What to investigate: Compare runtime and readability. The one-pass version avoids repeatedly traversing the range, but it is longer and easier to get wrong.

This experiment is a reminder that green software engineering should not produce fragile or incomprehensible code. Maintainability, correctness, security and accessibility still matter.

Experiment 5: Efficient Sorting Versus Bubble Sort

Bubble sort is often taught because it is easy to understand, not because it is an efficient way to sort large datasets.

Version A: Bubble Sort

def bubble_sort(values):
    values = values.copy()

    for end in range(len(values) - 1, 0, -1):
        for index in range(end):
            if values[index] > values[index + 1]:
                values[index], values[index + 1] = (
                    values[index + 1],
                    values[index]
                )

    return values

Version B: Python’s Built-In Sort

def built_in_sort(values):
    return sorted(values)

Create a shuffled list containing 10,000 or more numbers and time both functions.

What to investigate: Increase the size of the dataset gradually. The performance gap should become much more noticeable as the input grows.

Choosing an appropriate algorithm is often more consequential than making tiny improvements to individual lines of code. Before trying clever micro-optimisations, check whether the program is using a fundamentally inefficient method.

Experiment 6: Repeated File Access Versus One Read

Storage operations are usually slower and more resource-intensive than accessing information already held in memory.

Version A: Open the File Repeatedly

def count_keyword_repeatedly(filename, keyword, repeats):
    total = 0

    for _ in range(repeats):
        with open(filename, "r", encoding="utf-8") as file:
            text = file.read()
            total += text.count(keyword)

    return total

Version B: Read Once and Reuse

def count_keyword_once(filename, keyword, repeats):
    with open(filename, "r", encoding="utf-8") as file:
        text = file.read()

    count = text.count(keyword)
    return count * repeats

What to investigate: Compare the difference with a large text file and a high repeat count.

The second example is only valid when the file does not change during the test. In a real application, caching a file indefinitely could cause users to receive stale information. Efficient software still needs a sensible update strategy.

Experiment 7: Many Small Writes Versus Batched Output

Writing information to a file one small piece at a time can create unnecessary input/output operations.

Version A: Write Each Line Separately

def write_separately(filename, lines):
    with open(filename, "w", encoding="utf-8") as file:
        for line in lines:
            file.write(line + "\n")

Version B: Write the Lines Together

def write_in_batch(filename, lines):
    with open(filename, "w", encoding="utf-8") as file:
        file.write("\n".join(lines))

What to investigate: Try both functions with hundreds of thousands of short lines. Measure runtime and peak memory.

Batching may reduce the number of storage or network operations, but very large batches can consume excessive memory or delay useful output. Production systems often use moderate batch sizes rather than collecting everything indefinitely.

Experiment 8: Repeated API Requests Versus Reusing Data

Every web request can involve network equipment, servers, databases and data transfer. Making the same request repeatedly creates work throughout that chain.

For this experiment, use a local test server or a public API that explicitly permits repeated testing. Do not overload someone else’s service.

Version A: Request the Same Data Repeatedly

import requests

def repeated_requests(url):
    results = []

    for _ in range(100):
        response = requests.get(url, timeout=10)
        response.raise_for_status()
        results.append(response.json())

    return results

Version B: Request Once and Reuse

import requests

def reused_request(url):
    response = requests.get(url, timeout=10)
    response.raise_for_status()
    data = response.json()

    return [data for _ in range(100)]

What to investigate: Record the total runtime and estimate the amount of data transferred. Browser developer tools and command-line utilities can help you inspect response sizes.

Real applications need rules governing how long information may be cached. Weather data, stock availability and transport schedules may require frequent updates, while a country list or historical record might remain valid much longer.

Experiment 9: Fetching Everything Versus Only What You Need

Over-fetching happens when an application retrieves more information than it needs to complete a task.

Imagine a database containing a million customer records with dozens of fields.

Version A: Retrieve Complete Records

SELECT *
FROM customers;

Version B: Retrieve the Required Fields

SELECT customer_id, first_name
FROM customers
WHERE newsletter_subscriber = TRUE;

What to investigate: Using a local test database, compare query time, returned data size and application memory use.

Database optimisation can reduce computation, storage reads and network transfer while also making an application feel faster. Useful techniques include selecting only required columns, adding appropriate indexes, paginating large results and avoiding repeated queries inside loops.

You can explore more approachable software in the guide to beginner green coding tools.

Experiment 10: AI-Generated Code Versus an Improved Version

AI coding assistants can produce working programs quickly, but working code is not automatically efficient code.

Research comparing AI-generated solutions with established human-written solutions has found that efficiency varies considerably between models, prompts, languages and programming problems. Some generated solutions perform well, while others use much more time or energy to reach the same answer.

Choose a straightforward task, such as:

  • Finding duplicate values in a list
  • Counting word frequencies in a document
  • Locating two numbers that add to a target
  • Combining overlapping date ranges
  • Finding the most common item in a dataset

Ask an AI assistant to produce a Python solution. Confirm that it works, then inspect it for unnecessary nested loops, repeated calculations, excessive copying or inappropriate data structures.

For example, an AI might generate this duplicate finder:

def find_duplicates(values):
    duplicates = []

    for index, value in enumerate(values):
        if value in values[index + 1:] and value not in duplicates:
            duplicates.append(value)

    return duplicates

A more efficient version could use sets:

def find_duplicates(values):
    seen = set()
    duplicates = set()

    for value in values:
        if value in seen:
            duplicates.add(value)
        else:
            seen.add(value)

    return list(duplicates)

What to investigate: Run both versions with increasingly large input lists. Check whether they produce equivalent results, then compare runtime and memory use.

Do not assume the second version is always better simply because it looks more sophisticated. It may use more memory, and converting the result to a set changes its ordering. The correct choice depends on what the application requires.

Are Some Programming Languages Greener Than Others?

You may have seen charts ranking programming languages by energy consumption. These comparisons can be interesting, but they are easy to misinterpret.

An influential study comparing solutions to a set of programming challenges found large differences between languages. Those findings are often simplified into claims such as “Language A uses many times more energy than Language B.”

However, a programming language does not run by itself. The result can be affected by:

  • The specific program being tested
  • The quality of its implementation
  • The compiler or interpreter
  • The hardware and operating system
  • The number of processor cores in use
  • Memory and storage activity
  • Compiler settings and optimisations
  • The measurement boundary

A more recent analysis argued that, once relevant factors are controlled, the language implementation does not appear to affect energy consumption independently of execution time. In practical terms, the way a program is written and run may matter more than a simplistic ranking of language names.

This does not mean language choice is irrelevant. A language and its ecosystem can make certain algorithms, optimisations and deployment models easier or harder to use. It does mean you should be sceptical of universal claims that one language is always green and another is always wasteful.

Turn Your Results Into a Green Code Scorecard

Create a spreadsheet with one row for each test and the following columns:

ExperimentVersionMedian runtimePeak memoryRequests or operationsOutput correct?Notes
List versus setList
List versus setSet

Calculate the percentage change between your baseline and improved version:

percentage_reduction = (
    (baseline - improved) / baseline
) * 100

For example, if one version takes 10 seconds and another takes 6 seconds:

((10 - 6) / 10) * 100 = 40%

The second version reduces runtime by 40% under those particular test conditions.

Use careful language when discussing your findings. Rather than writing “sets use less energy than lists,” write:

In this membership-check experiment, the set-based version completed the defined workload faster on the tested computer. It also used more memory, so the best choice depends on how the data will be used.

That conclusion is less dramatic, but it is much more accurate.

How to Estimate Carbon Emissions

Once you have an energy estimate, operational carbon emissions can be approximated by multiplying the electricity consumed by the carbon intensity of the electricity supply:

Operational emissions = Energy consumed × Grid carbon intensity

For example, a task that consumes 0.001 kilowatt-hours in a region with a grid intensity of 400 grams of carbon dioxide equivalent per kilowatt-hour would produce an estimated:

0.001 kWh × 400 gCO2e/kWh = 0.4 gCO2e

This figure only covers the boundary you measured. It may exclude networking equipment, cooling, idle capacity, hardware manufacturing and other supporting infrastructure.

The full Software Carbon Intensity method also considers embodied emissions from hardware and expresses emissions per functional unit, such as one user, transaction, API call or completed job.

For a beginner experiment, the most important principle is consistency. Use the same method, computer, workload and assumptions when comparing the baseline and improved versions.

Common Measurement Mistakes

Testing Different Outputs

Both programs must complete the same useful work. A version that processes half the data is not more efficient; it is performing a smaller task.

Relying on One Run

Operating systems perform updates, antivirus scans and background maintenance. One unusually fast or slow result may not represent typical performance.

Ignoring Idle Consumption

Your computer uses electricity before the test begins. When measuring total device power, compare the additional consumption associated with the workload rather than treating all electricity as though the program caused it.

Optimising Tiny Tasks With No Real Impact

Reducing a function from 0.0002 seconds to 0.0001 seconds may be irrelevant if it runs once per month. Focus on code that executes frequently, handles large datasets or operates across many devices.

Sacrificing Readability

Highly compressed or obscure code can create bugs and make future maintenance more difficult. An optimisation that saves a tiny amount of processing but requires repeated rewrites may not improve the system as a whole.

Assuming Faster Always Means Lower Carbon

A faster program may temporarily use more processor cores or specialised hardware. Carbon emissions also depend on where and when electricity is consumed. Runtime is a valuable indicator, but it is not a complete environmental assessment.

Which Optimisations Usually Matter Most?

The most useful improvements are often not clever changes to individual lines. They are larger decisions that eliminate unnecessary work.

Start by asking:

  • Can the program use a more suitable algorithm?
  • Is it repeating a calculation that could be cached?
  • Is it loading or transferring data nobody needs?
  • Does it make avoidable database or API requests?
  • Can inactive services scale down or switch off?
  • Could a flexible task run when lower-carbon electricity is available?
  • Will the software continue working on older hardware?

These questions connect code-level optimisation with the wider principles of sustainable computing.

A Beginner’s Green Coding Checklist

Before finishing a project, check whether you have:

  • Chosen an appropriate algorithm and data structure
  • Removed avoidable repeated calculations
  • Limited database queries and network requests
  • Requested only the data the user needs
  • Measured runtime using repeatable tests
  • Checked memory use as well as speed
  • Tested larger and more realistic inputs
  • Compared equivalent outputs
  • Documented the test environment and assumptions
  • Kept the code understandable and maintainable
  • Tested AI-generated code rather than trusting it automatically
  • Considered the full system, not just one function

Green Coding Begins With Curiosity

You do not need a laboratory to begin thinking more carefully about software efficiency.

Simple experiments can show how algorithms, data structures, caching, file access, network requests and database design affect the work a computer must perform. They can also teach a more important lesson: there is rarely one perfectly green coding choice.

The best solution depends on the task, scale, hardware, users and wider system. A change that reduces runtime may increase memory use. Caching may prevent repeated calculations but serve outdated information. Batching may reduce network operations while delaying results.

Green software development is therefore not about following rigid rules. It is about measuring, comparing and making informed decisions.

Run the experiments. Record what happens. Question surprising results. Then use what you learn to build software that performs useful work with less waste.

Frequently Asked Questions

Does efficient code use less electricity?

Often, but not in every situation. Code that finishes a task faster commonly requires less energy, especially when the same hardware performs the same workload. However, processor utilisation, memory, storage, networking and hardware type can also affect total electricity consumption.

What is the easiest way to measure code efficiency?

Beginners can start by comparing execution time, peak memory use, request counts and transferred data. These metrics do not provide a complete carbon footprint, but they help identify avoidable computational work.

Is Python bad for the environment?

No programming language is environmentally harmful in isolation. Python may be slower than a compiled language for some computational tasks, but software impact depends on the implementation, workload, scale, hardware and deployment. An efficient Python solution may outperform a poorly designed program written in another language.

Is AI-generated code energy efficient?

It can be, but efficiency is not guaranteed. AI assistants may produce unnecessary loops, repeated work or unsuitable data structures. Treat generated code as a draft that must be tested for correctness, security, readability and performance.

Should beginners worry about green coding?

Beginners do not need to optimise every line. Learning to recognise wasteful algorithms, unnecessary requests and repeated calculations builds good habits that improve performance as well as sustainability.