你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

计算机究竟1s可以执行多少次操作呢?

[复制链接]
gaosmile 发布时间:2021-2-20 11:16
一些同学可能对计算机运行的速度还没有概念,就是感觉计算机运行速度应该会很快,那么在leetcode上做算法题目的时候为什么会超时呢?% P! ?7 B2 B  B: C  `

计算机究竟1s可以执行多少次操作呢?接下来探讨一下这个问题。

超时是怎么回事

微信图片_20210220111316.png

大家在leetcode上练习算法的时候应该都遇到过一种错误是“超时”。

也就是说程序运行的时间超过了规定的时间,一般OJ(online judge)的超时时间就是1s,也就是用例数据输入后最多要1s内得到结果,暂时还不清楚leetcode的判题规则,下文为了方便讲解,暂定超时时间就是1s。

如果写出了一个O(n)的算法 ,其实可以估算出来n是多大的时候算法的执行时间就会超过1s了。

如果n的规模已经足够让O(n)的算法运行时间超过了1s,就应该考虑log(n)的解法了。

从硬件配置看计算机的性能

计算机的运算速度主要看CPU的配置,以2015年MacPro为例,CPU配置:2.7 GHz Dual-Core Intel Core i5 。

也就是 2.7 GHz 奔腾双核,i5处理器,GHz是指什么呢,1Hz = 1/s,1Hz 是CPU的一次脉冲(可以理解为一次改变状态,也叫时钟周期),称之为为赫兹,那么1GHz等于多少赫兹呢

  • 1GHz(兆赫)= 1000MHz(兆赫)
  • 1MHz(兆赫)= 1百万赫兹
    . m" ~: y& C1 U

所以 1GHz = 10亿Hz,表示CPU可以一秒脉冲10亿次(有10亿个时钟周期),这里不要简单理解一个时钟周期就是一次CPU运算。

例如1 + 2 = 3,cpu要执行四次才能完整这个操作,步骤一:把1放入寄存机,步骤二:把2放入寄存器,步骤三:做加法,步骤四:保存3。

而且计算机的cpu也不会只运行我们自己写的程序上,同时cpu也要执行计算机的各种进程任务等等,我们的程序仅仅是其中的一个进程而已。

所以我们的程序在计算机上究竟1s真正能执行多少次操作呢?

做个测试实验

在写测试程序测1s内处理多大数量级数据的时候,有三点需要注意:

  • CPU执行每条指令所需的时间实际上并不相同,例如CPU执行加法和乘法操作的耗时实际上都是不一样的。
  • 现在大多计算机系统的内存管理都有缓存技术,所以频繁访问相同地址的数据和访问不相邻元素所需的时间也是不同的。
  • 计算机同时运行多个程序,每个程序里还有不同的进程线程在抢占资源。4 t) |# y! ^+ l$ [

尽管有很多因素影响,但是还是可以对自己程序的运行时间有一个大体的评估的。

引用算法4里面的一段话:

  • 火箭科学家需要大致知道一枚试射火箭的着陆点是在大海里还是在城市中;
  • 医学研究者需要知道一次药物测试是会杀死还是会治愈实验对象;
    0 U& f% @6 p! ^6 }$ ~

所以「任何开发计算机程序员的软件工程师都应该能够估计这个程序的运行时间是一秒钟还是一年」

这个是最基本的,所以以上误差就不算事了。

以下以C++代码为例:

测试硬件:2015年MacPro,CPU配置:2.7 GHz Dual-Core Intel Core i5

实现三个函数,时间复杂度分别是 O(n) , O(n^2), O(nlogn),使用加法运算来统一测试。

// O(n)3 _  R# ]0 K! ]4 H. k9 `
void function1(long long n) {9 H4 o) c7 H$ w+ d5 n7 O4 X
    long long k = 0;
# ~' C6 g& S5 K5 p# A    for (long long i = 0; i < n; i++) {
4 ?3 x# N& D% b; |" F        k++;
( S4 I6 f/ [  e8 P- ]  u    }
6 g, g% Z$ g* G! L. A}5 E0 C5 H7 h  c% o6 Z" Q/ G* E) H5 L1 x, ~
/ a$ D& N0 O1 E/ A+ `& K
// O(n^2)" j. f% d  ~; R% I- O- D8 N3 q
void function2(long long n) {
2 l$ N4 f7 H) Q  G/ N: B$ Q. j    long long k = 0;
' k. t* a/ f/ q, z& t: h# ?+ e  |5 J5 W    for (long long i = 0; i < n; i++) {: p# I5 C. q  T9 M
        for (long j = 0; j < n; j++) {$ ~8 R2 l9 O8 ?+ l: ]
            k++;+ y, U! Q" B# R; J5 K4 o
        }
! }. Y4 x/ [6 |1 ~    }
8 F* d4 @* K6 V* B# G8 y3 U8 y- L& Y; C. _& f# L1 W3 I- C# @
}
. n) I: \& Z  V2 o+ h& u' X  ^
// O(nlogn)8 W! X( m+ E3 P0 U' r/ O
void function3(long long n) {
: z# W& h3 F: v; j4 n% g    long long k = 0;: s- d- x4 o: N/ ~& z2 b
    for (long long i = 0; i < n; i++) {; b1 k- {/ n2 k' B  B
        for (long long j = 1; j < n; j = j * 2) { // 注意这里j=1
; U, W/ P; Z3 l! @            k++;0 [! J! }. q! ]2 H
        }6 S$ M' E9 v% M+ W! h- x' L
    }2 j6 v2 H3 {7 q6 Q, M
}$ Y' I5 v0 H5 }: G

+ m4 x# C4 }' ^* E/ S( o

来看一下这三个函数随着n的规模变化,耗时会产生多大的变化,先测function1 ,就把 function2 和 function3 注释掉

int main() {
' a1 a0 X4 G$ V+ z2 n( ~    long long n; // 数据规模
3 [( `6 p& p& W! \    while (1) {+ s6 D+ U0 ~- n- t4 Z# p
        cout << "输入n:";0 C" W3 _4 O5 G& \5 s: J  |% H2 l7 Q
        cin >> n;$ J# u& f& o! @8 l! r" s! _
        milliseconds start_time = duration_cast<milliseconds >(4 q+ e, b) \( v' a; a
            system_clock::now().time_since_epoch()
/ g. g: E4 a* r* V5 ]8 u        );
4 j1 a( ]. a/ }  k# P+ ]        function1(n);+ t5 |8 y$ @' C  j+ L
//        function2(n);  f& c, N; R8 p0 u5 `* g
//        function3(n);. `: m6 w" G4 f6 ^
        milliseconds end_time = duration_cast<milliseconds >(0 ^/ Y' u; f. s* G0 T; k1 m
            system_clock::now().time_since_epoch()
) A! }2 r" g0 A+ m+ \        );
- T+ U& v, g8 f, i        cout << "耗时:" << milliseconds(end_time).count() - milliseconds(start_time).count()+ g+ W4 W$ z: d1 c
            <<" ms"<< endl;
# Q. F7 s3 D- v" j+ v; S5 b4 l    }0 ?! o; g4 _" F+ X
}! ^; _5 H. O: M- S

来看一下运行的效果,如下图:

微信图片_20210220111320.png

O(n)的算法,1s内大概计算机可以运行 5 * (10^8)次计算,可以推测一下O(n^2) 的算法应该1s可以处理的数量级的规模是 5 * (10^8)开根号,实验数据如下。

微信图片_20210220111323.png

O(n^2)的算法,1s内大概计算机可以运行 22500次计算,验证了刚刚的推测。

在推测一下O(nlogn)的话, 1s可以处理的数据规模是什么呢?

理论上应该是比 O(n)少一个数量级,因为logn的复杂度 其实是很快,看一下实验数据。

微信图片_20210220111326.png - @# X2 ]; k+ o
O(nlogn)的算法,1s内大概计算机可以运行 2 * (10^7)次计算,符合预期。

这是在我个人PC上测出来的数据,不能说是十分精确,但数量级是差不多的,大家也可以在自己的计算机上测一下。

「整体测试数据整理如下:」

微信图片_20210220111330.png $ A, t9 W' y" j# w% e+ M  Q
至于O(logn) 和O(n^3) 等等这些时间复杂度在1s内可以处理的多大的数据规模,大家可以自己写一写代码去测一下了。

完整测试代码#include <iostream>( K* s5 X. W  U+ Z
#include <chrono>
) |* L& l; y' u+ B& ?3 r#include <thread>
3 V5 t$ t: k* S- j" z, I4 \using namespace std;& n2 f5 `7 }! V' q6 U) H* K
using namespace chrono;
9 _& M7 o& \, R0 s5 m7 K( c- F* f// O(n)
8 Y8 T& b; W3 ovoid function1(long long n) {
2 w9 X- b" ?% F  K    long long k = 0;
- J* A/ C3 u6 V+ t9 V2 J& m    for (long long i = 0; i < n; i++) {
5 f. s0 T2 B* \+ Q, a        k++;9 H) R% p2 v- W3 |! H* S
    }8 j& V8 ?, ~( _) @& v9 z
}& V6 W( B. q0 m: j4 V& H

8 Y) A+ ?' @  A8 P& Q& l// O(n^2)# M) x6 Z6 W" Y. g# k8 L) e% k: q
void function2(long long n) {
, f2 b1 s7 ?0 B3 K    long long k = 0;7 H; @, e4 ]( a* }
    for (long long i = 0; i < n; i++) {
( Y: _# V7 j4 e9 M2 h* \        for (long j = 0; j < n; j++) {
# H& `2 H2 J- V& b% r            k++;
. m: o$ l8 B0 m) q! L( l4 x        }
- r: s* l" e4 g0 o" O( c    }
! ^- e9 ~: R/ d, y$ ?
8 e' Z9 a0 \. }4 k}
! T; W# |, y5 F* l6 h5 w6 Q* K: V// O(nlogn)% e( u$ }7 w) h# M
void function3(long long n) {/ W  K6 h9 \* G! M0 ?
    long long k = 0;
4 O% E5 b! x0 S* p4 P    for (long long i = 0; i < n; i++) {* }- q1 u- C' t  V
        for (long long j = 1; j < n; j = j * 2) { // 注意这里j=1
3 c+ X6 P! P8 g/ Z. T            k++;& [$ \& x2 K. w- x- }) M
        }8 _6 t6 r0 }4 `$ Z0 @6 W3 p/ l
    }3 h  Q+ U# Q( G, Y4 W" Q4 O
}/ N) B. B  @) Q0 E% q+ k1 h
int main() {6 t( N' S$ A" k+ [) l2 Z
    long long n; // 数据规模
- T( V3 z2 Q) e$ \- p7 A8 g    while (1) {( O% S7 T+ b# I: U) L7 u" R
        cout << "输入n:";
& N' ]+ a+ N- \3 x        cin >> n;* b: h6 ?2 R8 E3 U$ _
        milliseconds start_time = duration_cast<milliseconds >(8 V* v2 ]2 O% M' J$ R  q  o
            system_clock::now().time_since_epoch()$ A% h4 l; f% V" z2 V
        );
% s0 `( l! t$ L$ V+ t3 M        function1(n);
/ |6 c, Z+ W" G/ A* ]1 n//        function2(n);- q8 I$ T( ~7 [2 m
//        function3(n);9 i- M6 \) a# D& A" ~- i" j, E
        milliseconds end_time = duration_cast<milliseconds >(
( j! ?! {/ l7 y- ^; A            system_clock::now().time_since_epoch()
: U  \& {! c% @0 u8 [" f        );" d0 i9 Z, c: {* U
        cout << "耗时:" << milliseconds(end_time).count() - milliseconds(start_time).count()
6 B6 Y; r. Q; P9 g6 g            <<" ms"<< endl;
- t8 Q0 p! |" y/ t/ s% s, v/ T5 T    }
( l& }' g; Y6 D; J9 |}
- H6 a, l, u) _5 L$ ]+ v7 R
1 n7 Q; X" u( C8 T' Z& C3 a# V
3 V6 L  D8 P0 h7 T/ l6 B
总结

本文详细分析了在leetcode上做题程序为什么会有超时,以及从硬件配置上大体知道CPU的执行速度,然后亲自做一个实验来看看O(n)的算法,跑一秒钟,这个n究竟是做大,最后给出不同时间复杂度,一秒内可以运算出来的n的大小。

建议大家也都自己做一做实验,测一测,看看是不是和我的测出来的结果差不多。

这样,大家应该对程序超时时候的数据规模有一个整体的认识了。

) N& k+ {3 ?* ]- |' u' q% i
收藏 评论0 发布时间:2021-2-20 11:16

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版