Page Faults in LRU | Implementation
Last Updated :
06 Feb, 2024
LRU uses the concept of paging for memory management, a page replacement algorithm is needed to decide which page needs to be replaced when the new page comes in. Whenever a new page is referred to and is not present in memory, the page fault occurs and the Operating System replaces one of the existing pages with a newly needed page. LRU is one such page replacement policy in which the least recently used pages are replaced.
For example:
Given a sequence of pages in an array of pages[] of length N and memory capacity C, find the number of page faults using the Least Recently Used (LRU) Algorithm.
Example-1 :
Input : N = 7, C = 3
pages = {1, 2, 1, 4, 2, 3, 5}
Output : 5
Explanation :
Capacity is 3, thus, we can store maximum 3 pages at a time.
Page 1 is required, since it is not present,
it is a page fault: page fault = 1
Page 2 is required, since it is not present,
it is a page fault: page fault = 1 + 1 = 2
Page 1 is required, since it is present,
it is not a page fault: page fault = 2 + 0 = 2
Page 4 is required, since it is not present,
it is a page fault: page fault = 2 + 1 = 3
Page 2 is required, since it is present,
it is not a page fault: page fault = 3 + 0 = 3
Page 3 is required, since it is not present,
it replaces LRU page 2: page fault = 3 + 1 = 4
Page 5 is required, since it is not present,
it replaces LRU page 1: page fault = 4 + 1 = 5
Example-2 :
Input : N = 9, C = 4
Pages = {5, 0, 1, 3, 2, 4, 1, 0, 5}
Output : 8
Explanation :
Capacity is 4, thus, we can store maximum 4 pages at a time.
Page 5 is required, since it is not present,
it is a page fault: page fault = 1
Page 0 is required, since it is not present,
it is a page fault: page fault = 1 + 1 = 2
Page 1 is required, since it is not present,
it is a page fault: page fault = 2 + 1 = 3
Page 3 is required, since it is not present,
it is a page fault: page fault = 3 + 1 = 4
Page 2 is required, since it is not present,
it replaces LRU page 5: page fault = 4 + 1 = 5
Page 4 is required, since it is not present,
it replaces LRU page 0: page fault = 5 + 1 = 6
Page 1 is required, since it is present,
it is not a page fault: page fault = 6 + 0 = 6
Page 0 is required, since it is not present,
it replaces LRU page 3: page fault = 6 + 1 = 7
Page 5 is required, since it is not present,
it replaces LRU page 2: page fault = 7 + 1 = 8
Algorithm :
step-1 : Initialize count as 0.
step-2 : Create a vector / array of size equal to memory capacity.
step-3 : Traverse elements of pages[]
step-4 : In each traversal:
if(element is present in memory):
remove the element and push the element at the end
else:
if(memory is full) remove the first element
Increment count
push the element at the end
Implementation of the algorithm :
Following is the implementation of the algorithm in C++ as follows.
C++
#include <bits/stdc++.h>
using namespace std;
int pageFaults( int n, int c, int pages[])
{
int count = 0;
vector< int > v;
int i;
for (i = 0; i <= n - 1; i++) {
auto it = find(v.begin(), v.end(), pages[i]);
if (it == v.end()) {
if (v.size() == c) {
v.erase(v.begin());
}
v.push_back(pages[i]);
count++;
}
else {
v.erase(it);
v.push_back(pages[i]);
}
}
return count;
}
int main()
{
int pages[] = { 1, 2, 1, 4, 2, 3, 5 };
int n = 7, c = 3;
cout << "Page Faults = " << pageFaults(n, c, pages);
return 0;
}
|
Java
import java.util.*;
class GFG {
public static void main(String[] args)
{
int pages[] = new int [] { 1 , 2 , 1 , 4 , 2 , 3 , 5 };
int n = 7 , c = 3 ;
System.out.println( "Page Faults = "
+ pageFaults(n, c, pages));
}
static int pageFaults( int N, int C, int pages[])
{
Queue<Integer> q = new LinkedList<>();
int i = 0 , c = 0 ;
while (i < N)
{
if (q.isEmpty() || !q.contains(pages[i]))
{
if (q.size()== C)
q.poll();
q.add(pages[i]);
c++;
}
else
{
q.remove(pages[i]);
q.add(pages[i]);
}
i++;
}
return c;
}
}
|
Python3
def pageFaults(n, c, pages):
count = 0
v = []
for i in range (n):
if pages[i] not in v:
if len (v) = = c:
v.pop( 0 )
v.append(pages[i])
count + = 1
else :
v.remove(pages[i])
v.append(pages[i])
return count
pages = [ 1 , 2 , 1 , 4 , 2 , 3 , 5 ]
n = 7
c = 3
print ( "Page Faults =" , pageFaults(n, c, pages))
|
C#
using System;
using System.Collections.Generic;
namespace pageFaults {
class Program
{
static int pageFaults( int n, int c, int [] pages)
{
int count = 0;
List< int > v = new List< int >();
int i;
for (i = 0; i <= n - 1; i++) {
int it = v.IndexOf(pages[i]);
if (it == -1) {
if (v.Count == c) {
v.RemoveAt(0);
}
v.Add(pages[i]);
count++;
}
else {
v.RemoveAt(it);
v.Add(pages[i]);
}
}
return count;
}
static void Main( string [] args)
{
int [] pages = { 1, 2, 1, 4, 2, 3, 5 };
int n = 7, c = 3;
Console.WriteLine( "Page Faults = "
+ pageFaults(n, c, pages));
}
}
}
|
Javascript
function pageFaults(n, c, pages) {
let count = 0;
let memory = [];
for (let i = 0; i < n; i++) {
let index = memory.indexOf(pages[i]);
if (index === -1) {
if (memory.length === c) {
memory.shift();
}
memory.push(pages[i]);
count++;
} else {
memory.splice(index, 1);
memory.push(pages[i]);
}
}
return count;
}
let pages = [1, 2, 1, 4, 2, 3, 5];
let n = 7;
let c = 3;
console.log(`Page Faults = ${pageFaults(n, c, pages)}`);
|
Output :
Page Faults = 5
Time Complexity: O(N*C)
Auxiliary Space: O(C)
Approach using Doubly Linked List and Maps
We can use an unordered map and a doubly linked list to solve this problem efficiently. This is done by maintaining a map of nodes in memory. For each recently used node we can push it at the back of our doubly linked list, it consumes O(1) time. Searching in the map also takes O(1) time (NO HASH COLLISIONS ASSUMED). When we hit full capacity in memory, shift the head of the linked list and erase its occurrence from the map. This also can be done in O(1) time. Thus, giving the algorithm a runtime of O(N) in the worst case.
Follow the following steps to implement the idea
1) Construct the structure of the node of the doubly linked list. This contains data, previous and next pointers.
2) Start iterating in the array/stream of inputs. If the data is already in the map, this means its in memory. Find the pointer which is against the data and place it at the end of the linked list, signifying it was recently accessed with necessary linkages.
3) If the data is not in the map, place it at the end of the linked list with necessary linkages. And insert the data in the map with its node pointer. Also increment the page fault, since its not in the memory
4) Return the number of page faults.
C++
#include <bits/stdc++.h>
using namespace std;
struct Node {
int data;
Node* next = nullptr;
Node* prev = nullptr;
Node( int data) {
this ->data = data;
}
};
unordered_map< int , Node*> mpp;
int size = 0;
Node* head = nullptr;
Node* tail = nullptr;
int pageFaults( int N, int C, int pages[]) {
int faults = 0;
for ( int i = 0; i < N; i++) {
if (mpp.find(pages[i]) == mpp.end()) {
faults++;
if (size == C) {
mpp.erase(head->data);
head = head->next;
size--;
}
Node* newNode = new Node(pages[i]);
if (head == nullptr) {
head = newNode;
tail = head;
}
newNode->prev = tail;
tail->next = newNode;
tail = tail->next;
size++;
mpp[pages[i]] = newNode;
}
else {
Node* ptr = mpp[pages[i]];
if (ptr == head) {
head = head->next;
ptr->prev = tail;
tail->next = ptr;
tail = tail->next;
}
else if (tail != ptr) {
ptr->prev->next = ptr->next;
ptr->next->prev = ptr->prev;
ptr->prev = tail;
tail->next = ptr;
tail = tail->next;
}
}
}
return faults;
}
int main(){
int pages[] = { 1, 2, 1, 4, 2, 3, 5 };
int n = 7, c = 3;
cout << "Page Faults = " << pageFaults(n, c, pages);
return 0;
}
|
Java
import java.util.HashMap;
import java.util.Map;
class Node {
int data;
Node next = null ;
Node prev = null ;
public Node( int data) {
this .data = data;
}
}
public class OptimalPageReplacement {
static Map<Integer, Node> mpp = new HashMap<>();
static int size = 0 ;
static Node head = null ;
static Node tail = null ;
static int pageFaults( int N, int C, int [] pages) {
int faults = 0 ;
for ( int i = 0 ; i < N; i++) {
if (!mpp.containsKey(pages[i])) {
faults++;
if (size == C) {
mpp.remove(head.data);
head = head.next;
size--;
}
Node newNode = new Node(pages[i]);
if (head == null ) {
head = newNode;
tail = head;
}
newNode.prev = tail;
tail.next = newNode;
tail = tail.next;
size++;
mpp.put(pages[i], newNode);
} else {
Node ptr = mpp.get(pages[i]);
if (ptr == head) {
head = head.next;
ptr.prev = tail;
tail.next = ptr;
tail = tail.next;
} else if (tail != ptr) {
ptr.prev.next = ptr.next;
ptr.next.prev = ptr.prev;
ptr.prev = tail;
tail.next = ptr;
tail = tail.next;
}
}
}
return faults;
}
public static void main(String[] args) {
int [] pages = { 1 , 2 , 1 , 4 , 2 , 3 , 5 };
int n = 7 , c = 3 ;
System.out.println( "Page Faults = " + pageFaults(n, c, pages));
}
}
|
Python3
class Node:
def __init__( self , data):
self .data = data
self . next = None
self .prev = None
def page_faults(N, C, pages):
size = 0
head = None
tail = None
faults = 0
mpp = {}
for i in range (N):
if pages[i] not in mpp:
faults + = 1
if size = = C:
del mpp[head.data]
head = head. next
size - = 1
new_node = Node(pages[i])
if head is None :
head = new_node
tail = head
new_node.prev = tail
tail. next = new_node
tail = tail. next
size + = 1
mpp[pages[i]] = new_node
else :
ptr = mpp[pages[i]]
if ptr = = head:
head = head. next
ptr.prev = tail
tail. next = ptr
tail = tail. next
elif tail ! = ptr:
ptr.prev. next = ptr. next
ptr. next .prev = ptr.prev
ptr.prev = tail
tail. next = ptr
tail = tail. next
return faults
if __name__ = = "__main__" :
pages = [ 1 , 2 , 1 , 4 , 2 , 3 , 5 ]
n = 7
c = 3
print ( "Page Faults =" , page_faults(n, c, pages))
|
C#
using System;
using System.Collections.Generic;
class LRUCache
{
class Node
{
public int Data;
public Node Next = null ;
public Node Prev = null ;
public Node( int data)
{
Data = data;
}
}
static Dictionary< int , Node> cacheMap = new Dictionary< int , Node>();
static int size = 0;
static Node head = null ;
static Node tail = null ;
static int PageFaults( int N, int C, int [] pages)
{
int faults = 0;
for ( int i = 0; i < N; i++)
{
if (!cacheMap.ContainsKey(pages[i]))
{
faults++;
if (size == C)
{
cacheMap.Remove(head.Data);
head = head.Next;
size--;
}
Node newNode = new Node(pages[i]);
if (head == null )
{
head = newNode;
tail = head;
}
newNode.Prev = tail;
tail.Next = newNode;
tail = tail.Next;
size++;
cacheMap[pages[i]] = newNode;
}
else
{
Node ptr = cacheMap[pages[i]];
if (ptr == head)
{
head = head.Next;
ptr.Prev = tail;
tail.Next = ptr;
tail = tail.Next;
}
else if (tail != ptr)
{
ptr.Prev.Next = ptr.Next;
ptr.Next.Prev = ptr.Prev;
ptr.Prev = tail;
tail.Next = ptr;
tail = tail.Next;
}
}
}
return faults;
}
static void Main()
{
int [] pages = { 1, 2, 1, 4, 2, 3, 5 };
int n = 7, c = 3;
Console.WriteLine( "Page Faults = " + PageFaults(n, c, pages));
}
}
|
Javascript
class Node {
constructor(data) {
this .data = data;
this .next = null ;
this .prev = null ;
}
}
const mpp = new Map();
let size = 0;
let head = null ;
let tail = null ;
function pageFaults(N, C, pages) {
let faults = 0;
for (let i = 0; i < N; i++) {
if (!mpp.has(pages[i])) {
faults++;
if (size === C) {
mpp. delete (head.data);
head = head.next;
size--;
}
const newNode = new Node(pages[i]);
if (head === null ) {
head = newNode;
tail = head;
}
newNode.prev = tail;
tail.next = newNode;
tail = tail.next;
size++;
mpp.set(pages[i], newNode);
} else {
const ptr = mpp.get(pages[i]);
if (ptr === head) {
head = head.next;
ptr.prev = tail;
tail.next = ptr;
tail = tail.next;
} else if (tail !== ptr) {
ptr.prev.next = ptr.next;
ptr.next.prev = ptr.prev;
ptr.prev = tail;
tail.next = ptr;
tail = tail.next;
}
}
}
return faults;
}
const pages = [1, 2, 1, 4, 2, 3, 5];
const n = 7, c = 3;
console.log( "Page Faults =" , pageFaults(n, c, pages));
|
Explanation: Node linkages in linked list takes O(1) constant time
Accessing elements from map takes O(1) time on average
Explanation: Linked list size is of C nodes.
Like Article
Suggest improvement
Share your thoughts in the comments
Please Login to comment...