Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save admir-live/0d6cb706acdea72b9cc0e9303cb4a9ae to your computer and use it in GitHub Desktop.
Save admir-live/0d6cb706acdea72b9cc0e9303cb4a9ae to your computer and use it in GitHub Desktop.
This C# file benchmarks various LINQ and PLINQ methods for analyzing potentially fraudulent transactions in different dataset sizes.
using System.Security.Cryptography;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
public class Transaction
{
public decimal Amount { get; }
public TimeSpan TransactionTime { get; }
public Transaction(decimal amount, TimeSpan transactionTime)
{
Amount = amount;
TransactionTime = transactionTime;
}
}
public class TransactionFactory
{
private const int MaxTransactionAmount = 20000;
private const int MinutesInADay = 1440;
private readonly RandomNumberGenerator _randomNumberGenerator = RandomNumberGenerator.Create();
public List<Transaction> CreateTransactions(int count)
{
List<Transaction> transactions = new List<Transaction>();
for (int i = 0; i < count; i++)
{
decimal amount = GenerateRandomAmount();
double minutes = GenerateRandomMinutes();
transactions.Add(new Transaction(amount, TimeSpan.FromMinutes(minutes)));
}
return transactions;
}
private decimal GenerateRandomAmount()
{
return (decimal)(NextDouble(_randomNumberGenerator) * MaxTransactionAmount);
}
private double GenerateRandomMinutes()
{
return NextDouble(_randomNumberGenerator) * MinutesInADay;
}
private static double NextDouble(RandomNumberGenerator rng)
{
byte[] buffer = new byte[8];
rng.GetBytes(buffer);
ulong ulongValue = BitConverter.ToUInt64(buffer, 0) / (1UL << 11);
return (double)ulongValue / (1UL << 53);
}
}
public class TransactionAnalysisBenchmark
{
private const int SmallNumberOfTransactions = 1000;
private const int LargeNumberOfTransactions = 1000000;
private const decimal FraudulentAmountThreshold = 10000;
private List<Transaction> smallTransactionList;
private List<Transaction> largeTransactionList;
[GlobalSetup]
public void PrepareTransactionData()
{
TransactionFactory transactionFactory = new TransactionFactory();
smallTransactionList = transactionFactory.CreateTransactions(SmallNumberOfTransactions);
largeTransactionList = transactionFactory.CreateTransactions(LargeNumberOfTransactions);
}
[Benchmark]
public List<Transaction> PerformLinqQueryOnSmallTransactions()
{
return smallTransactionList
.Where(IsTransactionPotentiallyFraudulent)
.ToList();
}
[Benchmark]
public List<Transaction> PerformLinqQueryOnLargeTransactions()
{
return largeTransactionList
.Where(IsTransactionPotentiallyFraudulent)
.ToList();
}
[Benchmark]
public List<Transaction> PerformPLinqQueryOnSmallTransactionsSingleCore()
{
return smallTransactionList.AsParallel()
.WithDegreeOfParallelism(1)
.Where(IsTransactionPotentiallyFraudulent)
.ToList();
}
[Benchmark]
public List<Transaction> PerformPLinqQueryOnSmallTransactionsMaxCore()
{
return smallTransactionList.AsParallel()
.WithDegreeOfParallelism(Environment.ProcessorCount)
.Where(IsTransactionPotentiallyFraudulent)
.ToList();
}
[Benchmark]
public List<Transaction> PerformPLinqQueryOnLargeTransactionsSingleCore()
{
return largeTransactionList.AsParallel()
.WithDegreeOfParallelism(1)
.Where(IsTransactionPotentiallyFraudulent)
.ToList();
}
[Benchmark]
public List<Transaction> PerformPLinqQueryOnLargeTransactionsMaxCore()
{
return largeTransactionList.AsParallel()
.WithDegreeOfParallelism(Environment.ProcessorCount)
.Where(IsTransactionPotentiallyFraudulent)
.ToList();
}
public static bool IsTransactionPotentiallyFraudulent(Transaction transaction)
{
return transaction is { Amount: > FraudulentAmountThreshold, TransactionTime.Hours: < 4 or 0x0 };
}
public static void Main(string[] args)
{
Summary summary = BenchmarkRunner.Run<TransactionAnalysisBenchmark>();
}
}
@admir-live
Copy link
Author

BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.3447/23H2/2023Update/SunValley3)
AMD Ryzen 9 3900X, 1 CPU, 24 logical and 12 physical cores
.NET SDK 8.0.204
[Host] : .NET 8.0.4 (8.0.424.16909), X64 RyuJIT AVX2
DefaultJob : .NET 8.0.4 (8.0.424.16909), X64 RyuJIT AVX2

Method Mean Error StdDev Median
PerformLinqQueryOnSmallTransactions 14.53 us 0.070 us 0.065 us 14.51 us
PerformLinqQueryOnLargeTransactions 15,889.61 us 56.362 us 52.721 us 15,860.68 us
PerformPLinqQueryOnSmallTransactionsSingleCore 18.43 us 0.087 us 0.077 us 18.41 us
PerformPLinqQueryOnSmallTransactionsMaxCore 26.34 us 1.854 us 5.467 us 23.47 us
PerformPLinqQueryOnLargeTransactionsSingleCore 23,330.55 us 167.596 us 148.570 us 23,278.21 us
PerformPLinqQueryOnLargeTransactionsMaxCore 6,918.39 us 51.540 us 45.689 us 6,935.14 us

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment