Saturday, May 31, 2014

Modules and efficient scripting - Mô-đun và kịch bản hiệu quả


Hướng dẫn này là dành cho tất cả các scripters arround amxx, đề nghị cho scripters trung gian.

Một plugin hiệu quả là đầu tiên của tất cả những gì?
Một plugin hiệu quả là sử dụng ít CPU và bộ nhớ.

Hãy để tôi đưa ra một ví dụ:
Chúng ta hãy cố gắng nghĩ ra một phương pháp để xem thời tiết một người chơi đã bị giết chết. Phương pháp này có thể được thực hiện nhiều cách khác nhau nhưng tôi sẽ chỉ ra 2 trong số họ, một tốt và một xấu.

Một trong những tốt là khi chúng ta nối các sự kiện DeathMsg
PHP Code:
register_event("DeathMsg""event_death""a")

public 
event_death()
{
    new 
attacker read_data(1)
    new 
victim read_data(2)

}  
Một xấu là chúng ta sử dụng PreThink và kiểm tra các người chơi đã bị giết, trong phương pháp này, tôi sẽ sử dụng các mô-đun engine
PHP Code:
public client_PreThink(id)
{
    if (!
is_user_alive(id))
    {
        new 
attacker get_user_attacker(id)
    }
}  
Những gì vấn đề hiệu quả * chúng ta có ở đây?
Vâng vấn đề là PreThink được gọi là> 30 lần mỗi giây và điều đó có nghĩa rằng chúng tôi đang kiểm tra quá nhiều lần người chơi đã bị giết chết. Đây là một vấn đề bởi vì nó sẽ gây ra rất nhiều sử dụng CPU, một vấn đề mà các máy tính nhỏ không thể xử lý. Bởi rất nhiều sử dụng CPU có nghĩa là chúng tôi sẽ có rất nhiều lag.

Chúng ta hãy một lần nữa cho một ví dụ tốt hơn inefficency nhưng một subtile hơn.
Chúng ta hãy cố gắng hook các sự kiện DeathMsg và nhận được tất cả các thông tin mà chúng tôi cần.
Đây là phương pháp tốt nhất của việc lấy các thông tin chúng ta cần là trong ví dụ đầu tiên. Điều thứ hai sẽ sử dụng quá nhiều hàm trong trường hợp này là không cần thiết ...
PHP Code:
register_event("DeathMsg""event_death""a")

public 
event_death()
{
    new 
attacker read_data(1)
    new 
victim read_data(2)
    new 
weapon_name[33],weapon_id
    
// get the weapon name
    
read_data(4,weapon_name,32)
    
format(weapon_name,32,"weapon_%s",weapon_name)
    
weapon_id get_weaponid(weapon_name)
}  
PHP Code:
register_event("DeathMsg""event_death""a")

public 
event_death()
{
    new 
victim read_data(2)
    new 
attacker get_user_attacker(victim)
    new 
weapon get_user_weapon(attacker)
}  
Những vấn đề hiệu quả * chúng ta có ở đây? 
Vâng vấn đề là cho đến khi tất cả các forwards event_death sẽ được gọi là, các dữ liệu mà chúng tôi có sẽ được lưu trữ trong lõi, và nó sẽ được truy cập thông qua read_data () chức năng. Vì vậy, trong trường hợp này là không cần thiết để truy cập vào các công cụ một lần nữa để có được thông tin này từ cốt lõi đã chuẩn bị cho chúng tôi.

Các mô-đun hiệu quả nhất là gì? 
Vâng câu hỏi này phụ thuộc vào những điều bạn muốn làm trong một plugin. 
Hầu hết mọi người nói rằng FakeMeta là module tốt nhất arround nhưng nó không phải là hiệu quả nhất! Module này sử dụng rất nhiều CPU bởi vì cơ sở dữ liệu lớn của chức năng mà nó cung cấp. Cho câu hỏi này là câu trả lời: tất cả các mô-đun có những điều tốt đẹp để cung cấp, chúng ta phải sử dụng các chức năng đặc biệt được cung cấp bởi mỗi mô-đun.

Điều đó có nghĩa là khi chúng ta có thể làm một điều trong engine hoặc fun, ham nó sẽ là tốt nhất để sử dụng engine / fun / ham thay vì fakemeta do rất nhiều sử dụng CPU. 
Tôi sẽ cung cấp cho bạn một ví dụ. 

Nếu chúng ta sử dụng fm_is_in_viewcone, tôi sẽ đánh dấu những nơi mà các plugin liên lạc với các mô-đun:
PHP Code:
stock bool:fm_is_in_viewcone(index, const Float:point[3]) {
    new 
Float:angles[3];
    
pev(indexpev_anglesangles); // COMUNICATION WITH MODULE
    
engfunc(EngFunc_MakeVectorsangles); // COMUNICATION WITH MODULE
    
global_get(glb_v_forwardangles); // COMUNICATION WITH MODULE
    
angles[2] = 0.0;

    new 
Float:origin[3], Float:diff[3], Float:norm[3];
    
pev(indexpev_originorigin); // COMUNICATION WITH MODULE
    
xs_vec_sub(pointorigindiff);
    
diff[2] = 0.0;
    
xs_vec_normalize(diffnorm);

    new 
Float:dotFloat:fov;
    
dot xs_vec_dot(normangles);
    
pev(indexpev_fovfov); // COMUNICATION WITH MODULE
    
if (dot >= floatcos(fov M_PI 360))
        return 
true;

    return 
false;
}  
Trong chức năng này, chúng ta thấy 5 Liên lạc với các mô-đun.

Khi chúng ta sử dụng engine ví dụ:
PHP Code:
is_in_viewcone entityFloat:origin[3] ) // COMUNICATION WITH MODULE  
Chúng tôi chỉ có một liên lạc với các mô-đun.

Đây là những gì sẽ xảy ra khi chúng tôi sử dụng fakemeta:
PLUGIN -> FAKEMETA -> METAMOD -> GAME ENGINE -> METAMOD -> FAKEMETA -> PLUGIN

Nhân với 5 lần và chúng ta sẽ thấy thời gian thực hiện của fm_is_in_viewcone. 

Đây là những gì sẽ xảy ra khi chúng tôi sử dụng mô-đun engine:
PLUGIN -> ENGINE -> METAMOD -> GAME ENGINE -> METAMOD -> ENGINE -> PLUGIN
Chỉ có một lần.
Nó cũng phụ thuộc vào dữ liệu chúng tôi muốn nhận hoặc thiết lập.

Đây là lý do tại sao trong một số trường hợp chúng ta nên sử dụng fun hoặc engine hơn fakemeta. Chúng không sử dụng nhiều CPU và có sử dụng bộ nhớ thấp trong tình huống này.
Để xác nhận những gì tôi đã nói về Liên lạc module tôi sẽ trả lời ở đây một số tiêu chuẩn sẽ so sánh fakemeta với các mô-đun cstrike và engine mô-đun natives / stocks. Bạn sẽ thấy rằng điều này làm cho sự khác biệt.

Entity_set_origin vs. fm_entity_set_origin [fakemeta vs engine]
Code:
type |                             name |      calls | time / min / max
-------------------------------------------------------------------
   n |                  register_plugin |          1 | 0.000008 / 0.000008 / 0.000008
   n |                   register_clcmd |          1 | 0.000017 / 0.000017 / 0.000017
   n |                     random_float |      60000 | 0.134438 / 0.000001 / 0.000116
   n |                       server_cmd |          1 | 0.000006 / 0.000006 / 0.000006
   p |                      plugin_init |          1 | 0.000010 / 0.000010 / 0.000010
   p |                          profile |          1 | 0.182920 / 0.182920 / 0.182920
   
   n |                entity_set_origin |      10000 | 0.035055 / 0.000002 / 0.000082
   
   n |                              pev |      20000 | 0.044686 / 0.000001 / 0.000183
   n |                          engfunc |      20000 | 0.052677 / 0.000002 / 0.000073
   f |             fm_entity_set_origin |      10000 | 0.113504 / 0.000008 / 0.000373
0 natives, 0 public callbacks, 2 function calls were not executed.
Fm_is_in_viewcone vs is_in_viewcone. [fakemeta vs engine]
Code:
type |                             name |      calls | time / min / max
-------------------------------------------------------------------
   n |                       server_cmd |          1 | 0.000008 / 0.000008 / 0.000008
   p |                      plugin_init |          1 | 0.000007 / 0.000007 / 0.000007
   p |                          profile |          1 | 0.112695 / 0.112695 / 0.112695
   n |                  register_plugin |          1 | 0.000004 / 0.000004 / 0.000004
   n |                   register_clcmd |          1 | 0.000016 / 0.000016 / 0.000016
   
   n |                      floatsqroot |      10000 | 0.022363 / 0.000001 / 0.000038
   n |                         floatdiv |      10000 | 0.022110 / 0.000001 / 0.000036
   n |                              pev |      30000 | 0.067148 / 0.000001 / 0.000086
   n |                          engfunc |      10000 | 0.025055 / 0.000001 / 0.000011
   n |                       global_get |      10000 | 0.022244 / 0.000001 / 0.000031
   n |                         floatcos |      10000 | 0.022657 / 0.000001 / 0.000209
   n |                     random_float |      30000 | 0.066903 / 0.000001 / 0.000035
   f |             operator/(Float:,_:) |      10000 | 0.022434 / 0.000001 / 0.000077
   f |        operator>=(Float:,Float:) |      10000 | 0.021916 / 0.000001 / 0.000011
   f |                         xs_rsqrt |      10000 | 0.067513 / 0.000004 / 0.000110
   f |                       xs_vec_sub |      10000 | 0.021978 / 0.000001 / 0.000009
   f |                 xs_vec_normalize |      10000 | 0.045749 / 0.000003 / 0.000073
   f |                       xs_vec_dot |      10000 | 0.022200 / 0.000001 / 0.000047
   f |                fm_is_in_viewcone |      10000 | 0.271947 / 0.000022 / 0.000220
   
   n |                   is_in_viewcone |      10000 | 0.025373 / 0.000002 / 0.000104
   
6 natives, 0 public callbacks, 2 function calls were not executed.
Fm_entity_range vs entity_range [fakemeta vs engine]
Code:
type |                             name |      calls | time / min / max
-------------------------------------------------------------------
   n |                  register_plugin |          1 | 0.000008 / 0.000008 / 0.000008
   n |                   register_clcmd |          1 | 0.000016 / 0.000016 / 0.000016
   
   n |                       server_cmd |          1 | 0.000004 / 0.000004 / 0.000004
   p |                      plugin_init |          1 | 0.000011 / 0.000011 / 0.000011
   p |                          profile |          1 | 0.046530 / 0.046530 / 0.046530
   
   n |                     entity_range |      10000 | 0.022555 / 0.000001 / 0.000056
   
   f |                  fm_entity_range |      10000 | 0.092081 / 0.000006 / 0.000087
   n |                              pev |      20000 | 0.044973 / 0.000001 / 0.000037
   n |                   get_distance_f |      10000 | 0.022852 / 0.000001 / 0.000025
0 natives, 0 public callbacks, 2 function calls were not executed.
Ctrike module vs Fakemeta module.
Code:
type |                             name |      calls | time / min / max
-------------------------------------------------------------------
   n |                    get_pdata_int |     300000 | 0.647404 / 0.000001 / 0.000253
   n |                    set_pdata_int |     150064 | 0.328161 / 0.000001 / 0.000331
   n |                   get_user_msgid |          2 | 0.000006 / 0.000003 / 0.000004
   n |                   emessage_begin |     150064 | 0.379386 / 0.000002 / 0.000309
   n |                      ewrite_long |     100000 | 0.238946 / 0.000001 / 0.000458
   n |                      ewrite_byte |     225160 | 0.518973 / 0.000001 / 0.000357
   n |                     emessage_end |     150064 | 0.374745 / 0.000002 / 0.000218
   n |                          set_pev |      50064 | 0.110391 / 0.000001 / 0.000077
   n |                    ewrite_string |      50064 | 0.121614 / 0.000001 / 0.000250
   n |                  register_plugin |          1 | 0.000007 / 0.000007 / 0.000007
   n |                   register_clcmd |          1 | 0.000016 / 0.000016 / 0.000016
   n |                           random |     300000 | 0.648347 / 0.000001 / 0.000217
   n |                       server_cmd |          1 | 0.000004 / 0.000004 / 0.000004
   p |                        benchmark |          1 | 2.511592 / 2.511592 / 2.511592
   p |                      plugin_init |          1 | 0.000007 / 0.000007 / 0.000007
   
   n |                cs_get_user_money |     100000 | 0.220167 / 0.000001 / 0.000414
   f |             fm_cs_get_user_money |     100000 | 0.446478 / 0.000003 / 0.000413
   
   n |                cs_set_user_money |     100000 | 0.276739 / 0.000002 / 0.000329
   f |             fm_cs_set_user_money |     100000 | 1.344050 / 0.000010 / 0.001747
   
   n |               cs_get_user_defuse |     100000 | 0.220294 / 0.000001 / 0.000094
   f |            fm_cs_get_user_defuse |     100000 | 0.452164 / 0.000003 / 0.000161
   
   n |               cs_set_user_defuse |     100000 | 0.336809 / 0.000002 / 0.000270
   f |            fm_cs_set_user_defuse |     100000 | 1.294073 / 0.000003 / 0.000333
Bây giờ chúng ta sẽ nói về mô-đun forwards. Chúng ta có thể thấy rằng chúng tôi có rất nhiều forwards mô-đun của chúng tôi có thể cung cấp. Ở đây chúng tôi sẽ đề cập đến chỉ fakemeta, engine và forwards hamsandwich. 
Sự khác biệt giữa các mô-đun forwards là gì? Vâng sự khác biệt chính của forwards là phương pháp đăng ký. 

Mô-đun cung cấp Những tiền đạo hiệu quả nhất? 
Sự khác biệt khi chúng ta nói về hiệu quả * sẽ được gần như không đáng kể.

Code:
type |                             name |      calls | time / min / max
-------------------------------------------------------------------
   n |                  register_plugin |          1 | 0.000010 / 0.000010 / 0.000010
   n |                 register_forward |          1 | 0.000008 / 0.000008 / 0.000008
   n |                       server_cmd |          1 | 0.000016 / 0.000016 / 0.000016
   p |                   playerPreThink |       2001 | 0.004775 / 0.000002 / 0.000007
   p |                      plugin_init |          1 | 0.000007 / 0.000007 / 0.000007
0 natives, 0 public callbacks, 2 function calls were not executed.


date: Mon Mar 30 15:46:09 2009 map: de_dust2
type |                             name |      calls | time / min / max
-------------------------------------------------------------------
   n |                  register_plugin |          1 | 0.000005 / 0.000005 / 0.000005
   n |                       server_cmd |          1 | 0.000016 / 0.000016 / 0.000016
   p |                  client_PreThink |       2001 | 0.004718 / 0.000002 / 0.000018
   p |                      plugin_init |          1 | 0.000004 / 0.000004 / 0.000004
Bây giờ chúng ta so sánh các thao tác tài sản thực thể của engine so với fakemeta. Fakemeta sẽ có một chút chậm hơn so với engine nhưng đủ tốt để sử dụng.
Code:
type |                             name |      calls | time / min / max
-------------------------------------------------------------------
   n |                  register_plugin |          1 | 0.000009 / 0.000009 / 0.000009
   n |                   register_clcmd |          1 | 0.000013 / 0.000013 / 0.000013
   n |                     random_float |     300000 | 0.665777 / 0.000001 / 0.000323
   n |                          set_pev |     100000 | 0.222021 / 0.000001 / 0.000154
   n |                entity_set_vector |     100000 | 0.220881 / 0.000001 / 0.000152
   n |                       server_cmd |          1 | 0.000008 / 0.000008 / 0.000008
   p |                      plugin_init |          1 | 0.000007 / 0.000007 / 0.000007
   p |                          profile |          1 | 1.132186 / 1.132186 / 1.132186
0 natives, 0 public callbacks, 2 function calls were not executed.
Code:
type |                             name |      calls | time / min / max
-------------------------------------------------------------------
   n |                   precache_model |          1 | 0.015410 / 0.015410 / 0.015410
   n |                  register_plugin |          1 | 0.000007 / 0.000007 / 0.000007
   n |                   register_clcmd |          1 | 0.000016 / 0.000016 / 0.000016
   n |                       server_cmd |          1 | 0.000007 / 0.000007 / 0.000007
   p |                      plugin_init |          1 | 0.000008 / 0.000008 / 0.000008
   p |                  plugin_precache |          1 | 0.000008 / 0.000008 / 0.000008
   p |                          profile |          1 | 0.466186 / 0.466186 / 0.466186
   
   n |                        pev_valid |     100000 | 0.228750 / 0.000001 / 0.000165
   n |                     is_valid_ent |     100000 | 0.226901 / 0.000001 / 0.000095
Bây giờ chúng ta phải nghĩ rằng đó là tốt để lưu lại kết quả hay không. 
Lợi thế đó sẽ xuất hiện một khi chúng ta bộ nhớ đệm chúng là gì? 
Tốt lợi thế là nó sẽ sử dụng ít CPU và nó sẽ làm cho một sự khác biệt lớn khi chúng ta nói về kiểm tra lặp đi lặp lại (như forwards prethink), bất lợi là nó sử dụng bộ nhớ. 

Có nghĩa là gì để kết quả bộ nhớ đệm? 
Ví dụ chúng tôi muốn biết một người chơi là một bot. 
Thay vì sử dụng chức năng này tất cả các thời gian:
PHP Code:
is_user_bot(id)  
Nó là tốt hơn để làm điều này:
PHP Code:
new bool:cl_is_bot[33]
public 
client_putinserver(id)
{
    if (
is_user_bot(id))
        
cl_is_bot[id] = true;
}
// now we only use cl_is_bot[id] not is_user_bot(id)  
Thay vì mất thời gian với mô-đun Liên lạc chúng tôi chỉ cần truy cập vào bộ nhớ Mà sẽ làm cho mọi việc nhanh hơn nhiều!
Bạn phải nhớ rằng việc này sẽ làm cho bạn Plugin khó khăn hơn để thực hiện.

Đây là một ví dụ với kết quả bộ nhớ cache và các bài kiểm tra, bạn sẽ thấy rằng chúng ta sẽ có một sự khác biệt lớn, vì vậy nó sẽ được khuyến khích sử dụng bộ nhớ đệm để làm cho một plugin hiệu quả.
Đây là ý tưởng chính của những gì MeRcyLeZZ muốn làm để cải thiện các plugin của mình.
http://forums.alliedmods.net/showpos...91&postcount=1
Đây là kết quả
http://forums.alliedmods.net/showpos...28&postcount=3

Tôi sẽ trích quan điểm về Fakemate của Hawk552s (fakemeta sử dụng chung).
Quote:
Originally Posted by Hawk552 View Post
I didn't say that. Porting stuff to Fakemeta is not a be-all end-all solution. There are many, many reasons not to use Fakemeta. Here are some of them:
  • Multiforwards as used in Engine and Fun are far more efficient once more than one plugin is using it
  • Engine and Fun are simpler to use and easier to read
  • Engine and Fun natives often require less parameters and as such the VM layer can run faster
  • More people know how to use Engine and Fun
  • Fakemeta has far less protection against bad use and can crash a server much more easily
  • Engine and Fun are better documented with more available support and example code
  • Engine and Fun themselves act as wrappers which means that if an update breaks something, plugins do not have to be recompiled - AMXX can just be updated. This is also beneficial as less code is run in the VM layer, making it faster
Tell me: can you know whether the user is already running a plugin using Engine or Fun? Would it even be worth the resources to check?

Why are we fretting over these trivial optimizations (which you're actually largely part wrong about being faster in the first place) as opposed to making code legible? Why should I write code that is 4x as long and is slower just because the API I'm using is more capable? Why don't we just use ASM, under that reasoning?

We've had this debate before. GHW_Chronic made it a requirement for plugins to only use Fakemeta. This makes absolutely no sense. There's no reason we can't use Fun and Engine for everything other than the things only Fakemeta can do.

I've written many APIs (see Apollo RP in my signature); I can guarantee you, far more than you have. From that experience, I know that capability is not the most important quality of an API. An API, especially for something like AMXX, should be simple. The way we have the APIs divided up is great. We should only encourage people to use Fakemeta when it is necessary and no other module can accomplish the task at hand.

There is absolutely no reason to make a point of using Fakemeta exclusively unless you're trying to appear intelligent or you actually know it better than Engine and Fun. Stop telling beginners to use Fakemeta, it will only confuse them and makes things more complicated than they have to be.
Conclusions:
We must use the module when it has a specific good function that others don't provide. 
The less module comunications the better!!!
And remember this tutorial does not want to say not to use fakemeta! It advises to use it only when it needs to be used!
A plugin isn't bad if it uses 10 modules instead of just 2...
If you propose to cache some results in a plugin, it will be more efficient but there will be a problem when we talk about easy scripting.

Good tutorials that fit in with this one:
http://forums.alliedmods.net/showthread.php?t=43049
http://forums.alliedmods.net/showthread.php?t=40340
http://forums.alliedmods.net/showthr...296#post809296

Benchmark tool, for the people who want to do some more tests, if you want feel free to post the results in the efficency tutorial: 
http://forums.alliedmods.net/showthread.php?t=67752

My comment: Hope that it is ok, if you would like to add some more please tell me. I'm not used on writing such long posts... [Hope that my english was fine ...]
I thank all that spend their time reading this tutorial, suggested improvements and contributed to this tutorial!

0 nhận xét:

Post a Comment