下面是一个简单的过程编码C#和Python(对于你们好奇的过程,它是项目欧拉问题5的解决方案).
我的问题是,C#下面的代码只需要9秒来迭代,而Python代码的完成需要283秒(确切地说,Python 3.4.3上的283秒 - 64位和Python 2.7.9上的329秒--32位).
到目前为止,我已经编写了类似的进程C#,Python并且执行时间差异可比.然而,这一次,经过的时间之间存在极大的差异.
我认为,这种差异的某些部分来自灵活变量类型的python语言(我怀疑,python将变量的某些部分转换为double)但是这仍然很难解释.
我究竟做错了什么?
我的系统:Windows-7 64位,
C# - VS Express 2012(9秒)
Python 3.4.3 64位(283秒)
Python 2.7.9 32位(329秒)
c-sharp代码:
using System;
namespace bug_vcs {
class Program {
public static void Main(string[] args) {
DateTime t0 = DateTime.Now;
int maxNumber = 20;
bool found = false;
long start = maxNumber;
while (!found) {
found = true;
int i = 2;
while ((i < maxNumber + 1) && found) {
if (start % i != 0) {
found = false;
}
i++;
}
start++;
}
Console.WriteLine("{0:d}", start - 1);
Console.WriteLine("time elapsed = {0:f} sec.", (DateTime.Now - t0).Seconds);
Console.ReadLine();
}
}
}
Run Code Online (Sandbox Code Playgroud)
和python代码:
from datetime import datetime
t0 = datetime.now()
max_number = 20
found = False
start = max_number
while not found:
found = True
i = 2
while ((i < max_number + 1) and found):
if (start % i) != 0:
found = False
i += 1
start += 1
print("number {0:d}\n".format(start - 1))
print("time elapsed = {0:f} sec.\n".format((datetime.now() - t0).seconds))
Run Code Online (Sandbox Code Playgroud)
Bli*_*ixt 28
答案很简单,Python处理所有事物的对象,默认情况下它没有JIT.因此,通过修改堆栈上的几个字节并优化代码的热点部分(即迭代),而不是非常有效 - Python突出显示代表数字的丰富对象,而不是动态优化.
如果您在具有JIT的Python变体(例如,PyPy)中尝试过此操作,我保证您会看到巨大的差异.
一般提示是避免使用标准Python进行计算成本非常高的操作(特别是如果这是针对来自多个客户端的请求的后端).使用JIT的Java,C#,JavaScript等无比高效.
顺便说一句,如果你想以更Pythonic的方式编写你的例子,你可以这样做:
from datetime import datetime
start_time = datetime.now()
max_number = 20
x = max_number
while True:
i = 2
while i <= max_number:
if x % i: break
i += 1
else:
# x was not divisible by 2...20
break
x += 1
print('number: %d' % x)
print('time elapsed: %d seconds' % (datetime.now() - start_time).seconds)
Run Code Online (Sandbox Code Playgroud)
以上为我执行了90秒.它更快的原因依赖于看似愚蠢的事情,例如x短时间start,我不经常分配变量,而且我依赖于Python自己的控制结构而不是变量检查来跳入/跳出循环.
如果您想要像 C 一样快但牺牲一点代码可读性,请尝试使用 python JIT 实现,例如 pypy 和 numba 或 cython。
例如在pypy
# PyPy
number 232792560
time elapsed = 4.000000 sec.
Run Code Online (Sandbox Code Playgroud)
例如在 cython
# Cython
number 232792560
time elapsed = 1.000000 sec.
Run Code Online (Sandbox Code Playgroud)
Cython 来源:
from datetime import datetime
cpdef void run():
t0 = datetime.now()
cdef int max_number = 20
found = False
cdef int start = max_number
cdef int i
while not found:
found = True
i = 2
while ((i < max_number + 1) and found):
if (start % i) != 0:
found = False
i += 1
start += 1
print("number {0:d}\n".format(start - 1))
print("time elapsed = {0:f} sec.\n".format((datetime.now() - t0).seconds))
Run Code Online (Sandbox Code Playgroud)
TL; DR:啰嗦的帖子就是我试图用C#来保护Python(我选择的语言).在这个例子中,C#表现得更好,但仍然需要更多行代码才能完成相同的工作量,但最终的性能优势是,如果编码正确,C#比Python中的类似方法快约5倍.最终结果是您应该使用适合您的语言.
当我运行C#示例时,我的机器上花了大约3秒钟完成,并给了我232,792,560的结果.它可以使用已知的事实进行优化,如果数字是20的倍数,您只能将数字从1到20整除,因此您不需要增加1,而是需要增加20.使代码在353毫秒内快速执行~10倍.
当我运行Python示例时,我放弃了等待,并尝试使用itertools编写我自己的版本,这没有更好的成功,并且只需要你的例子.然后我找到了一个可接受的itertools版本,如果我考虑到只有我的最大数字的倍数可以被从最小到最大的所有数字整除.因此,精炼的Python(3.6)代码在这里有一个装饰器计时功能,它打印执行所花费的秒数:
import time
from itertools import count, filterfalse
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
print(time.time() - start)
return res
return wrapper
@timer
def test(stop):
return next(filterfalse(lambda x: any(x%i for i in range(2, stop)), count(stop, stop)))
print("Test Function")
print(test(20))
# 11.526668787002563
# 232792560
Run Code Online (Sandbox Code Playgroud)
这也让我想起了我最近使用Python中最大公分母函数在CodeFights for Least Common Multiple上回答的一个问题.该代码如下:
import time
from fractions import gcd
from functools import reduce
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
print(time.time() - start)
return res
return wrapper
@timer
def leastCommonDenominator(denominators):
return reduce(lambda a, b: a * b // gcd(a, b), denominators)
print("LCM Function")
print(leastCommonDenominator(range(1, 21)))
# 0.001001596450805664
# 232792560
Run Code Online (Sandbox Code Playgroud)
与大多数编程任务一样,有时最简单的方法并不总是最快的.不幸的是,这次在Python中尝试时确实很突出.也就是说,Python中的优点在于获得高性能执行的简单性,其中需要10行C#,我能够(可能)返回一个单行lambda表达式中的正确答案,并且比我的300倍快. C#的简单优化.我不是C#的专家,但是在这里实现相同的方法是我使用的代码及其结果(比Python快约5倍):
using System;
using System.Diagnostics;
namespace ConsoleApp1
{
class Program
{
public static void Main(string[] args)
{
Stopwatch t0 = new Stopwatch();
int maxNumber = 20;
long start;
t0.Start();
start = Orig(maxNumber);
t0.Stop();
Console.WriteLine("Original | {0:d}, {1:d}", maxNumber, start);
// Original | 20, 232792560
Console.WriteLine("Original | time elapsed = {0}.", t0.Elapsed);
// Original | time elapsed = 00:00:02.0585575
t0.Restart();
start = Test(maxNumber);
t0.Stop();
Console.WriteLine("Test | {0:d}, {1:d}", maxNumber, start);
// Test | 20, 232792560
Console.WriteLine("Test | time elapsed = {0}.", t0.Elapsed);
// Test | time elapsed = 00:00:00.0002763
Console.ReadLine();
}
public static long Orig(int maxNumber)
{
bool found = false;
long start = 0;
while (!found)
{
start += maxNumber;
found = true;
for (int i=2; i < 21; i++)
{
if (start % i != 0)
found = false;
}
}
return start;
}
public static long Test(int maxNumber)
{
long result = 1;
for (long i = 2; i <= maxNumber; i++)
{
result = (result * i) / GCD(result, i);
}
return result;
}
public static long GCD(long a, long b)
{
while (b != 0)
{
long c = b;
b = a % b;
a = c;
}
return a;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然而,对于大多数更高级别的任务,我通常认为Python与.NET实现相比表现得非常好,但我目前无法证实这些声明,除了说Python请求库给了我多达两倍的声明.与C#WebRequest相比,性能的三重回报以相同的方式编写.编写Selenium进程时也是如此,因为我可以在100毫秒或更短的时间内读取Python中的文本元素,但每个元素检索都需要C#> 1秒才能返回.也就是说,我实际上更喜欢C#实现,因为它采用面向对象的方法,Python的Selenium实现很有用,有时很难阅读.
小智 5
正如一些人所说,最好的方法是使用 JIT 实现。我知道这是一个老话题,但我很好奇实现之间的执行时间差异,所以我在 Jupiter Notebook 中用 Numba 和 Cython 做了一些测试,这是我的结果:
%%time
def test():
max_number = 20
found = False
start = max_number
while not found:
found = True
i = 2
while ((i < max_number + 1) and found):
if (start % i) != 0:
found = False
i += 1
start += 1
return start-1
test()
Run Code Online (Sandbox Code Playgroud)
CPU 时间:用户 1 分 18 秒,系统:462 毫秒,总计:1 分 19 秒挂墙时间:1 分 21 秒
%%time
def test():
max_number = 20
x = max_number
while True:
i = 2
while i <= max_number:
if x % i: break
i += 1
else:
# x was not divisible by 2...20
break
x += 1
return x
test()
Run Code Online (Sandbox Code Playgroud)
CPU 时间:用户 40.1 秒,系统:305 毫秒,总计:40.4 秒挂墙时间:41.9 秒
%%time
from numba import jit
@jit(nopython=True)
def test():
max_number = 20
x = max_number
while True:
i = 2
while i <= max_number:
if x % i: break
i += 1
else:
# x was not divisible by 2...20
break
x += 1
return x
test()
Run Code Online (Sandbox Code Playgroud)
CPU 时间:用户 4.48 秒,系统:70.5 毫秒,总计:4.55 秒挂墙时间:5.01 秒
%%time
from numba import jit, int32
@jit(int32())
def test():
max_number = 20
x = max_number
while True:
i = 2
while i <= max_number:
if x % i: break
i += 1
else:
# x was not divisible by 2...20
break
x += 1
return x
test()
Run Code Online (Sandbox Code Playgroud)
CPU 时间:用户 3.56 秒,系统:43.1 毫秒,总计:3.61 秒挂墙时间:3.79 秒
%load_ext Cython
Run Code Online (Sandbox Code Playgroud)
%%time
%%cython
def test():
cdef int max_number = 20
cdef int x = max_number
cdef int i = 2
while True:
i = 2
while i <= max_number:
if x % i: break
i += 1
else:
# x was not divisible by 2...20
break
x += 1
return x
test()
Run Code Online (Sandbox Code Playgroud)
CPU 时间:用户 617 毫秒,系统:20.7 毫秒,总计:637 毫秒挂墙时间:1.31 秒
| 归档时间: |
|
| 查看次数: |
24706 次 |
| 最近记录: |