Soci (Soczó Zsolt) szakmai blogja

2017.12.27.

Python pandas lassú io.parsers.read_csv metódus

Filed under: Machine Learning,Python,Szakmai élet — Soczó Zsolt @ 11:44

Elkezdtem pythonozni, mivel machine learninget tanulok, és ahhoz vagy python vagy R javasolt. Legjobb mindkettőhöz érteni, most a pyhon van soron.

Akit érdekel bátran vágjon bele, a nyelv nem nagy szám (nekem ronda ez a kettőspontos mindenség, de majd megszokom), a tanulást a libek megismerése viszi el (kb. mint a legtöbb nyelvnél).

Mivel van sok tőzsdei adatom ezeken futtatom az ML libeket. A legtöbb példában napos adatokat használnak, de én intraday akarok keresgélni, ami sokkal több adatot jelent. Valószínűleg bajban leszek, pl. a Support Vector Machine o(n^4)-es alg, quadratikus, így nem tolhatok rá túl sok adatot.
De már az elején elakadtam, mert 1-2M CSV betöltése is 10mp volt.

Mindenféléket írtak a stackoverflown, csak a megoldást nem.

sym1 = pd.io.parsers.read_csv(os.path.join(datadir, '%s.txt' % symbol),
header=None, index_col=0, nrows = rows, parse_dates=[['Date', 'Time']],
infer_datetime_format=True,
names=['Date', 'Time','Open','High','Low','Close', 'Up', 'Down'], usecols=['Date', 'Time','Close'])

Az infer_datetime_format=True hozott megoldást, valamiért a dátum parsolása csapnivaló, ezen flag nélkül. A read_csv doksija írta, onnan jött az ötlet:
“infer_datetime_format : boolean, default False

If True and parse_dates is enabled, pandas will attempt to infer the format of the datetime strings in the columns, and if it can be inferred, switch to a faster method of parsing them. In some cases this can increase the parsing speed by 5-10x.”

IIS lassulás probléma – megoldás

Filed under: IIS,Optimalizálás,Szakmai élet — Soczó Zsolt @ 11:21

Annak idején írtam róla, hogy egy cégnél a web szerverek és a mögöttük levő webszervizek közötti kommunikációban jelen van egy 200ms-os plusz késleltetés, amire nem találtunk racionális magyarázatot.
Semmilyen eddig bevált eszköz, profilerek, WinDebug, Dynatrace, logok elemézése nem adott magyarázatot a jelenségre.
A Google a Nagle és a Delayed Ack irányába tolta a vizsgálódást.
Végül wiresharkkal alaposan kielemezve a forgalmat kiderült, hogy a protokoll leírással szemben csak 200ms késleltetéssel küldtek Acknowledge-et egymásnak a felek, ez adott késleltetést minden híváshoz. Miután a delayed ackot kikapcsoltuk mindkét oldalon, a jelenség megoldódott. A megoldás szerintem csak workaround, mivel a tcp protokol rfcje alapján nem így kellene működni a hálózatnak, vagy a Windows vagy a VMWare network card driver a bugos.

Röviden, mi zajlik itt le. A kliens (web app) TCP kommunikációt kezdeményez a webszerviz felé. A TCP protokollban minden csomag vételét meg kell erősíteni a másik oldalnak. Esetünkben olyan kicsi kérések mentek a szerviz felé, hogy belefértek egy hálózati csomagba. A Nagle algoritmus már eleve bufferelhetné a hívó oldalon a csomag kiküldését, de ezt úgy néz ki a generált webszerviz proxy kikapcsolja a Naglét, így ez nem okozott problémát.
Átmegy a kérés a szervizbe. Onnan egy acknowledgenek kell visszamenni a kliensre, jelezve, hogy a szerviz megkapta a csomagot. Ezt nem akarja visszaküldeni a szerviz azon mód, pár bájt miatt nem akar egy teljes csomagot visszaküldeni. A protokol alapján vár arra, amíg amúgy is küldene valamit vissza a kliensnek, és annak a hátára rakná rá az acknowledget (piggybacking).
Esetünkben volt sok kérés, lett volna alkalma visszaküldeni gyorsan a megerősítést, de nem tette. A protokollban van egy másik elem is. Ha eltelik 200ms, akkor ha eddig nem volt alkalmunk visszaküldeni a választ, legalább 200ms múlva meg kell tenni. Ez a delayed ack. Meg is tette a szerver, de ezzel minden egyes kérésre az ackot 200ms múlva küldte csak vissza. A kikapcsolással pazarló módon minden egyes csomagot azonnal visszajelez a szerver, azaz kikapcsoltuk ezt az optimalizálást, de cserébe a bugos késleltetés kiesett.
Összegezve, a válasznak vissza kellett volna menni más kérésekre adott válaszok hátán, de nem mentek vissza, ezért állítom, hogy ez bug, a delayed ack kikapcsolás csak workaround, de legalább megoldotta a késleltetést.
Két kép, ami szemlélteti a hibát.
Az első egy csatornán a kommunikáció lépéseit mutatja meg (a http keepalive miatt sok kérés meg át egy tcp csatornán). Látható, hogy nagyon sok kérésre csak 200ms múlva jön meg a válasz.

A második képen sok kérés ackjának maximuma látható. Jól megfigyelhető a “fék” hatása.

Sajnos nincs kéznél képem, de a delayed ack kikapcsolása után lemegy pár száz mikro!secre a válasz, mivel ilyenkor buzgó módon azonnal megy vissza az ack.

2017.09.22.

Debugolás Visual Studio 2017-ben

Filed under: .NET,C#,Debugging,Szakmai élet,Visual Studio — Soczó Zsolt @ 16:02

.NET Core és VS 2017 alatt van egy újfajta debug symbol kezelési módszer, a source link.
Itt egy egyszerű leírás, hogyan kell használni.

Tök szépen lehet a core kódokat így debugolni, csak szokás szerint a lokális változók nem látszanak.

2017.09.04.

IIS lassulás probléma – help needed

Filed under: Szakmai élet — Soczó Zsolt @ 20:44

Kivételesen nem megoldást írok le, hanem kérdést teszek fel.

Egyik ügyfelemnél vagy egy eset, amit egyelőre nem sikerült visszafejteni. Adott 3 IIS publikus web láb NLBS-sel összenyalábolva, és 3 belső IIS alatt futó WS szintén NLBS mögött, ezeket hívják a külső webappok. A Windowsok VmWare alatt vannak virtualizálva.

A hiba az, hogy random időpontban belassulnak a webszerverek webszervizek irányába mutató hívásai. Normál esetben egy gyors ws metódus hívása 2-5ms, amikor beáll ez az állapotváltás a webszerver worker process belsejében, akkor felmegy kb. 200 ms-re.

Azért írok állapotváltást, mert nem azért lassulnak be a dolgok, mert nap közben nagyobb a terhelés, hanem egyszer csak “elborul” a webapp, és belassul. Dynatrace alapján a ws hívások mélyén a recv windows hívás válaszol lassan. Ilyenkor a ws iis logjában is lassú a hívás, vélhetően mert a webapp mint kliens lassan viszi el az adatot.
Hamarosan lesz DotTrace lenyomat és FREB log is, illetve System.NET trace is (csak ez nagyon sok adatot termel).

IIS App reset megoldja a problémát egy ideig. Ha egy app beteg, akkor egy console appból ugyanaz a ws hívás ugyanezen a gépről a wsek felé gyors, tehát nem valószínű, hogy a wsek lassulnak be.

Nehézkes megfogni az estet, mert pl. egy config módosítás a trace-ek kedvéért azonnal appol resetet csinál, így elillan a hiba, aztán lehet megint egy napot várni rá.

A .NET perf counterek nem mutatnak kiugró értékeket, minden normálisnak tűnik. A web processben kb. 500 szál fut 1-3 kérés/sec esetén, ez mondjuk kicsit soknak tűnik, de a procmon nem mutatta meg a managed stacket (debug symbolokkal se), majd csak dottrace-ből látszik, mit csinálnak. A procik 10%-ig vannak kiterhelve. A diszk terhelés minimális, paging nincs, van 0.5G szabad memória, más proceszek nem eszik el az erőforrásokat. A resource monitorban a wsek irányába futó kérések jelentős része 200ms körüli latency-t mutat, ez egybevág más megfigyelésekkel.

Látott már ilyet valaki? Mit lehetne még mérni, amire nem gondoltam?

2017.09.02.

.NET optimalizálási praktikák

Filed under: .NET,Optimalizálás,Szakmai élet — Soczó Zsolt @ 10:25

Akik olyanok mint én, hogy az utolsó órajelet is meg akarják sprórolni, azoknak érdekes .NET optimalizálási cikk.

(Nitpickerek kedvéért: nyilván akinek minden ns számít, az nem .NET-ben programoz.)

2017.08.18.

Protobuf.net

Filed under: .NET,Szakmai élet — Soczó Zsolt @ 13:24

Eszement gyorsan serializál!
https://code.google.com/archive/p/protobuf-net/wikis/GettingStarted.wiki

2017.08.17.

Forceseek delete-hez

Filed under: Adatbázisok,SQL Server,Szakmai élet — Soczó Zsolt @ 18:02

Volt egy delete-em, ami nem akart rendesen index mentén lefutni. A delete-et kicserélve select-re ugyanez volt a helyzet, de select esetén egy oszlopokkal kikényszerített forceseek segített.
Viszont delete-et nem lehet hintelni. A megoldás CTE volt, így indirekten mégiscsak lehet hintelni. A lekérdezés pár százszor gyorsabb lett. :)

Érdemes megjegyezni három trükköt tanulságul:
1. CTE kimenetén lehet futtatni DDL-eket
2. Így indirekten lehet hintelni
3. Néha jó index esetén se seekel a szerver, ilyenkor csakis az oszlopokkal megsegített forceseek segít.

;with B
as
(
select * from dbo.Bar with(forceseek(IX_Natural_Key(TickerId, BDT)))
where TickerId = @tickerId and BDT in 
    (select DATEADD(day, DATEDIFF(day,'19000101',DATEADD(DAY, 1, cast(StartDate as datetime))), CAST(ClosingTime AS DATETIME2(7)))
    from TradingHours where TickerId = @timeTemplateTickerId and StartDate is not null and EndDate is null and IsEnabled = 1 and Priority > 10)
)
delete from B;

2017.05.31.

Kell nekem a Microservices architektúra?

Filed under: Szakmai élet — Soczó Zsolt @ 08:22

https://aadrake.com/posts/2017-05-20-enough-with-the-microservices.html

2017.05.12.

Google Mock Test Adapter for VS 2017

Filed under: Szakmai élet — Soczó Zsolt @ 07:45

Az MS mögé állt, eddig külsősök írták. Végre kényelmesedik a C++ tesztelési irány is. Hát még, ha NCrunch is menne…

2017.01.17.

Nagy táblák join-olása eredmények

Filed under: Adatbázisok,SQL Server,SQL Server 2016,Szakmai élet — Soczó Zsolt @ 19:26

Korábbi bejegyzésemben írtam, hogy demó környezetben a columnstore indexek nagyon jelentős gyorsulást okoznak.

Élő adatbázisban azt tapasztaltuk, hogy integer kulcsokon végzett join-okon 5-10-szeres gyorsulást okozott a normál indexekhez képest.

Azonban GUID-os oszlopokkal nem okozott értékelhető gyorsulást, annak ellenére, hogy batch módúak voltak a műveletek. Erre úgy látszik nem gyúrtak még rá, vagy a guid randomsága miatt nem tud érdelmes teljesítményt elérni.

2016.11.22.

Nagy táblák joinolása

Egyik folyó munkámban több tízmilló soros táblákon végzett joinokat kellett optimalizálni. Általában ez nem kihívás, mert szinte mindig vannak szűrési feltételek, amelyeket kellő közelségbe víve a táblákhoz és rendes indexekat alápakolva már csak pár ezer joint kell végrehajtani, ami gyors lesz.
De most tényleg össze kellett joinolni sok millió sort, szűrés nélkül.
Mit lehet ezzel kezdeni? Sajnos itt már eléggé behatárolt területen mozgunk. A normál indexelős megoldások nem segítenek, mivel minden táblát teljes egészében be kell járni (nincs where).
Ráadásul ha *-os a select, akkor a cover NC index se játszik, hogy legalább az IO csökkenne.
Merge joinra lehet játszani clu indexekkel, de azért ez korlátos terület sok tábla esetén, illetve párhuzamos tervek esetén magától nem fog merge joint használni (itt írnak egy trace flagről, amivel mégis rá lehet venni).
Mit lehet tenni. Egyik lehetőség előre elkészíteni a join indexelt view-ban. Erre ügyesen ráharap az optimizer, ha van olyan join amit aztán többször futtatunk, akkor megéri ez a denormalizálás.
Ha viszont van újabb szerverünk (2016), akkor van sokkal durvább lehetőség: Columnstore index.
Az a baj ugye a nagy joinnal, hogy akárhogy is trükközünk, ez nagy meló a prociknak és az IO alrendszernek (vinkóknak). Az indexed view ezt úgy oldja meg, hogy egyszer kell megcsinálni, aztán sokszor élvezni az előre összepakolt adatokat.
A columnstore viszont (dióhéjban) azért piszok gyors mert:
1. 5-10-szeresen tömörítve tárolja az adatokat, kevesebb IO, illetve a memóriában a buffer cache-t is jobban ki tudja használni (mintha több RAM-unk lenne)
2. Képes az adatok csak egy részét felolvasni, ha csak kevés oszlop kell (select *-on ez nem segít persze)
3. Képes batch módban belülről párhuzamosan végrehajtani a műveletek egy részét (ez nagyon durván megdobja)
4. Képes a sorok egy részét felolvasni where feltétel alapján, mivel minden 1m sorhoz (szegmens) nyilván tarja az adott oszlop min és max értékét
5. Le tud nyomni operátorokat (pl. sum) a storage engine-be, így nem kell adatokat passzolgatni a rétegek között.

No, lássuk a medvét. Létrehoztam két másolatot egy 100 millió soros táblából. A tesztgép egy két éves laptop 2 core-ral és 8G RAM-mal, SSD-vel. Nem egy szerver.
A két táblát a kulcsai mentés join-olom, így mind a 100 millió sort végig kell néznie, és ennyi találat is lesz.

Először sima Clu index:
create clustered index IX_Clu1 on B1(Id)
create clustered index IX_Clu2 on B2(Id)

select count(*) from B1 join B2 on B1.Id = B2.Id

SQL Server parse and compile time:
CPU time = 15 ms, elapsed time = 18 ms.

(1 row(s) affected)
Table ‘B1’. Scan count 5, logical reads 1141262, physical reads 6,
read-ahead reads 1138814, lob logical reads 0, lob physical reads 0,
lob read-ahead reads 0.
Table ‘B2’. Scan count 5, logical reads 1140956, physical reads 4,
read-ahead reads 1138821, lob logical reads 0, lob physical reads 0,
lob read-ahead reads 0.
Table ‘Workfile’. Scan count 896, logical reads 480256, physical reads
2688, read-ahead reads 477568, lob logical reads 0, lob physical reads
0, lob read-ahead reads 0.
Table ‘Worktable’. Scan count 0, logical reads 0, physical reads 0,
read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob
read-ahead reads 0.

SQL Server Execution Times:
CPU time = 477262 ms, elapsed time = 377318 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 1 ms.

SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.

377 másodperc.

Jöhet a columnstore index:
create clustered columnstore index IX_CStore1 on B1
create clustered columnstore index IX_CStore2 on B2

select count(*) from B1 join B2 on B1.Id = B2.Id

SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 6 ms.

(1 row(s) affected)
Table ‘B2’. Scan count 4, logical reads 0, physical reads 0,
read-ahead reads 0, lob logical reads 105018, lob physical reads 0,
lob read-ahead reads 0.
Table ‘B2’. Segment reads 103, segment skipped 0.
Table ‘B1’. Scan count 4, logical reads 0, physical reads 0,
read-ahead reads 0, lob logical reads 104998, lob physical reads 0,
lob read-ahead reads 0.
Table ‘B1’. Segment reads 102, segment skipped 0.
Table ‘Worktable’. Scan count 0, logical reads 0, physical reads 0,
read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob
read-ahead reads 0.

SQL Server Execution Times:
CPU time = 79920 ms, elapsed time = 27834 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.

SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.

377 sec vs. 28 sec. Azért ez masszív különbség. :)

Érdekességképpen megnéztem NC Columnstore index-szel is, úgy 60 sec jön ki. Ez se rossz.

A jövő héten lehet ki tudjuk próbálni egy nagyobb géppel is, kíváncsi vagyok, ott mit tudunk vele kihozni.

Ha esetleg valakinek vannak már gyakorlati sikerei, érdekelnek a számok.

2016.11.04.

Mit is jelent pontosan, hogy Accent Insensitive?

Filed under: Adatbázisok,SQL Server,Szakmai élet — Soczó Zsolt @ 12:18

Bólyai magyar verseny kapcsán gondolkodtam a magyar nyelv rendezési szabályain.

string[] words = new string[] { "írógép", "írat", "irkál", "irodalom", "író", "írás" };
Array.Sort(words, StringComparer.Create(new System.Globalization.CultureInfo("hu-hu"), false));
foreach (var w in words)
{
    Console.WriteLine(w);
}

Kimenet:

írás
írat
irkál
író
irodalom
írógép

Ezek szerint az alap magyar kultúra szerint a C# (valójában a Windows) accent insensitive módon rendez. A gyerekek is azt mondták, így tanulják az iskolában. Az én fejemben ez nem így volt. Vagy bugos a fejem, vagy mi még nem így tanultuk, nem tudom.

Nézzük SQL Serverben!

CREATE TABLE [dbo].[A](
	[Nev] [nvarchar](50) NOT NULL
) ON [PRIMARY]

select * from A
order by Nev collate hungarian_CS_AI

Azaz Accent Insensitive a rendezés, így joggal azonos a C#-pos kimenettel:

írás
írat
irkál
író
irodalom
írógép

Ami viszont bizarr:

select * from A
order by Nev collate hungarian_CS_AS

Kimenet:

írás
írat
irkál
író
irodalom
írógép

Ugyanaz! Magyar nyelv esetén nem számít az ékezet érzékenység, akkor se vesszük figyelembe az ékezeteket, ha kifejezetten kérjük. Ez nekem furcsa, de nem én írom a magyar nyelvi szabályokat.

Van egy másik collation is, annál meg mindig accent insensitive a rendezés, függetlenül a beállítástól:

select * from A
order by Nev collate Hungarian_Technical_CI_AS
select * from A
order by Nev collate Hungarian_Technical_CI_AI

Mindét esetben ez a kimenet:

irkál
irodalom
írat
írás
író
írógép

Tehát a magyar az egy olyan állatfajta, amiben nem az AI/AS szabályozza az Accent Sensitivity-t, hanem, hogy melyik collationt használjuk.

Ami viszont végképp fura, hogy a Latin1_General_100_CI_AS vs Latin1_General_100_CI_AI sem változtatja meg a sorrendet. Hogy van ez?

A mi aá, stb. variánsaink nem tartoznak az accentek közé?

Itt és itt érdekeseket írnak a témáról.

2016.08.19.

Windows Internals cikkeim

Filed under: Szakmai élet,Windows,Winternals — Soczó Zsolt @ 11:22

Annak idején írtam pár cikket a Windows belső működéséről, utóbbi időben többen kerestétek, mivel eltűntek az ms oldalairól.

Ezért most kiraktam ide őket, hogy bárki elérhesse.

2016.06.19.

SQL Server memória hiány miatti lassú lekérdezés

Érdekes hibába futottam bele mostanában. A lapotopomban csak 8G RAM van, így az azon futó SQL servernek nem sok marad, amikor a Chrome, a Visual Studio 2015 és más memóriazabáló alkalmazások elkezdenek terjeszkedni.
A probléma az volt, hogy egy olyan lekérdezés, amely Sortot tartalmazott a szokásos 2-3 mp helyett fél-egy percig futott. A végrehajtási tervben volt egy figyelmeztetés, hogy a sort operátor kénytelen kipakolni a rendezést a tempdb-be (spill). De ettől még nem kellett volna ilyen lassúnak lennie. Csak 3500 sorról volt szó, ez azért belefért volna memóriába.

A select * from sys.dm_exec_query_memory_grants lekérdezésből kiderült, hogy a lekérdezés sortjának kellett volna 10M memória, de nem tudott annyit kapni, ezért várakozott, és aztán 20 másodperc múlva egyszer csak timeout lett, és kényszeredetten csak nekifogott végrehajtani a sortot a tempdbben.

Azaz egy egy nem szokványos lassú lekérdezés volt, nem sok lapolvasással járt, mint a tipikus rosszul optimalizált lekérdezések, hanem a memory grantre várt.
A megoldás az lett, hogy beállítottam 1G min memory-t az SQL Servernek, így már kiszámíthatóan jól érzi magát.

Egy rendes szerveren valószínűleg ritkább az ilyen memória kényszer, de azért jó tudni róla.

2016.02.28.

Ha a Visual Studio (2015) üresjáratban megeszik 1-2 procit…

Filed under: Szakmai élet,Visual Studio — Soczó Zsolt @ 20:11

akkor kapcsold ki a Git Source Control típust, mint default providert. Piszok idegesítő volt ez a hiba.

2015.12.22.

Egy példa mitől fordul le állandóan egy .NET project

Filed under: .NET,Szakmai élet — Soczó Zsolt @ 19:02

Bosszantó, amikor semmit nem változtatsz, mégis újrafordul pár project egy nagyobb solutionben. Ha bekapcsolod a diagnostic build loglevelt, akkor egyből kiderül az ok. Pl. most nálam:

Project ‘Basics’ is not up to date. Project item ‘C:\ATS\Basics\x64\lz4X64.dll’ has ‘Copy to Output Directory’ attribute set to ‘Copy always’.

Update: egyéb okok: felesleges referenciák illetve Resource-ként megjelölt file, ami nem is resource, csak sima xml, ki kell másolni a kimenetbe.

.NET fejtörő

Filed under: .NET,Szakmai élet — Soczó Zsolt @ 15:29

Élő kódból. Az első megoldás jól működik, a második nem, mi lehet az oka? A hibajelenség, hogy az első beállítja a TickerId-t az objektumon belül, a második nem.

Parallel.For(0, tickOhlcs.Count, i => tickOhlcs[i].SetTickerId(key.TickerId));

Parallel.ForEach(tickOhlcs, tickOhlc => tickOhlc.SetTickerId(key.TickerId));

2015.11.16.

TDD következő alkalom early bird

Filed under: .NET,Felhívás,Szakmai élet,Test Driven Development,Testing — Soczó Zsolt @ 23:19

Jövő januárban újra lesz TDD tanfolyam, most ősszel már nem volt időm betervezni egyet.

Aki december 1-ig jelentkezik, azt most 150e-ért csatlakozhat.

2015.09.17.

SQL Server change tracking cikk

Filed under: Adatbázisok,SQL Server,SQL Server 2008,Szakmai élet — Soczó Zsolt @ 20:21

Annak idején írtam egy jó hosszú cikket a témában, hogyan lehet offline, disconnected appokat írni, amiben adatokat kell szinkronizálni. Valamiért már nem volt kinn a weben, ezért most kiraktam ide újra.

Olvassátok egészséggel.

2015.06.24.

2 karakter, és máris más az execution plan

Az eredeti lekérdezésben az ORDER BY így nézett ki: order by BDT.
Ez a számított oszlopra vonatkozott, azért a szervernek meg kellett oldania a rendezést egy külön lépésben. A b.BDT után viszont már tudja használni az alatta levő index rendezettségét, így nem kell rendeznie. 4x teljesítménynövekedést okozott ez a 2 karakter.

select
dateadd(second, @dtModifier, BDT) BDT,
cast(O as real) O, 
cast(H as real) H, 
cast(L as real) L,
cast(C as real) C,
V
from dbo.Bar b with (nolock)
where b.TickerID = @TickerID
and b.BDT >= @StartDate
and I = @I
order by b.BDT
Newer Posts »

Powered by WordPress