Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

Teveel geheugen verbruik bij genereren van custom live tiles

Pagina: 1
Acties:

  • Niels9001
  • Registratie: Januari 2013
  • Laatst online: 06-09 05:12
Hallo allemaal,

Op dit moment ben ik de laatste hand aan het leggen op versie 4.0 van Regenmeter. Deze versie is gemaakt a.h.v. het universele app model in WinRT (gebruikmakend van C# en XAML).

De laatste stap is het genereren van de live tiles in de BackgroundTask. Ik gebruik hiervoor de XamlRenderingBackgroundTask, en dat gaat in principe goed in Debug mode. Bij het uploaden naar de Store gaat het echter mis: BTs mogen daar maar minder MB aan geheugen verbruiken (rond de 20). Ik ga daar overheen met 3 a 4 MB. De achtergrondtaak doet het volgende:
  • Geolocaten van device.
  • Ophalen van relevante weer informatie.
  • Locatienaam ophalen via Bing.
  • A.d.v. die data 4 tiles genereren (MediumFront, MediumBack, WideFront, WideBack.
Het geheugenverbruik na het ophalen van de data zit rond de 8, dus dat valt ruim binnen het limiet. Bij het renderen gaat het echter mis. Ik doe het op de volgende manier:


C#:
1
2
3
4
5
6
7
//// Data is opgehaald, tiles worden aangemaakt.
string WideFrontTile = await RenderWideFront(WeatherData, bingResult.CityName);
string MediumFrontTile = await RenderMediumFront(WeatherData, bingResult.CityName);
string WideBackTile = await RenderWideBack(WeatherData);
string MediumBackTile = await RenderMediumBack(WeatherData);

//// Alle URLs worden gepushed naar de live tiles.


De 4 'render functies' maken gebruik van de data die is opgehaald, en gebruiken dat als input om de tiles te generen. Ik heb dus 4 functies (voor elke tile 1). Zo'n functie ziet er zo uit:

C#:
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
  private async Task<string> RenderMediumFront(WorldWeatherOnline.WeatherClass weatherClass, string Location)
        {
            var folder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync("Assets");
            var file = await folder.GetFileAsync("MediumFrontTile.xml");
            string szCustomTileXML = await Windows.Storage.FileIO.ReadTextAsync(file);

            Border tile = XamlReader.Load(szCustomTileXML) as Border;
            string TileURI = "MediumFront.png";
            if (null != tile)
            {

                Grid grid = tile.Child as Grid;
                TextBlock tempTxt = grid.FindName("tempTxt") as TextBlock;
                tempTxt.Text = weatherClass.Temp;
                TextBlock conditionTxt = grid.FindName("conditionTxt") as TextBlock;
                conditionTxt.Text = weatherClass.Condition.ToUpper();
                Path iconPath = grid.FindName("iconPath") as Path;
                iconPath.Data = supportingClasses.PathMarkupToGeometry(weatherClass.IconPath);
                TextBlock locationTxt = grid.FindName("locationTxt") as TextBlock;
                locationTxt.Text = Location;


                RenderTargetBitmap rtb = new RenderTargetBitmap();
                await rtb.RenderAsync(tile, 336, 336);
                IBuffer pixels = await rtb.GetPixelsAsync();
                DataReader dReader = Windows.Storage.Streams.DataReader.FromBuffer(pixels);
                byte[] data = new byte[pixels.Length];
                dReader.ReadBytes(data);


                var outputFile = await Windows.ApplicationModel.Package.Current.InstalledLocation.CreateFileAsync(TileURI, Windows.Storage.CreationCollisionOption.ReplaceExisting);
                var outputStream = await outputFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
                BitmapEncoder enc = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, outputStream);
                enc.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, (uint)rtb.PixelWidth, (uint)rtb.PixelHeight, 96, 96, data);
           

                await enc.FlushAsync();
            }

            return TileURI;
        }


Op MSDN wordt geadviseerd om gebruik te maken van C++ i.p.v. C# om het geheugen niet te veel te belasten. Ik heb echter weinig kaas gegeten van C++, en dat zou dan voor mijn hele BT gelden :(.

Ik had een ander idee om de bovenstaande Render functie naar C++ te herschrijven, en dat als los runtime component in mijn project te stoppen.
Je kan dan in de C# achtergrondtaak de data ophalen, die doorsturen naar het C++ object, die rendert alles en stuurt dan een URL terug. De vraag is of dat uberhaupt kan en gaat.. en hoe?


Misschien hebben jullie nog goede ideeen om het geheugen gebruik te limieteren.

Groeten,
Niels

Trein & Regenmeter voor Windows Phone 7.x & 8!


  • Swerfer
  • Registratie: Mei 2003
  • Laatst online: 19-11 21:48

Swerfer

Hmm...

Ik heb je code even in code tags geplaatst:
C#:
1
2
3
4
5
6
7
//// Data is opgehaald, tiles worden aangemaakt.
string WideFrontTile = await RenderWideFront(WeatherData, bingResult.CityName);
string MediumFrontTile = await RenderMediumFront(WeatherData, bingResult.CityName);
string WideBackTile = await RenderWideBack(WeatherData);
string MediumBackTile = await RenderMediumBack(WeatherData);

//// Alle URLs worden gepushed naar de live tiles.


C#:
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
  private async Task<string> RenderMediumFront(WorldWeatherOnline.WeatherClass weatherClass, string Location)
        {
            var folder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync("Assets");
            var file = await folder.GetFileAsync("MediumFrontTile.xml");
            string szCustomTileXML = await Windows.Storage.FileIO.ReadTextAsync(file);

            Border tile = XamlReader.Load(szCustomTileXML) as Border;
            string TileURI = "MediumFront.png";
            if (null != tile)
            {

                Grid grid = tile.Child as Grid;
                TextBlock tempTxt = grid.FindName("tempTxt") as TextBlock;
                tempTxt.Text = weatherClass.Temp;
                TextBlock conditionTxt = grid.FindName("conditionTxt") as TextBlock;
                conditionTxt.Text = weatherClass.Condition.ToUpper();
                Path iconPath = grid.FindName("iconPath") as Path;
                iconPath.Data = supportingClasses.PathMarkupToGeometry(weatherClass.IconPath);
                TextBlock locationTxt = grid.FindName("locationTxt") as TextBlock;
                locationTxt.Text = Location;


                RenderTargetBitmap rtb = new RenderTargetBitmap();
                await rtb.RenderAsync(tile, 336, 336);
                IBuffer pixels = await rtb.GetPixelsAsync();
                DataReader dReader = Windows.Storage.Streams.DataReader.FromBuffer(pixels);
                byte[] data = new byte[pixels.Length];
                dReader.ReadBytes(data);


                var outputFile = await Windows.ApplicationModel.Package.Current.InstalledLocation.CreateFileAsync(TileURI, Windows.Storage.CreationCollisionOption.ReplaceExisting);
                var outputStream = await outputFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
                BitmapEncoder enc = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, outputStream);
                enc.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, (uint)rtb.PixelWidth, (uint)rtb.PixelHeight, 96, 96, data);
           

                await enc.FlushAsync();
            }

            return TileURI;
        }


Zoals ik jouw code bekijk, maak je 4 livetile images die je lokaal wegschrijft. Daarna geef je de urls door om de livetiles ook zichtbaar te maken.

Kan je niet na het renderen van elke livetile je streams, grid en dergelijke disposen of nullen? Nu laat je in de achtergrondtaak data rondzwerven die je toch niet meer gebruikt. Ik kan mij voorstellen dat je na 4 livetiles renderen over die 20 MB heen gaat. Of zit je per gerenderede livetile al over de 20 MB heen?

Home Assistant | Unifi | LG 51MR.U44 | Volvo EX30 SMER+ Vapour Grey, trekhaak | SmartEVSE V3 | Cronos Crypto.com


  • Kwastie
  • Registratie: April 2005
  • Laatst online: 19:24

Kwastie

Awesomeness

Ik heb geen kaas gegeten van Windows 8 ontwikkeling, maar er is toch maximaal 1 tile actief? Waarom zou je er dan 4 renderen?

When I get sad i stop being sad and be awesome instead


  • Swerfer
  • Registratie: Mei 2003
  • Laatst online: 19-11 21:48

Swerfer

Hmm...

Kwastie schreef op zondag 07 september 2014 @ 13:43:
Ik heb geen kaas gegeten van Windows 8 ontwikkeling, maar er is toch maximaal 1 tile actief? Waarom zou je er dan 4 renderen?
Als een gebruiker zijn livetile in een ander formaat wilt weergeven, dan moet dat formaat wel beschikbaar zijn. Je kan niet detecteren of een gebruiker van livetile veranderd, dus je moet alle mogelijke livetiles van tevoren genereren...

Home Assistant | Unifi | LG 51MR.U44 | Volvo EX30 SMER+ Vapour Grey, trekhaak | SmartEVSE V3 | Cronos Crypto.com


  • Niels9001
  • Registratie: Januari 2013
  • Laatst online: 06-09 05:12
Swerfer schreef op zondag 07 september 2014 @ 13:42:
Ik heb je code even in code tags geplaatst:
C#:
1
2
3
4
5
6
7
//// Data is opgehaald, tiles worden aangemaakt.
string WideFrontTile = await RenderWideFront(WeatherData, bingResult.CityName);
string MediumFrontTile = await RenderMediumFront(WeatherData, bingResult.CityName);
string WideBackTile = await RenderWideBack(WeatherData);
string MediumBackTile = await RenderMediumBack(WeatherData);

//// Alle URLs worden gepushed naar de live tiles.


C#:
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
  private async Task<string> RenderMediumFront(WorldWeatherOnline.WeatherClass weatherClass, string Location)
        {
            var folder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync("Assets");
            var file = await folder.GetFileAsync("MediumFrontTile.xml");
            string szCustomTileXML = await Windows.Storage.FileIO.ReadTextAsync(file);

            Border tile = XamlReader.Load(szCustomTileXML) as Border;
            string TileURI = "MediumFront.png";
            if (null != tile)
            {

                Grid grid = tile.Child as Grid;
                TextBlock tempTxt = grid.FindName("tempTxt") as TextBlock;
                tempTxt.Text = weatherClass.Temp;
                TextBlock conditionTxt = grid.FindName("conditionTxt") as TextBlock;
                conditionTxt.Text = weatherClass.Condition.ToUpper();
                Path iconPath = grid.FindName("iconPath") as Path;
                iconPath.Data = supportingClasses.PathMarkupToGeometry(weatherClass.IconPath);
                TextBlock locationTxt = grid.FindName("locationTxt") as TextBlock;
                locationTxt.Text = Location;


                RenderTargetBitmap rtb = new RenderTargetBitmap();
                await rtb.RenderAsync(tile, 336, 336);
                IBuffer pixels = await rtb.GetPixelsAsync();
                DataReader dReader = Windows.Storage.Streams.DataReader.FromBuffer(pixels);
                byte[] data = new byte[pixels.Length];
                dReader.ReadBytes(data);


                var outputFile = await Windows.ApplicationModel.Package.Current.InstalledLocation.CreateFileAsync(TileURI, Windows.Storage.CreationCollisionOption.ReplaceExisting);
                var outputStream = await outputFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
                BitmapEncoder enc = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, outputStream);
                enc.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, (uint)rtb.PixelWidth, (uint)rtb.PixelHeight, 96, 96, data);
           

                await enc.FlushAsync();
            }

            return TileURI;
        }


Zoals ik jouw code bekijk, maak je 4 livetile images die je lokaal wegschrijft. Daarna geef je de urls door om de livetiles ook zichtbaar te maken.

Kan je niet na het renderen van elke livetile je streams, grid en dergelijke disposen of nullen? Nu laat je in de achtergrondtaak data rondzwerven die je toch niet meer gebruikt. Ik kan mij voorstellen dat je na 4 livetiles renderen over die 20 MB heen gaat. Of zit je per gerenderede livetile al over de 20 MB heen?
Het vreemde is, is dat na het data ophalen ik rond de 7 zit. En na de eerste tile bij de 19. Daarna komt er paar een paar MB per tile bij. Het maakt niet uit elke tile er als eerste gerenderd wordt, dat is het vreemde.

Ik heb geprobeerd zoveel mogelijk te nullen, maar dit had nauwelijks effect :(.

Trein & Regenmeter voor Windows Phone 7.x & 8!


  • Swerfer
  • Registratie: Mei 2003
  • Laatst online: 19-11 21:48

Swerfer

Hmm...

Misschien een workaround:

Ik weet niet hoe vaak je backgroundtask de livetiles ververst, maar stel dat je elke 4 minuten de livetiles ververst. Kan je dan niet in plaats van elke 4 minuten alle vier de livetiles renderen om de minuut 1 livetile renderen en dan na elke minuut het andere livetile formaat? Je zou dan alleen bij de eerste keer opstarten alle livetiles tegelijk moeten renderen in een voorgrondtaak...

Het zou waarschijnlijk ook anders kunnen, maar als bovenstaande methode werkt, kan je voorlopig vooruit...

Home Assistant | Unifi | LG 51MR.U44 | Volvo EX30 SMER+ Vapour Grey, trekhaak | SmartEVSE V3 | Cronos Crypto.com


  • johnkeates
  • Registratie: Februari 2008
  • Laatst online: 04-07 16:30
Kan je niet gewoon een debugger en/of een profiler pakken en kijken waar dat geheugen dan aan verspild wordt?

  • Niels9001
  • Registratie: Januari 2013
  • Laatst online: 06-09 05:12
Swerfer schreef op zondag 07 september 2014 @ 14:24:
Misschien een workaround:

Ik weet niet hoe vaak je backgroundtask de livetiles ververst, maar stel dat je elke 4 minuten de livetiles ververst. Kan je dan niet in plaats van elke 4 minuten alle vier de livetiles renderen om de minuut 1 livetile renderen en dan na elke minuut het andere livetile formaat? Je zou dan alleen bij de eerste keer opstarten alle livetiles tegelijk moeten renderen in een voorgrondtaak...

Het zou waarschijnlijk ook anders kunnen, maar als bovenstaande methode werkt, kan je voorlopig vooruit...
De TimerTrigger gaat alleen elke 30 minuten, dus dat is helaas geen optie.
johnkeates schreef op zondag 07 september 2014 @ 14:27:
Kan je niet gewoon een debugger en/of een profiler pakken en kijken waar dat geheugen dan aan verspild wordt?
De standaard VS 2013 Performance & Diagnostics profiler lijkt geen BackgroundTasks te kunnen managen..

Trein & Regenmeter voor Windows Phone 7.x & 8!


  • biomass
  • Registratie: Augustus 2004
  • Laatst online: 18:42
Alle vier de tiles worden tegelijkertijd gerenderd, door de manier waarop je de URIs assigned aan de strings? Zou je die na elkaar doen, zou je geheugenbelasting ook een stuk kleiner zijn?

  • Swerfer
  • Registratie: Mei 2003
  • Laatst online: 19-11 21:48

Swerfer

Hmm...

biomass schreef op zondag 07 september 2014 @ 17:17:
Alle vier de tiles worden tegelijkertijd gerenderd, door de manier waarop je de URIs assigned aan de strings? Zou je die na elkaar doen, zou je geheugenbelasting ook een stuk kleiner zijn?
Als ik kijk naar zijn code om de strings te assignen, dan worden ze 1 voor 1 gerenderd omdat de aangesproken tasks ge-await worden...

Toch moet per taak volgens mij geheugen kunnen worden teruggegeven aan het systeem door de stream, file, grid, rendertargetbitmap, ibuffer, datareader, byte, bitmapencoder enzv te nullen en/of te disposen.

Misschien gebruik maken van het using statement? Dan weet je zeker dat alles gedisposed wordt:

MSDN: using Statement (C# Reference)

[ Voor 32% gewijzigd door Swerfer op 07-09-2014 17:46 ]

Home Assistant | Unifi | LG 51MR.U44 | Volvo EX30 SMER+ Vapour Grey, trekhaak | SmartEVSE V3 | Cronos Crypto.com


  • Niels9001
  • Registratie: Januari 2013
  • Laatst online: 06-09 05:12
Hmm bedankt voor de tip, aan USING was ik inderdaad nog niet toegekomen.

Hoe werkt dat precies - moet je echt voor elke regel using gebruiken, of wordt alles wat in de using statement zit gedisposed?

Trein & Regenmeter voor Windows Phone 7.x & 8!


  • epic007
  • Registratie: Februari 2004
  • Laatst online: 17-11 15:31
Hier nog een voorbeeld http://code.msdn.microsof...leId=86717&pathId=2647585

Hier wordt de IBuffer gelijk mee gegeven aan de encoder encoder.SetPixelData(... , buffer.ToArray()). Volgens mij kan je de stappen die je doet met de DataReader en de byte [] data er dan uitslopen.

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Niels9001 schreef op maandag 15 september 2014 @ 07:57:
Hmm bedankt voor de tip, aan USING was ik inderdaad nog niet toegekomen.

Hoe werkt dat precies - moet je echt voor elke regel using gebruiken, of wordt alles wat in de using statement zit gedisposed?
In principe moet je alles wat IDisposable implementeerd in een using constructie gebruiken, of je moet het een member van een class maken die dan ook weer IDisposable implementeerd, en dus in zijn Dispose methode die resources vrijgeeft.

Van wat ik zo snel even zie is het waarschijnlijk de RenderTargetBitmap die geheugen allocate, maar die implementeerd op de een of andere manier geen IDisposable. Volgens http://stackoverflow.com/...unmanaged-memory-disposal zou een Clear op het RenderBitmapTarget je probleem op kunnen lossen ( Al lijkt dat mij wel vreemd, want in de docs staat daar niks over geheugen vermeld, alleen dat hij de pixels zwart maakt ).

Mocht dat niet helpen kun je nog expliciet met GC.Collect e.d. aan de gang.

[ Voor 33% gewijzigd door Woy op 15-09-2014 11:26 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • JayDz
  • Registratie: December 2012
  • Laatst online: 17-11 10:52
Stuur me een linkje als je hem released? :D

  • sig69
  • Registratie: Mei 2002
  • Laatst online: 02:31
Woy schreef op maandag 15 september 2014 @ 11:20:
[...]
...
Mocht dat niet helpen kun je nog expliciet met GC.Collect e.d. aan de gang.
Als je met GC.Collect aan de gang moet zit er doorgaans iets goed mis in je programma. En als het al enig effect heeft is het meer geluk dan wijsheid.

Roomba E5 te koop


  • Ramon
  • Registratie: Juli 2000
  • Laatst online: 01:35
Ik ben benieuwd of de TS al een oplossing heeft gevonden?

Check mijn V&A ads: https://tweakers.net/aanbod/user/9258/


  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
sig69 schreef op donderdag 18 september 2014 @ 21:17:
[...]

Als je met GC.Collect aan de gang moet zit er doorgaans iets goed mis in je programma. En als het al enig effect heeft is het meer geluk dan wijsheid.
In principe ben ik het met je eens, maar als een framework class resources vasthoudt, en met een finalizer wel vrijgeeft dan moet je wat.

Wel zaak om dat helder te hebben, want random GC. Collect zal weinig nut hebben. Eventueel recyclen van het object heeft dan nog de voorkeur.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • Niels9001
  • Registratie: Januari 2013
  • Laatst online: 06-09 05:12
Bedankt voor alle feedback.

Ben op dit moment op vakantie, maar hoop bovenstaande volgende week uit proberen.

Ik houd jullie op de hoogte :).

Trein & Regenmeter voor Windows Phone 7.x & 8!

Pagina: 1