Which is Faster? Python or C#/.NET?

Please note, that this is only one basic test of speed.

Which is Faster? Python or C#/.NET?

Please note, that this is only one basic test of speed.

In software development, we have the languages that are interpreted when they are run or are compiled into native code. Overall, an interpreted language will normally be slower than a compiled language, as the code needs to be converted from high-level code to machine code as the program is run. But compiled programs can hog the processor and cause it to crash if it does not handle things correctly.

We thus have managed environments where the code is compiled to a near machine-ready version and then run within a managed environment. This managed environment makes sure that the code is run correctly and does not crash the system. Typical languages we have for this are Java and .NET/C#. Overall, C# is compiled into an CIL (Common Intermediate Language and previously known as Microsoft Intermediate Language) form as an EXE and when it is run in the .NET framework on the system. The CIL is then portable on a range of systems that support the .NET framework. Overall, the .NET framework is open source and will run on Windows, Mac OS and Linux.

With Python, we do not normally compile straight to machine code but will convert it into byte code ahead of the code running on the processor. The Python runtime engine will then convert this byte code into machine code when the program is run. As with .NET, the runtime environment will manage the code running on the system.

So, let’s put Python, C#/.NET and Golang (a compiled language) head-to-head. For this, we will generate RSA key parameters using safe primes — and which takes quite a bit of processing (the C# code is given in the next section). From these results, we can see that Golang is generally the fastest and around seven times faster than Python and around two times faster than C# for light procession loads. As the work scales up, though, both Python and C# show weaker performance than Golang (possibly due to memory management and not hogging the CPU):

Overall, Golang is the fastest, with C# in 2nd place and Python in 3rd. What is noticeable is that Golang scales up better than Python and C# and can sustain more of a processing load over a sustained amount of time.

Safe prime generation for RSA

The code for Golang is here, and for Python it is here. We will outline the C# code in this section.

Overall, Bob generates two random prime numbers (p and q), and create a public modulus of:

N=p.q

Next Bob computes φ:

φ=(p−1).(q−1)

Bob then picks a public exponent of e and which does not share a factor with φ, and computes the private exponent as:

d=e^{−1} (mod φ)

Bob will then use a public exponent (e) to cipher a message (M) with:

C=M^e (modN)

To decrypt we use the private exponent (d) to decipher the ciphertext:

M=C^d (modN)

Bob’s public key is [e,N] and his private key is [d,N].

Initally we generate a random number with the number of bits that is required for the prime numbers:

static public BigInteger randomGenerator(int size)   
// Get random number of size bits

{
var rng = RandomNumberGenerator.Create();
byte[] randomNumber = new byte[size/8];
rng.GetBytes(randomNumber);
var result = new BigInteger(randomNumber);
result = BigInteger.Abs(result);
return result;
}

Next we call a primality tester to get the next prime number, and which should check whether p is a prime number, and also if 2p+1 is prime:

public static BigInteger GetNearestSafePrime(BigInteger p)
//get the next safe prime

{
var TwopPlus1 = BigInteger.Multiply(new BigInteger(2),p);
TwopPlus1 =BigInteger.Add(TwopPlus1,BigInteger.One);
while (MillerRabinTest(p,10) == false || MillerRabinTest(TwopPlus1,10) == false)
{
p++;
TwopPlus1 = BigInteger.Multiply(new BigInteger(2),p);
TwopPlus1 =BigInteger.Add(TwopPlus1,BigInteger.One);

}
return p;
}

The coding is here:

namespace SafePrimes
{
using System;
using System.Numerics;
using System.Security.Cryptography;
using System.Diagnostics;

class Program
{


public static bool MillerRabinTest(BigInteger result, int k)
// Test for prime number
{
if (result == 2 || result == 3)
return true;
if (result < 2 || result % 2 == 0)
return false;
BigInteger d = result - 1;
int s = 0;
while (d % 2 == 0)
{
d /= 2;
s += 1;
}
for (int i = 0; i < k; i++)
{
var rng = RandomNumberGenerator.Create();
byte[] _a = new byte[result.ToByteArray().LongLength];
BigInteger a;
do
{
rng.GetBytes(_a);
a = new BigInteger(_a);
}
while (a < 2 || a >= result - 2);
BigInteger x = BigInteger.ModPow(a, d, result);
if (x == 1 || x == result - 1)
continue;
for (int r = 1; r < s; r++)
{
x = BigInteger.ModPow(x, 2, result);
if (x == 1)
return false;
if (x == result - 1)
break;
}
if (x != result - 1)
return false;
}
return true;
}
public static BigInteger GetNearestSafePrime(BigInteger p) //get the next safe prime
{
var TwopPlus1 = BigInteger.Multiply(new BigInteger(2),p);
TwopPlus1 =BigInteger.Add(TwopPlus1,BigInteger.One);

while (MillerRabinTest(p,10) == false || MillerRabinTest(TwopPlus1,10) == false)
{
p++;
TwopPlus1 = BigInteger.Multiply(new BigInteger(2),p);
TwopPlus1 =BigInteger.Add(TwopPlus1,BigInteger.One);

}
return p;
}
static public BigInteger randomGenerator(int size)
// Get random number of size bits
{
var rng = RandomNumberGenerator.Create();
byte[] randomNumber = new byte[size/8];
rng.GetBytes(randomNumber);
var result = new BigInteger(randomNumber);
result = BigInteger.Abs(result);
return result;
}


static void Main(string[] args)
{
var bits=256;
if (args.Length >0) bits=Convert.ToInt32(args[0]);
try {
Stopwatch timer = new Stopwatch();
timer.Start();
var p=randomGenerator(bits/2);
var q=randomGenerator(bits/2);
p=GetNearestSafePrime(p);
q=GetNearestSafePrime(q);

var n = BigInteger.Multiply(p,q);

var pminus1 = BigInteger.Subtract(p,BigInteger.One);
var qminus1 = BigInteger.Subtract(q,BigInteger.One);
var PHI = BigInteger.Multiply(pminus1,qminus1);
Org.BouncyCastle.Math.BigInteger e= new Org.BouncyCastle.Math.BigInteger("65537");
Org.BouncyCastle.Math.BigInteger PHI1= new Org.BouncyCastle.Math.BigInteger(PHI.ToString());

var d = e.ModInverse(PHI1);
timer.Stop();
Console.WriteLine("RSA-{0}\n",bits);
Console.WriteLine("Time elapsed: {0} ms", timer.ElapsedMilliseconds);

Console.WriteLine("p={0}\n",p);
Console.WriteLine("q={0}\n",q);
Console.WriteLine("n={0}\n",n);
Console.WriteLine("PHI={0}\n",PHI);
Console.WriteLine("e={0}\n",e);
Console.WriteLine("d={0}\n",d);

} catch (Exception e) {
Console.WriteLine("Error: {0}",e.Message);
}
}
}

}

Note, for the inverse mod operation, we have converted our .NET Big Integers into Bouncy Castle versions, as the Bouncy Castle implementation of Big Integers has made more operations:

It is likely that with a full Bouncy Castle Big Integer we would have achieved a significantly improved performance. As we can see, we have the NextProbablyPrime() and RabinMillTest() methods that we can use, and not use our own code for these functions.

A sample run for RSA-128 is:

RSA-128
Time elapsed: 18 ms
p=7046818119314923871
q=5055727111291365563
n=35626789414159693490208560648976054373
PHI=35626789414159693478106015418369764940
e=65537
d=16050728355623222143744117235192115593

A sample run for RSA-256 is:

RSA-256
Time elapsed: 146 ms
p=61248372060595206442911598523489999003
q=126792125373346225165763537953515743573
n=7765811269220343496081891189154409480515227961973786735095368917374573657719
PHI=7765811269220343496081891189154409480327187464539845303486693780897567915144
e=65537
d=864421520804467793825127732805612358804748959424724529486174697219093915513

We can prove this in Python with:

>>> p=61248372060595206442911598523489999003
>>>
>>> q=126792125373346225165763537953515743573
>>> n=p*q
>>> PHI=(p-1)*(q-1)
>>> e=65537
>>> d=pow(e,-1,PHI)
>>> print (d)
864421520804467793825127732805612358804748959424724529486174697219093915513

A sample run for RSA-512 is:

RSA-512
Time elapsed: 13374 ms
p=1323470027344410410473196826077128031533319915257508093003742428887559847131
q=1127686320575107135617301047703345495684745318409087176323588734271757934899
n=1492459045527454604857603751349690625374225830953511888237217551407600703543054160798553536086484464743712726767466013037099683691343448081592689924769
PHI=1492459045527454604857603751349690625374225830953511888237217551407600703540603004450634018540393966869932253240247947803433088422016116918433372142740
e=65537
d=932431078919108729357379580984986841874783353653532268243478838555078975334085936451641208617062590193163802263178848937418066213580876561717112812373

And for RSA-1024 (run in an offline mode):

RSA-1024
Time elapsed: 244606 ms
p=7676076876255789774187746702560650298863749150691673038475040353988215607719796059634874041132339579268135429366800915101892471738721878716670
71131461761
q=4871082464141476428458502361491350136148938166702040141843057190200982421675969095771452699677500683189691597963668638566941929015002398931149
680377494363
n=3739080346533145948854201933284044244170136388501151961148027281808272043033253352935859781386632733744272760251214862228962260801629631598724
043770910867121472886002600211043107206155888362503225146006057885564102680414881733783830408459013288335524249830801965413396773664638574814932
021452233326727553243
PHI=37390803465331459488542019332840442441701363885011519611480272818082720430332533529358597813866327337442727602512148622289622608016296315987
240437709108614827827342355448051658301744084731964679120642348504398735414548150777513358817067240731845447896087142968245130480435875073986260
57434649416575218597120
e=65537
d=1045609739702961163993636553874859680225004037292836290827469917660256065011679420773845342531284894202226023118604983140366378590894704644854
28918304901595121777271

For RSA-2048, in this test run, it took 956.766 seconds (15 minutes):

RSA-2048
Time elapsed: 956766 ms
p=2895564815172559079360457635758537690852702431440068124322170604110209288703890256911533380860680638503934944856070293464950273132853834032502
371705234664195773496415736714239635751516124574384028826225828307355560701136634633245710659246468082198895827521582056185973673398872985365605
3055190486402481009393
q=8486931872629636116518711710542141719818520927176120294045774875153821405541941839982828029093139174926752197652488168658560588003790181948445
468821916648186705022987178482200918101558404758223091161282545597915716196674286886580872052799535509822234747173896725792848154275622511904633
3146783629324114182979
n=2457446131915293301578071663219775328824736905298271191447241424066561781067387358036333432674774224832288310978289802998384174194440093202447
846663226819574418244391992239090239435320817456346935698944960337274584628603002583594076446970035677403787551790852492280939530503987067833908
901404233982914909942715444689289235612876081547017500038059563093331952476844142590822167004413818191890660800689271050069003227241490161117481
441465189179284346777424804216838877965120126256416609901982993586558017664571891486740824415949631050806717252371926761079538712756193943634584
769605022447867464326841368514975419721747
PHI=24574461319152933015780716632197753288247369052982711914472414240665617810673873580363334326747742248322883109782898029983841741944400932024
478466632268195744182443919922390902394353208174563469356989449603372745846286030025835940764469700356774037875517908524922809395305039870678339
089014042339829149099426016197224112136609172898535544932439528508597457905926604631360295266974713598709217171865897328519346963558164045764962
463328538227391245372990195327037150531799260971044522043634522482932319464646968077476881116469715218356084514252514667251593274070092391558147
96551328277492894761940639394399248824529376
e=65537
d=2790157722749240498808677731055487468420108841162158160361158343604267240325683099798336370681141188485444454581298262677720469380781655174849
997256674972069708127702033088342535001330882202828562268008827054894210022038686882909428695531384633956632615602748584015513533802305227848713
876947206168556669497062522293431472427924005340540303069754972496050845751252957290568031517445777011387231271711277927162683000996363344936229
757658314387571247088210376252856205000508123434735928203678560133960802850879495010535611755972213250341408220592362162123770577773738278898622
32608049084099515138018184136057654505441Conclusions

As would be expected, in this case, for a lighter processing loading, Golang is generally the fastest by a factor of two and seven over C# and Python, respectively. But, Golang is then able to sustain its processing for a longer period of time. What is noticeable, in this case, is that C# is generally faster than Python — even at the heavier processing loads. But, with Golang, we need to watch out for bad code, as it will not be managed. For this, Rust is possibly the best compiled language around for making sure our code does not crash the system. So, if you want fast code which is robust, Rust may be your answer.

Here is some Golang code:

https://asecuritysite.com/golang/

And some Rust:

https://asecuritysite.com/rust