[Erlang 0125] Know a little Erlang opcode

简介:   Erlang源代码编译为beam文件,代码要经过一系列的过程(见下面的简图),Core Erlang之前已经简单介绍过了Core Erlang,代码转换为Core Erlang,就容易拨开一些语法糖的真面目了.
  Erlang源代码编译为beam文件,代码要经过一系列的过程(见下面的简图),Core Erlang之前已经简单介绍过了Core Erlang,代码转换为Core Erlang,就容易拨开一些语法糖的真面目了.下一阶段就是将Core Erlang转换为opcode,使用c(m,'S')生成的是.S文件可以看到反编译的代码.编译器最终输出的是Virtual Beam Code 但这还不是最终VM执行的代码,在erts\emulator\beam\beam_load.c执行的过程中会完成指令优化,优化之后的代码可以通过erts_debug:df(m)生成.dis文件查看,换句话说,这个方法执行成功必须要要求模块已经加载.

 

下面的代码里面,模块t还没有编译,所以第一次执行erts_debug:df(t)返回值是{undef,t}

?
1
2
3
4
5
6
7
8
9
10
11
12
13
Eshell V6.0  (abort with ^G)
1> c(t, 'S' ).
** Warning: No object file created - nothing loaded **
ok
2> erts_debug:df(t).
{undef,t}
3> c(t).
{ok,t}
4> erts_debug:df(t).
ok
5> q().
ok
6>

  

   之前有两次使用过反汇编的代码:

 

    [Erlang 0029] Erlang Inline编译 [链接] 里面我们使用过反编译的代码观察inline机制.
    [Erlang 0100] make_ref 与 Selective Receive [链接]通过反汇编代码看到selective receive的优化处理.


    除了Erlang源代码几乎没有什么资料可供参考.有两个方法可以继续跟踪下去,一是通过erts_debug:instructions()输出所有命令,再就是编写erl代码对照.S代码去推测意义.霸爷有一篇<实验Erlang语法对应的opcode 让你对erlang理解更深>就是这样做的,下面把我比较关注的一些地方做下测试.

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
Eshell V6.0  (abort with ^G)
1> rp(erts_debug:instructions()).
[ "allocate_tt" , "allocate_heap_tIt" , "allocate_heap_zero_tIt" ,
"allocate_init_tIy" , "allocate_zero_tt" , "apply_I" ,
"apply_bif" , "apply_last_IP" , "badarg_j" , "badmatch_r" ,
"badmatch_x" , "badmatch_y" , "bif1_fbsd" , "bif1_body_bsd" ,
"bs_context_to_binary_r" , "bs_context_to_binary_x" ,
"bs_context_to_binary_y" , "bs_init_writable" ,
"bs_put_string_II" , "bs_test_tail_imm2_frI" ,
"bs_test_tail_imm2_fxI" , "bs_test_unit_frI" ,
"bs_test_unit_fxI" , "bs_test_unit8_fr" , "bs_test_unit8_fx" ,
"bs_test_zero_tail2_fr" , "bs_test_zero_tail2_fx" ,
"call_bif_e" , "call_error_handler" , "call_nif" , "case_end_r" ,
"case_end_x" , "case_end_y" , "catch_yf" , "catch_end_y" ,
"continue_exit" , "deallocate_I" , "deallocate_return_Q" ,
"error_action_code" , "extract_next_element_x" ,
"extract_next_element_y" , "extract_next_element2_x" ,
"extract_next_element2_y" , "extract_next_element3_x" ,
"extract_next_element3_y" , "fclearerror" , "fconv_dl" ,
"fmove_ql" , "fmove_ld" , "fmove_dl" , "get_list_rrx" ,
"get_list_rry" , "get_list_rxr" , "get_list_rxx" , "get_list_rxy" ,
"get_list_ryr" , "get_list_ryx" , "get_list_ryy" , "get_list_xrx" ,
"get_list_xry" , "get_list_xxr" , "get_list_xxx" , "get_list_xxy" ,
"get_list_xyr" , "get_list_xyx" , "get_list_xyy" , "get_list_yrx" ,
"get_list_yry" , "get_list_yxr" , "get_list_yxx" , "get_list_yxy" ,
"get_list_yyr" , "get_list_yyx" , "get_list_yyy" ,
"hipe_call_count" , "hipe_trap_call" , "hipe_trap_call_closure" ,
"hipe_trap_resume" , "hipe_trap_return" , "hipe_trap_throw" ,
"i_apply" , "i_apply_fun" , "i_apply_fun_last_P" ,
"i_apply_fun_only" , "i_apply_last_P" , "i_apply_only" ,
"i_band_jId" , "i_bif2_fbd" , "i_bif2_body_bd" , "i_bor_jId" ,
"i_bs_add_jId" , "i_bs_append_jIIId" ,
"i_bs_get_binary2_frIsId" , "i_bs_get_binary2_fxIsId" ,
"i_bs_get_binary_all2_frIId" , "i_bs_get_binary_all2_fxIId" ,
"i_bs_get_binary_all_reuse_rfI" ,
"i_bs_get_binary_all_reuse_xfI" ,
"i_bs_get_binary_imm2_frIIId" , "i_bs_get_binary_imm2_fxIIId" ,
"i_bs_get_float2_frIsId" , "i_bs_get_float2_fxIsId" ,
"i_bs_get_integer_fIId" , "i_bs_get_integer_16_rfd" ,
"i_bs_get_integer_16_xfd" , "i_bs_get_integer_32_rfId" ,
"i_bs_get_integer_32_xfId" , "i_bs_get_integer_8_rfd" ,
"i_bs_get_integer_8_xfd" , "i_bs_get_integer_imm_rIIfId" ,
"i_bs_get_integer_imm_xIIfId" ,
"i_bs_get_integer_small_imm_rIfId" ,
"i_bs_get_integer_small_imm_xIfId" , "i_bs_get_utf16_rfId" ,
"i_bs_get_utf16_xfId" , "i_bs_get_utf8_rfd" ,
"i_bs_get_utf8_xfd" , "i_bs_init_IId" , "i_bs_init_bits_IId" ,
"i_bs_init_bits_fail_rjId" , "i_bs_init_bits_fail_xjId" ,
"i_bs_init_bits_fail_yjId" , "i_bs_init_bits_fail_heap_IjId" ,
"i_bs_init_bits_heap_IIId" , "i_bs_init_fail_rjId" ,
"i_bs_init_fail_xjId" , "i_bs_init_fail_yjId" ,
"i_bs_init_fail_heap_IjId" , "i_bs_init_heap_IIId" ,
"i_bs_init_heap_bin_IId" , "i_bs_init_heap_bin_heap_IIId" ,
"i_bs_match_string_rfII" , "i_bs_match_string_xfII" ,
"i_bs_private_append_jId" , "i_bs_put_utf16_jIs" ,
"i_bs_put_utf8_js" , "i_bs_restore2_rI" , "i_bs_restore2_xI" ,
"i_bs_save2_rI" , "i_bs_save2_xI" , "i_bs_skip_bits2_frxI" ,
"i_bs_skip_bits2_fryI" , "i_bs_skip_bits2_fxrI" ,
"i_bs_skip_bits2_fxxI" , "i_bs_skip_bits2_fxyI" ,
"i_bs_skip_bits_all2_frI" , "i_bs_skip_bits_all2_fxI" ,
"i_bs_skip_bits_imm2_frI" , "i_bs_skip_bits_imm2_fxI" ,
"i_bs_start_match2_rfIId" , "i_bs_start_match2_xfIId" ,
"i_bs_start_match2_yfIId" , "i_bs_utf16_size_sd" ,
"i_bs_utf8_size_sd" , "i_bs_validate_unicode_js" ,
"i_bs_validate_unicode_retract_j" , "i_bsl_jId" , "i_bsr_jId" ,
"i_bxor_jId" , "i_call_f" , "i_call_ext_e" , "i_call_ext_last_eP" ,
"i_call_ext_only_e" , "i_call_fun_I" , "i_call_fun_last_IP" ,
"i_call_last_fP" , "i_call_only_f" , "i_debug_breakpoint" ,
"i_element_rjsd" , "i_element_xjsd" , "i_element_yjsd" ,
"i_fadd_lll" , "i_fast_element_rjId" , "i_fast_element_xjId" ,
"i_fast_element_yjId" , "i_fcheckerror" , "i_fdiv_lll" ,
"i_fetch_rx" , "i_fetch_ry" , "i_fetch_xr" , "i_fetch_xx" ,
"i_fetch_xy" , "i_fetch_yr" , "i_fetch_yx" , "i_fetch_yy" ,
"i_fetch_rc" , "i_fetch_xc" , "i_fetch_yc" , "i_fetch_cr" ,
"i_fetch_cx" , "i_fetch_cy" , "i_fetch_cc" , "i_fetch_ss" ,
"i_fmul_lll" , "i_fnegate_ll" , "i_fsub_lll" , "i_func_info_IaaI" ,
"i_gc_bif1_jIsId" , "i_gc_bif2_jIId" , "i_gc_bif3_jIsId" ,
"i_generic_breakpoint" , "i_get_sd" , "i_get_map_element_frar" ,
"i_get_map_element_frax" , "i_get_map_element_fray" ,
"i_get_map_element_frxr" , "i_get_map_element_frxx" ,
"i_get_map_element_frxy" , "i_get_map_element_fxar" ,
"i_get_map_element_fxax" , "i_get_map_element_fxay" ,
"i_get_map_element_fxxr" , "i_get_map_element_fxxx" ,
"i_get_map_element_fxxy" , "i_get_map_element_fyar" ,
"i_get_map_element_fyax" , "i_get_map_element_fyay" ,
"i_get_map_element_fyxr" , "i_get_map_element_fyxx" ,
"i_get_map_element_fyxy" , "i_get_map_elements_fsI" ,
"i_get_tuple_element_rPr" , "i_get_tuple_element_rPx" ,
"i_get_tuple_element_rPy" , "i_get_tuple_element_xPr" ,
"i_get_tuple_element_xPx" , "i_get_tuple_element_xPy" ,
"i_get_tuple_element_yPr" , "i_get_tuple_element_yPx" ,
"i_get_tuple_element_yPy" , "i_has_map_field_fra" ,
"i_has_map_field_frr" , "i_has_map_field_frx" ,
"i_has_map_field_fry" , "i_has_map_field_fxa" ,
"i_has_map_field_fxr" , "i_has_map_field_fxx" ,
"i_has_map_field_fxy" , "i_has_map_field_fya" ,
"i_has_map_field_fyr" , "i_has_map_field_fyx" ,
"i_has_map_field_fyy" , "i_has_map_fields_fsI" , "i_hibernate" ,
"i_increment_rIId" , "i_increment_xIId" , "i_increment_yIId" ,
"i_int_bnot_jsId" , "i_int_div_jId" , "i_is_eq_f" ,
"i_is_eq_exact_f" , "i_is_eq_exact_immed_frc" ,
"i_is_eq_exact_immed_fxc" , "i_is_eq_exact_immed_fyc" ,
"i_is_eq_exact_literal_rfc" , "i_is_eq_exact_literal_xfc" ,
"i_is_eq_exact_literal_yfc" , "i_is_ge_f" , "i_is_lt_f" ,
"i_is_ne_f" , "i_is_ne_exact_f" , "i_is_ne_exact_immed_frc" ,
"i_is_ne_exact_immed_fxc" , "i_is_ne_exact_immed_fyc" ,
"i_is_ne_exact_literal_rfc" , "i_is_ne_exact_literal_xfc" ,
"i_is_ne_exact_literal_yfc" , "i_jump_on_val_rfII" ,
"i_jump_on_val_xfII" , "i_jump_on_val_yfII" ,
"i_jump_on_val_zero_rfI" , "i_jump_on_val_zero_xfI" ,
"i_jump_on_val_zero_yfI" , "i_loop_rec_fr" , "i_m_div_jId" ,
"i_make_fun_It" , "i_minus_jId" , "i_move_call_crf" ,
"i_move_call_ext_cre" , "i_move_call_ext_last_ePcr" ,
"i_move_call_ext_only_ecr" , "i_move_call_last_fPcr" ,
"i_move_call_only_fcr" , "i_new_bs_put_binary_jsIs" ,
"i_new_bs_put_binary_all_jsI" , "i_new_bs_put_binary_imm_jIs" ,
"i_new_bs_put_float_jsIs" , "i_new_bs_put_float_imm_jIIs" ,
"i_new_bs_put_integer_jsIs" , "i_new_bs_put_integer_imm_jIIs" ,
"i_plus_jId" , "i_put_tuple_rI" , "i_put_tuple_xI" ,
"i_put_tuple_yI" , "i_recv_set" , "i_rem_jId" ,
"i_return_time_trace" , "i_return_to_trace" ,
"i_select_tuple_arity_rfI" , "i_select_tuple_arity_xfI" ,
"i_select_tuple_arity_yfI" , "i_select_tuple_arity2_rfAfAf" ,
"i_select_tuple_arity2_xfAfAf" ,
"i_select_tuple_arity2_yfAfAf" , "i_select_val_rfI" ,
"i_select_val_xfI" , "i_select_val_yfI" ,
"i_select_val2_rfcfcf" , "i_select_val2_xfcfcf" ,
"i_select_val2_yfcfcf" , "i_times_jId" , "i_trim_I" ,
"i_wait_error" , "i_wait_error_locked" , "i_wait_timeout_fI" ,
"i_wait_timeout_fs" , "i_wait_timeout_locked_fI" ,
"i_wait_timeout_locked_fs" , "i_yield" , "if_end" , "init_y" ,
"init2_yy" , "init3_yyy" , "int_code_end" , "is_atom_fr" ,
"is_atom_fx" , "is_atom_fy" , "is_binary_fr" , "is_binary_fx" ,
"is_binary_fy" , "is_bitstring_fr" , "is_bitstring_fx" ,
"is_bitstring_fy" , "is_boolean_fr" , "is_boolean_fx" ,
"is_boolean_fy" , "is_float_fr" , "is_float_fx" , "is_float_fy" ,
"is_function_fr" , "is_function_fx" , "is_function_fy" ,
"is_function2_fss" , "is_integer_fr" , "is_integer_fx" ,
"is_integer_fy" , "is_integer_allocate_frII" ,
"is_integer_allocate_fxII" , "is_list_fr" , "is_list_fx" ,
"is_list_fy" , "is_map_fr" , "is_map_fx" , "is_map_fy" ,
"is_nil_fr" , "is_nil_fx" , "is_nil_fy" ,
"is_non_empty_list_test_heap_frIt" , "is_nonempty_list_fr" ,
"is_nonempty_list_fx" , "is_nonempty_list_fy" ,
"is_nonempty_list_allocate_frIt" ,
"is_nonempty_list_allocate_fxIt" , "is_number_fr" ,
"is_number_fx" , "is_number_fy" , "is_pid_fr" , "is_pid_fx" ,
"is_pid_fy" , "is_port_fr" , "is_port_fx" , "is_port_fy" ,
"is_reference_fr" , "is_reference_fx" , "is_reference_fy" ,
"is_tuple_fr" , "is_tuple_fx" , "is_tuple_fy" ,
"is_tuple_of_arity_frA" , "is_tuple_of_arity_fxA" ,
"is_tuple_of_arity_fyA" , "jump_f" , "label_L" , "line_I" ,
"loop_rec_end_f" , "move_nr" , "move_nx" , "move_rx" , "move_ry" ,
"move_xr" , "move_xx" , "move_xy" , "move_yr" , "move_yx" , "move_yy" ,
"move_cr" , "move_cx" , "move2_xxxx" , "move2_xyxy" , "move2_yxyx" ,
"move_call_xrf" , "move_call_yrf" , "move_call_last_xrfQ" ,
"move_call_last_yrfQ" , "move_call_only_xrf" ,
"move_deallocate_return_nrQ" , "move_deallocate_return_xrQ" ,
"move_deallocate_return_yrQ" , "move_deallocate_return_crQ" ,
"move_jump_fn" , "move_jump_fx" , "move_jump_fy" , "move_jump_fc" ,
"move_return_nr" , "move_return_xr" , "move_return_cr" ,
"move_x1_c" , "move_x2_c" , "new_map_jdII" , "node_r" , "node_x" ,
"node_y" , "normal_exit" , "on_load" , "put_list_rnx" ,
"put_list_rxr" , "put_list_rxx" , "put_list_ryx" , "put_list_xnx" ,
"put_list_xrr" , "put_list_xrx" , "put_list_xxr" , "put_list_xxx" ,
"put_list_xyr" , "put_list_xyx" , "put_list_ynx" , "put_list_yrr" ,
"put_list_yrx" , "put_list_yxr" , "put_list_yxx" , "put_list_yyr" ,
"put_list_yyx" , "put_list_rcr" , "put_list_rcx" , "put_list_rcy" ,
"put_list_xcr" , "put_list_xcx" , "put_list_xcy" , "put_list_ycr" ,
"put_list_ycx" , "put_list_ycy" , "put_list_crr" , "put_list_crx" ,
"put_list_cry" , "put_list_cxr" , "put_list_cxx" , "put_list_cxy" ,
"put_list_cyr" , "put_list_cyx" , "put_list_cyy" , "put_list_ssd" ,
"raise_ss" , "recv_mark_f" , "remove_message" , "return" ,
"return_trace" , "self_r" , "self_x" , "self_y" , "send" ,
"set_tuple_element_sdP" , "system_limit_j" , "test_arity_frA" ,
"test_arity_fxA" , "test_arity_fyA" , "test_heap_It" ,
"test_heap_1_put_list_Iy" , "timeout" , "timeout_locked" ,
"try_case_end_s" , "try_end_y" , "update_map_assoc_jsdII" ,
"update_map_exact_jsdII" , "wait_f" , "wait_locked_f" ,
"wait_unlocked_f" ]
ok
2>

 

回顾 [Erlang 0010] Erlang 热更新 [ 链接 ],看看两种调用时候为什么m:f(a)会触发热更新,测试代码如下:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
loop() ->
     receive
         code_switch ->
             m:loop();
         Msg ->
             io:format( "." ),
             loop()
     end.
 
  
 
%% t.S文件
 
 
{function, loop, 0, 9}.
   {label,8}.
     {line,[{location, "t.erl" ,14}]}.
     {func_info,{atom,t},{atom,loop},0}.
   {label,9}.
     {allocate,0,0}.
     {line,[{location, "t.erl" ,15}]}.
   {label,10}.
     {loop_rec,{f,12},{x,0}}.
     {test,is_eq_exact,{f,11},[{x,0},{atom,code_switch}]}.
     remove_message.
     {line,[{location, "t.erl" ,17}]}.
     {call_ext_last,0,{extfunc,m,loop,0},0}.
   {label,11}.
     remove_message.
     {move,{literal, "." },{x,0}}.
     {line,[{location, "t.erl" ,19}]}.
     {call_ext,1,{extfunc,io,format,1}}.
     {call_last,0,{f,9},0}.
   {label,12}.
     {wait,{f,10}}.
 
  
 
%% t.dis文件
 
  
 
00007F9D864F6C38: i_func_info_IaaI 0 t loop 0
00007F9D864F6C60: allocate_tt 0 0
00007F9D864F6C70: i_loop_rec_fr f(00007F9D864F6CF0) x(0)
00007F9D864F6C80: i_is_eq_exact_immed_frc f(00007F9D864F6CB8) x(0) code_switch
00007F9D864F6C98: remove_message
00007F9D864F6CA0: i_call_ext_last_eP m:loop/0 0
00007F9D864F6CB8: remove_message
00007F9D864F6CC0: i_move_call_ext_cre "." x(0) io:format/1
00007F9D864F6CD8: i_call_last_fP t:loop/0 0
00007F9D864F6CF0: wait_f f(00007F9D864F6C70)

   

 然后,使用这个方法,我把ErlangEfficiency Guide 里面大部分代码都做了一下测试,目的是想从一个新的角度切入以前的问题,其中一个比较有意思的是:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
bin()->
  Bin0 = <<0>>,                    %% 1
 
  Bin1 = <<Bin0/binary,1,2,3>>,    %% 2
 
  Bin2 = <<Bin1/binary,4,5,6>>,    %% 3
 
  Bin3 = <<Bin2/binary,7,8,9>>,    %% 4
 
  Bin4 = <<Bin1/binary,17>>,       %% 5 !!!
 
  {Bin4,Bin3}.                      %% 6

  

这个方法由于中间的变量都是已知的,编译之后直接使用运算结果了:

?
1
2
3
4
5
6
7
{function, bin, 0, 14}.
   {label,13}.
     {line,[{location, "t.erl" ,25}]}.
     {func_info,{atom,t},{atom,bin},0}.
   {label,14}.
     {move,{literal,{<<0,1,2,3,17>>,<<0,1,2,3,4,5,6,7,8,9>>}},{x,0}}.
     return .

 

印象比较深刻的是 http://www.erlang.org/doc/efficiency_guide/functions.html#id67136 里面提到函数分支的例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
atom_map1(one) -> 1;
atom_map1(two) -> 2;
atom_map1(three) -> 3;
atom_map1(Int) when is_integer(Int) -> Int;
atom_map1(four) -> 4;
atom_map1(five) -> 5;
atom_map1(six) -> 6.
 
atom_map2(one) -> 1;
atom_map2(two) -> 2;
atom_map2(three) -> 3;
atom_map2(four) -> 4;
atom_map2(five) -> 5;
atom_map2(six) -> 6;
atom_map2(Int) when is_integer(Int) -> Int.
 
atom_map3(Int) when is_integer(Int) -> Int;
atom_map3(one) -> 1;
atom_map3(two) -> 2;
atom_map3(three) -> 3;
atom_map3(four) -> 4;
atom_map3(five) -> 5;
atom_map3(six) -> 6.

 

对于atom_map1为什么有问题文档上是这样说的:

 

First the input value is compared to one, two, and three (using a single instruction that does a binary search; thus, quite efficient even if there are many values) to select which one of the first three clauses to execute (if any).

If none of the first three clauses matched, the fourth clause will match since a variable always matches. If the guard test is_integer(Int) succeeds, the fourth clause will be executed.

If the guard test failed, the input value is compared to four, five, and six, and the appropriate clause is selected. (There will be a function_clause exception if none of the values matched.)

 

   二分查找,优化等等,虽然道理能明白,但是具体到细节就有点恍惚,看下t.S文件就明白了,代码太长被我折叠了,所谓的二分查找就是select_val指令实现的,可以看到atom_map2,atom_map3都是将所有的分支放在一起做二分查找,而atom_map1的执行过程被分成了三段;再回头看上面的说明,是不是就清晰多了.

 

  

 

  我非常喜欢汇丰银行的一句宣传语"不同的视角,打开迥然不同的世界",使用不同的视角对同一个技术问题也会有不同的理解.

 

 

参考资料:

[0] A Peek Inside the Erlang Compiler http://prog21.dadgum.com/127.html

[1] Erlang源码汇编格式 http://blog.yufeng.info/archives/498

[2] Erlang代码反编译以及查看汇编码 http://blog.yufeng.info/archives/1599

[3] 实验Erlang语法对应的opcode 让你对erlang理解更深 http://blog.yufeng.info/archives/34 

 



本文转自博客园坚强2002的博客,原文链接:

http://www.cnblogs.com/me-sa/p/know-a-little-erlang-opcode.html

如需转载请自行联系原博主。


目录
相关文章
|
9月前
|
人工智能 搜索推荐 数据可视化
2025年国内知名智能营销产品(企业级智能营销系统)深度解析:功能亮点与市场排名
本文深度解析瓴羊Quick Audience、神策、致趣百川等主流用户智能运营产品,在功能、市场表现与行业应用三大维度对比,助力企业根据业务需求精准选型,提升运营效率与竞争力。2025年国内知名智能营销产品(企业级智能营销系统)深度解析:功能亮点与市场排名。
|
网络协议 API 开发者
无线通信模块通过TCP/IP协议实现与PC端的数据传输
本文介绍了无线通信模块借助TCP/IP协议向PC端传输数据的过程,包括数据封装、发送和接收,并以WIFI模块为例,讨论了在QT平台下实现无线数据传输的方法。通过QTcpSocket类,开发者能轻松建立WIFI模块与PC间的连接。随着无线通信技术的进步,未来将有更多创新应用出现。
621 2
|
Android开发
鸿蒙开发:自定义一个简单的标题栏
本身就是一个很简单的标题栏组件,没有什么过多的技术含量,有一点需要注意,当使用沉浸式的时候,注意标题栏的位置,需要避让状态栏。
376 5
鸿蒙开发:自定义一个简单的标题栏
|
算法 测试技术 异构计算
【SAM模型超级进化】MobileSAM轻量化的分割一切大模型出现,模型缩小60倍,速度提高40倍,效果不减
【SAM模型超级进化】MobileSAM轻量化的分割一切大模型出现,模型缩小60倍,速度提高40倍,效果不减
|
文件存储 数据安全/隐私保护
用旧手机搭建服务器并实现内网穿透不需要root(本人亲测很多次最简单的一个)
用旧手机搭建服务器并实现内网穿透不需要root(本人亲测很多次最简单的一个)
1469 0
|
3天前
|
存储 弹性计算 缓存
阿里云服务器租赁费用:新版租赁收费标准及活动报价参考
本文更新了2026年阿里云全系列云服务器租赁活动报价,所有特惠资源均可前往阿里云活动中心选购,整体覆盖从个人入门到企业级高性能场景的全梯度需求。其中轻量应用服务器主打极致性价比,2核2G峰值200M带宽配置每日10点、15点限时抢购价仅38元/年,2核4G配置379元/年起;高性价比的经济型e实例、通用算力型u2i实例覆盖2核4G至4核32G全档位,适配开发测试与中小型企业业务;搭载英特尔至强6处理器的第九代c9i企业级实例算力较上代提升20%,支撑高并发生产环境,不同实例规格价差清晰,用户可根据自身业务负载与预算灵活选型。
1434 109
|
10天前
|
云安全 人工智能 运维
阿里云联动百位企业安全专家,共识Agent防御最佳实践
当Agent成为新员工,你的安全边界在哪里?
1932 8
阿里云联动百位企业安全专家,共识Agent防御最佳实践
|
4天前
|
编解码 人工智能 安全
2核4G/4核8G/8核16G阿里云服务器如何选择实例?经济型e、通用算力型u2i与计算型c9i选哪个?
本文介绍了阿里云2核4G、4核8G、8核16G三档主流配置下经济型e、通用算力型u2i和计算型c9i三种实例的最新活动价格与适用场景。同配置下三者价差显著,以2核4G为例,经济型e低至599.93元/年,计算型c9i则高达1742.08元/年。文章详细解析了各实例的性能定位:经济型e适合轻负载入门场景,u2i兼顾稳定算力与性价比,c9i凭借第9代至强处理器与芯片级安全能力支撑高性能业务。同时提示用户可叠加满减优惠券享受折上折,建议根据业务负载与预算综合决策。
513 112

热门文章

最新文章