eğer değiştirin
static const double Points[] = { 1.0 e- 0 , 1.0 e- 1 , 1.0 e- 2 , 1.0 e- 3 , 1.0 e- 4 , 1.0 e- 5 , 1.0 e- 6 , 1.0 e- 7 , 1.0 e- 8 };
anahtar varyantında, anahtar uygulamasının kalitesini sayılarla görebileceksiniz.
NormalizeDouble ile betiğin temizlenmiş bir sürümünü düşünün:
#define EPSILON ( 1.0 e- 7 + 1.0 e- 13 ) #define HALF_PLUS ( 0.5 + EPSILON) //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double MyNormalizeDouble( const double Value, const int digits) { static const double Points[]={ 1.0 e- 0 , 1.0 e- 1 , 1.0 e- 2 , 1.0 e- 3 , 1.0 e- 4 , 1.0 e- 5 , 1.0 e- 6 , 1.0 e- 7 , 1.0 e- 8 }; return (( int )((Value > 0 ) ? Value / Points[digits] + HALF_PLUS : Value / Points[digits] - HALF_PLUS) * Points[digits]); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ ulong BenchStandard( const int Amount= 1.0 e8) { double Price= 1.23456 ; const double point= 0.00001 ; const ulong StartTime= GetMicrosecondCount (); //--- for ( int i= 0 ; i<Amount;i++) { Price= NormalizeDouble (Price+point, 5 ); } Print ( "Result: " ,Price); // специально выводим результат, чтобы цикл не оптимизировался в ноль //--- return ( GetMicrosecondCount () - StartTime); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ ulong BenchCustom( const int Amount= 1.0 e8) { double Price= 1.23456 ; const double point= 0.00001 ; const ulong StartTime= GetMicrosecondCount (); //--- for ( int i= 0 ; i<Amount;i++) { Price=MyNormalizeDouble(Price+point, 5 ); } Print ( "Result: " ,Price); // специально выводим результат, чтобы цикл не оптимизировался в ноль //--- return ( GetMicrosecondCount () - StartTime); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnStart ( void ) { Print ( "Standard: " ,BenchStandard(), " msc" ); Print ( "Custom: " ,BenchCustom(), " msc" ); }
Sonuçlar:
Custom: 1110255 msc Result: 1001.23456 Standard: 1684165 msc Result: 1001.23456
Hemen yorumlar ve açıklamalar:
- static burada gereklidir, böylece derleyici bu diziyi işlevden çıkarır ve işlev her çağrıldığında yığın üzerinde oluşturmaz . C++ derleyicisi de aynı şeyi yapar.
static const double Points
- Derleyicinin işe yaramaz olduğu için döngüyü dışarı atmaması için hesaplamaların sonuçlarını kullanmanız gerekir. Örneğin, Print değişkenini Fiyat yapın.
- İşlevinizde bir hata var - rakamların sınırları kontrol edilmiyor, bu da kolayca bir dizinin sınırların dışına çıkmasına neden olabilir.
Örneğin, MyNormalizeDouble(Price+point, 10 ) gibi bir çağrı yapın ve hatayı yakalayın:array out of range in 'BenchNormalizeDouble.mq5' (19,45)
Kontrolleri atlayarak hızlandırma yöntemi kabul edilebilir, ancak bizim durumumuzda değil. Herhangi bir hatalı veri girişini işlemekle yükümlüyüz. - 8'den büyük bir dizin için basit bir koşul ekleyelim ve kodu basitleştirmek için <0 ek koşulu yerine >8 için bir karşılaştırma yapmak için digit değişkeninin türünü uint ile değiştireceğiz.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double MyNormalizeDouble( const double Value, uint digits) { static const double Points[]={ 1.0 e- 0 , 1.0 e- 1 , 1.0 e- 2 , 1.0 e- 3 , 1.0 e- 4 , 1.0 e- 5 , 1.0 e- 6 , 1.0 e- 7 , 1.0 e- 8 }; //--- if (digits> 8 ) digits= 8 ; //--- return (( int )((Value > 0 ) ? Value / Points[digits] + HALF_PLUS : Value / Points[digits] - HALF_PLUS) * Points[digits]); }
- Kodu çalıştırıyoruz ve ... şaşırıyoruz!
Custom: 1099705 msc Result: 1001.23456 Standard: 1695662 msc Result: 1001.23456
Kodunuz, standart NormalizeDouble işlevinden daha da önde!
Ayrıca, koşulun eklenmesi süreyi bile azalttı (aslında buradaki azalma hata payı içinde). Neden bu kadar hız farkı var? - Her şey performans testçilerinin standart hatasıyla ilgili.
Testler yazarken, derleyici tarafından uygulanabilecek optimizasyonların tam listesini aklınızda bulundurmanız gerekir. Basitleştirilmiş bir örnek test yazarken hangi girdileri kullandığınız ve bunların nasıl yok edileceği konusunda çok net olmanız gerekir.
Derleyicimizin yaptığı tüm optimizasyonları adım adım değerlendirip uygulayalım. - Sürekli yayılma ile başlayalım - bu, bu testte yaptığınız önemli hatalardan biridir.
Girilen verilerin yarısı sabittir. Dağılımlarını dikkate alarak örneği yeniden yazalım.ulong BenchStandard( void ) { double Price= 1.23456 ; const ulong StartTime= GetMicrosecondCount (); //--- for ( int i= 0 ; i< 1.0 e8;i++) { Price= NormalizeDouble (Price + 0.00001 , 5 ); } Print ( "Result: " ,Price); //--- return ( GetMicrosecondCount () - StartTime); } ulong BenchCustom( void ) { double Price= 1.23456 ; const ulong StartTime= GetMicrosecondCount (); //--- for ( int i= 0 ; i< 1.0 e8;i++) { Price=MyNormalizeDouble(Price + 0.00001 , 5 ); } Print ( "Result: " ,Price, " " , 1.0 e8); //--- return ( GetMicrosecondCount () - StartTime); }
Lansmandan sonra hiçbir şey değişmedi - olması gerektiği gibi. - Devam edin - kodunuzu satır içine alın (NormalizeDouble'ımız satır içine alınamaz)
Kaçınılmaz satır içi işleminden sonra işlevinizin gerçekte dönüştüğü şey budur. Çağrılardaki tasarruflar, dizi çıkarımlarındaki tasarruflar, sabitler ayrıştırılarak kontroller kaldırılır:ulong BenchCustom( void ) { double Price= 1.23456 ; const ulong StartTime= GetMicrosecondCount (); //--- for ( int i= 0 ; i< 1.0 e8;i++) { //--- этот код полностью вырезается, так как у нас заведомо константа 5 //if(digits>8) // digits=8; //--- распространяем переменные и активно заменяем константы if ((Price+ 0.00001 )> 0 ) Price= int ((Price+ 0.00001 )/ 1.0 e- 5 +( 0.5 + 1.0 e- 7 + 1.0 e- 13 ))* 1.0 e- 5 ; else Price= int ((Price+ 0.00001 )/ 1.0 e- 5 -( 0.5 + 1.0 e- 7 + 1.0 e- 13 ))* 1.0 e- 5 ; } Print ( "Result: " ,Price); //--- return ( GetMicrosecondCount () - StartTime); }
Zaman kaybetmemek için saf sabitleri toplamadım. hepsinin derleme zamanında katlanması garanti edilir.
kodu çalıştırın ve orijinal sürümdekiyle aynı zamanı alın:Custom: 1149536 msc Standard: 1767592 msc
sayıların titremesine dikkat etmeyin - mikrosaniye düzeyinde, zamanlayıcı hatası ve bilgisayardaki kayan yük normal aralıktadır. oran tamamen korunur. - Sabit girdiler nedeniyle test etmeye başladığınız koda bakın.
Derleyici çok güçlü optimizasyonlara sahip olduğundan, göreviniz etkili bir şekilde basitleştirildi. - Peki performansı nasıl test edersiniz?
Derleyicinin nasıl çalıştığını anlayarak, ön optimizasyon ve basitleştirmeler uygulamasını engellemeniz gerekir.
Örneğin, digits parametresini değişken yapalım:#define EPSILON ( 1.0 e- 7 + 1.0 e- 13 ) #define HALF_PLUS ( 0.5 + EPSILON) //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double MyNormalizeDouble( const double Value, uint digits) { static const double Points[]={ 1.0 e- 0 , 1.0 e- 1 , 1.0 e- 2 , 1.0 e- 3 , 1.0 e- 4 , 1.0 e- 5 , 1.0 e- 6 , 1.0 e- 7 , 1.0 e- 8 }; //--- if (digits> 8 ) digits= 8 ; //--- return (( int )((Value > 0 ) ? Value / Points[digits] + HALF_PLUS : Value / Points[digits] - HALF_PLUS) * Points[digits]); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ ulong BenchStandard( const int Amount= 1.0 e8) { double Price= 1.23456 ; const double point= 0.00001 ; const ulong StartTime= GetMicrosecondCount (); //--- for ( int i= 0 ; i<Amount;i++) { Price= NormalizeDouble (Price+point, 2 +(i& 15 ) ); } Print ( "Result: " ,Price); // специально выводим результат, чтобы цикл не оптимизировался в ноль //--- return ( GetMicrosecondCount () - StartTime); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ ulong BenchCustom( const int Amount= 1.0 e8) { double Price= 1.23456 ; const double point= 0.00001 ; const ulong StartTime= GetMicrosecondCount (); //--- for ( int i= 0 ; i<Amount;i++) { Price=MyNormalizeDouble(Price+point, 2 +(i& 15 ) ); } Print ( "Result: " ,Price); // специально выводим результат, чтобы цикл не оптимизировался в ноль //--- return ( GetMicrosecondCount () - StartTime); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnStart ( void ) { Print ( "Standard: " ,BenchStandard(), " msc" ); Print ( "Custom: " ,BenchCustom(), " msc" ); }
Çalıştırırız ve... öncekiyle aynı hız sonucunu alırız.
Kodunuz daha önce olduğu gibi yaklaşık %35 kazanır. - Neden öyle?
Satır içi oluşturma nedeniyle kendimizi hala optimizasyondan kurtaramadık. 100.000.000 çağrıyı yığından veri geçirerek aynı uygulamanın NormalizeDouble işlevimize kaydederek kolayca böyle bir ivme verebilir.
Bir MQL5 programında fonksiyon yer değiştirme tablosunu yüklerken NormalizeDouble'ımızı direct_call mekanizmasına koymadığımıza dair bir şüphemiz daha var.
Sabah kontrol edeceğiz ve eğer durum buysa, onu direct_call'a aktaracağız ve hızı tekrar kontrol edeceğiz.
İşte NormalizeDouble'ın bir çalışması.
MQL5 derleyicimiz, C++ kodunun hızıyla karşılaştırıldığında yeterliliğini gösteren sistem işlevimizi yendi.
eğer değiştirin
anahtar varyantında, anahtar uygulamasının kalitesini sayılarla görebileceksiniz.
Statik bir diziye doğrudan indekslenmiş erişimi sabit bir indeks (bir alandan bir sabite dönüşen) ve anahtar ile karıştırıyorsunuz.
anahtarı bu durumda çok rekabetçi değil. switch'in yaygın olarak kullanılan birkaç form optimizasyonu vardır:
- "Açıkça sıralı ve kısa değerler statik bir diziye ve indeks geçişine konur" - en basit ve en hızlı, statik bir diziyle rekabet edebilir, ancak her zaman değil.
- "bölge sınırlarını kontrol eden sıralı ve yakın değer parçalarında birkaç dizi" - burada zaten frenlerle
- "çok az kontrol edilen değer aptalca kontrol edilirse" - hız yok, ancak programcı suçlanacak, yerinde olmayan anahtarı kullanıyor
- "ikili arama ile tamamen seyrek sıralı tablo" - korkunç durumlar için çok yavaş
Aslında, bir anahtar için en iyi strateji, geliştiricinin kasıtlı olarak daha düşük sayılar kümesindeki değerler kümesini kapsamda kompakt hale getirmeye çalışmasıdır.
NormalizeDouble ile betiğin temizlenmiş bir sürümünü düşünün:
Sonuçlar:
Hemen yorumlar ve açıklamalar:
- static burada gereklidir, böylece derleyici bu diziyi işlevden çıkarır ve işlev her çağrıldığında yığın üzerinde oluşturmaz . C++ derleyicisi de aynı şeyi yapar.
- Derleyicinin işe yaramaz olduğu için döngüyü dışarı atmaması için hesaplamaların sonuçlarını kullanmanız gerekir. Örneğin, Print değişkenini Fiyat yapın.
- İşlevinizde bir hata var - rakamların sınırları kontrol edilmiyor, bu da kolayca bir dizinin sınırların dışına çıkmasına neden olabilir.
Örneğin, MyNormalizeDouble(Price+point, 10 ) gibi bir çağrı yapın ve hatayı yakalayın:
Kontrolleri atlayarak hızlandırma yöntemi kabul edilebilir, ancak bizim durumumuzda değil. Herhangi bir hatalı veri girişini işlemekle yükümlüyüz. - 8'den büyük bir dizin için basit bir koşul ekleyelim ve kodu basitleştirmek için <0 ek koşulu yerine >8 için bir karşılaştırma yapmak için digit değişkeninin türünü uint ile değiştireceğiz.
double MyNormalizeDouble( const double Value, const uint digits ) { static const double Points[] = { 1.0 e- 0 , 1.0 e- 1 , 1.0 e- 2 , 1.0 e- 3 , 1.0 e- 4 , 1.0 e- 5 , 1.0 e- 6 , 1.0 e- 7 , 1.0 e- 8 }; const double point = digits > 8 ? 1.0 e- 8 : Points[digits]; return (( int )((Value > 0 ) ? Value / point + HALF_PLUS : Value / point - HALF_PLUS) * point); }
- Her şey performans testçilerinin standart hatasıyla ilgili.
Testler yazarken, derleyici tarafından uygulanabilecek optimizasyonların tam listesini aklınızda bulundurmanız gerekir. Basitleştirilmiş bir örnek test yazarken hangi girdileri kullandığınız ve bunların nasıl yok edileceği konusunda çok net olmanız gerekir. - Peki performansı nasıl test etmelisiniz?
Derleyicinin nasıl çalıştığını anlayarak, ön optimizasyon ve basitleştirmeler uygulamasını engellemeniz gerekir.
Örneğin, digits parametresini değişken yapalım:
İşte NormalizeDouble'ın bir çalışması.
MQL5 derleyicimiz, C++ kodunun hızıyla karşılaştırıldığında yeterliliğini gösteren sistem işlevimizi yendi.
Statik bir diziye doğrudan indekslenmiş erişimi sabit bir indeks (bir alandan bir sabite dönüşen) ve anahtar ile karıştırıyorsunuz.
anahtarı bu durumda çok rekabetçi değil. switch'in yaygın olarak kullanılan birkaç form optimizasyonu vardır:
- "Açıkça sıralı ve kısa değerler statik bir diziye ve indeks geçişine konur" - en basit ve en hızlı, statik bir diziyle rekabet edebilir, ancak her zaman değil.
İşte tam da böyle bir düzen durumu.
Aslında, bir anahtar için en iyi strateji, geliştiricinin kasıtlı olarak daha düşük sayılar kümesindeki değerler kümesini kapsamda kompakt hale getirmeye çalışmasıdır.
İşte tam da böyle bir düzen durumu.
32 bit sistemde denendi. Orada, yukarıdaki örnekte anahtarla değiştirme ciddi frenlere yol açtı. Yeni bir arabada test etmedim.
Her MQL5 aslında iki derlenmiş programa sahiptir: 32 bit için basitleştirilmiş program ve 64 bit için maksimum optimize edilmiş program. 32 bit MT5'te yeni optimize edici hiç uygulanmaz ve 32 bit işletim sistemleri için kod, MT4'teki MQL4 kadar basittir.
Yalnızca MT5'in 64 bit sürümünde çalıştırıldığında on kat daha hızlı kod üretebilen bir derleyicinin tüm verimliliği: https://www.mql5.com/en/forum/58241
Tamamen platformun 64 bit sürümlerine odaklandık.
- incelemeler: 8
- www.mql5.com
NormalizeDouble konusunda böyle bir saçmalık var
Ticaret, otomatik ticaret sistemleri ve ticaret stratejilerinin test edilmesi hakkında forum
Sıralı olarak bir enum üzerinde yineleme nasıl yapılır?
fxsaber , 2016.08.26 16:08
İşlev açıklamasında bir not var
Bu yalnızca minimum fiyat adımı 10^N olan ve N'nin bir tam sayı olduğu ve pozitif olmadığı semboller için geçerlidir. Minimum fiyat adımı farklı bir değere sahipse, o zaman OrderSend'den önce fiyat seviyelerini normalleştirmek anlamsız bir işlemdir ve çoğu durumda OrderSend'in false döndürmesine neden olur.
NormalizeDouble tamamen gözden düştü. Sadece yavaş bir uygulama olmakla kalmaz, aynı zamanda çeşitli stok sembollerinde (örneğin, RTS, MIX, vb.) anlamsızdır .
double CTrade::CheckVolume( const string symbol, double volume, double price, ENUM_ORDER_TYPE order_type) { //--- check if (order_type!= ORDER_TYPE_BUY && order_type!= ORDER_TYPE_SELL ) return ( 0.0 ); double free_margin= AccountInfoDouble ( ACCOUNT_FREEMARGIN ); if (free_margin<= 0.0 ) return ( 0.0 ); //--- clean ClearStructures(); //--- setting request m_request.action= TRADE_ACTION_DEAL ; m_request.symbol=symbol; m_request.volume=volume; m_request.type =order_type; m_request.price =price; //--- action and return the result if (!:: OrderCheck (m_request,m_check_result) && m_check_result.margin_free< 0.0 ) { double coeff=free_margin/(free_margin-m_check_result.margin_free); double lots= NormalizeDouble (volume*coeff, 2 ); if (lots<volume) { //--- normalize and check limits double stepvol= SymbolInfoDouble (symbol, SYMBOL_VOLUME_STEP ); if (stepvol> 0.0 ) volume=stepvol*( MathFloor (lots/stepvol)- 1 ); //--- double minvol= SymbolInfoDouble (symbol, SYMBOL_VOLUME_MIN ); if (volume<minvol) volume= 0.0 ; } } return (volume); }
Pekala, bunu yapamazsın! NormalizeDouble'ı unutarak birçok kez daha hızlı olabilir
double NormalizePrice( const double dPrice, double dPoint = 0 ) { if (dPoint == 0 ) dPoint = :: SymbolInfoDouble (:: Symbol (), SYMBOL_TRADE_TICK_SIZE ); return (( int )((dPrice > 0 ) ? dPrice / dPoint + HALF_PLUS : dPrice / dPoint - HALF_PLUS) * dPoint); }
Ve aynı hacim için o zaman
volume = NormalizePrice(volume, stepvol);
Fiyatlar için
NormalizePrice(Price, TickSize)
Standart NormalizeDouble'ın aşırı yüklenmesi olarak böyle bir şey eklemek doğru görünüyor. İkinci parametre olan "rakamlar" int değil, iki katı olacaktır.
2016 itibariyle, çoğu C++ derleyicisi aynı optimizasyon seviyelerine ulaşmıştı.
MSVC, her güncellemedeki iyileştirmeleri merak etmenizi sağlar ve bir derleyici olarak Intel C++ birleştirilmiştir - ve büyük projelerdeki "iç hatalarından" kurtarılmamıştır.
Derleyicideki 1400 derlemesindeki iyileştirmelerimizden bir diğeri, karmaşık projeleri derlemenin daha hızlı hale gelmesidir.
Bu konuda. Standart işlevlere alternatifler yaratmalısınız, çünkü bazen ihtiyacınız olanı vermezler. İşte SymbolInfoTick'e örnek bir alternatif
// Получение тика, который на самом деле вызвал крайнее событие NewTick bool MySymbolInfoTick( const string Symb, MqlTick &Tick, const uint Type = COPY_TICKS_ALL ) { MqlTick Ticks[]; const int Amount = :: CopyTicks (Symb, Ticks, Type, 0 , 1 ); const bool Res = (Amount > 0 ); if (Res) Tick = Ticks[Amount - 1 ]; return (Res); } // Возвращает в точности то, что SymbolInfoTick bool CloneSymbolInfoTick( const string Symb, MqlTick &Tick ) { MqlTick TickAll, TickTrade, TickInfo; const bool Res = (MySymbolInfoTick(Symb, TickAll) && MySymbolInfoTick(Symb, TickTrade, COPY_TICKS_TRADE ) && MySymbolInfoTick(Symb, TickInfo, COPY_TICKS_INFO )); if (Res) { Tick = TickInfo; Tick.time = TickAll.time; Tick.time_msc = TickAll.time_msc; Tick.flags = TickAll.flags; Tick.last = TickTrade.last; Tick.volume = TickTrade.volume; } return (Res); }
Test cihazında, her olayda NewTick SymbolInfoTick'i arayın ve değişim cirosunu bulmak için hacim alanını ekleyin. Ama hayır, yapamazsın! MySymbolInfoDouble anlamında çok daha mantıklı yapmanız gerekiyor.
NormalizeDouble konusunda böyle bir saçmalık var
Pekala, bunu yapamazsın! NormalizeDouble'ı unutarak birçok kez daha hızlı olabilir
Ve aynı hacim için o zaman
Fiyatlar için
Standart NormalizeDouble'ın aşırı yüklenmesi olarak böyle bir şey eklemek doğru görünüyor. İkinci parametre "rakamlar" int değil, iki katı olacaktır.
Etrafınızdaki her şeyi optimize edebilirsiniz.
Bu sonsuz bir süreç. Ancak vakaların %99'unda ekonomik olarak uygun değildir.
- Ücretsiz alım-satım uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz
NormalizeÇift
Sonuç, MyNormalizeDouble lehine 1123275 ve 1666643'tür (Optimize=1). Optimizasyon olmadan - dört kat daha hızlı (bellek için).